@capybara-hq/kernel 0.1.0-rc.19 → 0.1.0-rc.20

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 (141) hide show
  1. package/bootstrap.json +25 -25
  2. package/dist/admin/admin-agents.controller.d.ts +28 -0
  3. package/dist/admin/admin-agents.controller.js +79 -0
  4. package/dist/admin/admin-connectors.controller.d.ts +29 -0
  5. package/dist/admin/admin-connectors.controller.js +79 -0
  6. package/dist/admin/admin-items.schemas.d.ts +42 -0
  7. package/dist/admin/admin-items.schemas.js +48 -0
  8. package/dist/admin/admin.controller.d.ts +11 -20
  9. package/dist/admin/admin.controller.js +28 -34
  10. package/dist/admin/admin.module.d.ts +4 -1
  11. package/dist/admin/admin.module.js +50 -4
  12. package/dist/admin/admin.schemas.d.ts +4 -0
  13. package/dist/admin/admin.schemas.js +4 -1
  14. package/dist/admin/audit.controller.d.ts +14 -0
  15. package/dist/admin/audit.controller.js +42 -0
  16. package/dist/admin/audit.schemas.d.ts +8 -0
  17. package/dist/admin/audit.schemas.js +17 -0
  18. package/dist/admin/health.service.d.ts +104 -0
  19. package/dist/admin/health.service.js +246 -0
  20. package/dist/admin/overview.controller.d.ts +31 -0
  21. package/dist/admin/overview.controller.js +78 -0
  22. package/dist/admin/overview.schemas.d.ts +8 -0
  23. package/dist/admin/overview.schemas.js +13 -0
  24. package/dist/admin/overview.service.d.ts +112 -0
  25. package/dist/admin/overview.service.js +223 -0
  26. package/dist/admin/platform-audit.service.d.ts +41 -0
  27. package/dist/admin/platform-audit.service.js +70 -0
  28. package/dist/admin/usage.service.d.ts +149 -0
  29. package/dist/admin/usage.service.js +321 -0
  30. package/dist/agents/admin-agents.d.ts +92 -0
  31. package/dist/agents/admin-agents.js +170 -0
  32. package/dist/agents/agents.module.d.ts +4 -3
  33. package/dist/agents/agents.module.js +7 -3
  34. package/dist/agents/agents.service.d.ts +41 -3
  35. package/dist/agents/agents.service.js +113 -27
  36. package/dist/agents/definition.d.ts +16 -0
  37. package/dist/agents/definition.js +32 -2
  38. package/dist/agents/org-templates.js +4 -0
  39. package/dist/background/launch.d.ts +1 -1
  40. package/dist/background/launch.js +3 -3
  41. package/dist/background/tasks.service.d.ts +35 -3
  42. package/dist/background/tasks.service.js +91 -36
  43. package/dist/catalog/catalog.module.js +8 -2
  44. package/dist/channels/channels.module.js +11 -2
  45. package/dist/channels/supervisor.d.ts +2 -0
  46. package/dist/channels/supervisor.js +4 -0
  47. package/dist/conversations/events.js +1 -1
  48. package/dist/conversations/titles.js +4 -8
  49. package/dist/db/kernel-schema.d.ts +8 -1
  50. package/dist/devices/devices.module.js +5 -2
  51. package/dist/errors.d.ts +22 -2
  52. package/dist/errors.js +22 -2
  53. package/dist/evals/evals.module.js +11 -2
  54. package/dist/extensions/permission-view.js +3 -3
  55. package/dist/http/api.module.js +10 -1
  56. package/dist/identity/settings.js +3 -2
  57. package/dist/identity/write-actor.d.ts +11 -0
  58. package/dist/identity/write-actor.js +7 -0
  59. package/dist/inbox/inbox.service.d.ts +2 -1
  60. package/dist/inbox/platform-inbox.service.d.ts +7 -1
  61. package/dist/inbox/platform-inbox.service.js +20 -2
  62. package/dist/infra/presence/presence.module.d.ts +6 -0
  63. package/dist/infra/presence/presence.module.js +22 -0
  64. package/dist/infra/presence/replica-presence.d.ts +87 -0
  65. package/dist/infra/presence/replica-presence.js +189 -0
  66. package/dist/kernel.module.js +3 -0
  67. package/dist/lifecycle/kernel-lifecycle.d.ts +3 -1
  68. package/dist/lifecycle/kernel-lifecycle.js +17 -3
  69. package/dist/local-devices/local-device-gateway.d.ts +6 -0
  70. package/dist/local-devices/local-device-gateway.js +12 -0
  71. package/dist/overrides.d.ts +7 -1
  72. package/dist/overrides.js +4 -0
  73. package/dist/platform/exclusions.js +15 -0
  74. package/dist/platform/operation.d.ts +1 -1
  75. package/dist/platform/operation.js +1 -1
  76. package/dist/platform/ops/admin-items.ops.d.ts +37 -0
  77. package/dist/platform/ops/admin-items.ops.js +278 -0
  78. package/dist/platform/ops/admin.ops.d.ts +10 -10
  79. package/dist/platform/ops/admin.ops.js +111 -42
  80. package/dist/platform/ops/agent-skills.ops.d.ts +29 -3
  81. package/dist/platform/ops/agent-skills.ops.js +290 -7
  82. package/dist/platform/ops/inbox.ops.js +7 -4
  83. package/dist/platform/ops/platform.ops.js +1 -1
  84. package/dist/platform/ops/skills.ops.js +1 -57
  85. package/dist/platform/platform-ops.module.d.ts +2 -1
  86. package/dist/platform/platform-ops.module.js +5 -0
  87. package/dist/platform/route-schemas.js +8 -0
  88. package/dist/roles/gateway-role.d.ts +4 -0
  89. package/dist/roles/gateway-role.js +8 -0
  90. package/dist/runtime/run-jobs.js +4 -2
  91. package/dist/sandbox/sandbox-file-reader.d.ts +35 -0
  92. package/dist/sandbox/sandbox-file-reader.js +98 -0
  93. package/dist/skills/agent-skills.controller.d.ts +48 -0
  94. package/dist/skills/agent-skills.controller.js +131 -0
  95. package/dist/skills/agent-skills.service.d.ts +262 -0
  96. package/dist/skills/agent-skills.service.js +996 -0
  97. package/dist/skills/catalog.d.ts +8 -6
  98. package/dist/skills/catalog.js +8 -4
  99. package/dist/skills/changes.controller.d.ts +5 -22
  100. package/dist/skills/changes.controller.js +7 -55
  101. package/dist/skills/changes.service.d.ts +69 -115
  102. package/dist/skills/changes.service.js +249 -599
  103. package/dist/skills/conversation-catalog.d.ts +53 -0
  104. package/dist/skills/conversation-catalog.js +149 -0
  105. package/dist/skills/invocation-keys.d.ts +1 -1
  106. package/dist/skills/invocation-keys.js +1 -1
  107. package/dist/skills/package.d.ts +0 -5
  108. package/dist/skills/package.js +0 -14
  109. package/dist/skills/skills.controller.js +1 -10
  110. package/dist/skills/skills.module.js +15 -2
  111. package/dist/skills/skills.schemas.d.ts +11 -15
  112. package/dist/skills/skills.schemas.js +27 -20
  113. package/dist/skills/skills.service.d.ts +37 -30
  114. package/dist/skills/skills.service.js +107 -59
  115. package/dist/skills/usable.d.ts +4 -3
  116. package/dist/skills/usable.js +13 -4
  117. package/dist/tools/admin-connectors.d.ts +69 -0
  118. package/dist/tools/admin-connectors.js +135 -0
  119. package/dist/tools/builtin-names.d.ts +9 -14
  120. package/dist/tools/builtin-names.js +13 -22
  121. package/dist/tools/builtins/builtin-tools.d.ts +2 -3
  122. package/dist/tools/builtins/builtin-tools.js +21 -26
  123. package/dist/tools/builtins/sandbox/bash.js +2 -2
  124. package/dist/tools/builtins/skill-load.js +1 -0
  125. package/dist/tools/connectors.controller.d.ts +5 -1
  126. package/dist/tools/connectors.controller.js +17 -3
  127. package/dist/tools/connectors.service.d.ts +20 -1
  128. package/dist/tools/connectors.service.js +61 -0
  129. package/dist/tools/legacy-keys.js +0 -1
  130. package/dist/tools/org-tool-switches.d.ts +3 -2
  131. package/dist/tools/org-tool-switches.js +4 -4
  132. package/dist/tools/policy.d.ts +7 -9
  133. package/dist/tools/policy.js +11 -22
  134. package/dist/tools/tools.module.js +3 -0
  135. package/package.json +1 -1
  136. package/src/migrations/0109_audit_platform_idx.sql +5 -0
  137. package/src/migrations/0110_usage_time_idx.sql +8 -0
  138. package/src/migrations/0111_agent_skill_sharing.sql +22 -0
  139. package/src/migrations/0112_skill_share_requests.sql +4 -0
  140. package/dist/tools/builtins/skill-write.d.ts +0 -59
  141. package/dist/tools/builtins/skill-write.js +0 -244
@@ -0,0 +1,53 @@
1
+ import { type Selectable } from "kysely";
2
+ import type { EventLog } from "../conversations/event-log.js";
3
+ import type { SkillChangeRequestsTable } from "../db/kernel-schema.js";
4
+ import type { KernelDb } from "../infra/database/kernel-db.js";
5
+ import type { KernelLogger } from "../log.js";
6
+ import { type SkillsSnapshot } from "../runtime/context/skills.js";
7
+ /** What appending a conversation's skills catalog snapshot needs. */
8
+ export interface ConversationCatalogDeps {
9
+ db: KernelDb;
10
+ log: KernelLogger;
11
+ eventLog: EventLog;
12
+ }
13
+ /** Cancelled requests: draft prefixes to delete and (conversation, seq) to publish after the commit. */
14
+ export interface CancelledDrafts {
15
+ prefixes: string[];
16
+ seqs: [string, number][];
17
+ }
18
+ /**
19
+ * In the caller's transaction, derives the conversation's next skills catalog snapshot from its
20
+ * latest one and appends it (only when different). It first locks the conversation row (the lock
21
+ * `EventLog.append` takes) and reads the latest snapshot under it, so no other snapshot slips in
22
+ * between "read latest" and "append". Returns the seq written, or null when nothing was written;
23
+ * the caller calls `eventLog.notify` after the commit.
24
+ * `derive` gets the whole latest payload, so `pinned` and `toolMap` are carried forward; a pinned
25
+ * entry is kept only while `skills` has the same id, version and source (pinned ⊆ skills), and the
26
+ * pinned bodies still fit the size limit. The tool map is only recomputed at run start.
27
+ */
28
+ export declare function appendConversationCatalog(deps: ConversationCatalogDeps, conversationId: string, derive: (base: SkillsSnapshot) => SkillsSnapshot | null, idemKey: string, runId?: string | null): Promise<number | null>;
29
+ /**
30
+ * When a draft stops applying (rejected / cancelled), appends a reverting snapshot to the
31
+ * requesting conversation in the caller's transaction: derived from that conversation's latest
32
+ * snapshot, the draft's entry is replaced by the skill's current real version (already the
33
+ * post-decision state in the transaction; for a version proposal the org skill it targets), or
34
+ * removed when there is none. No snapshot is written when the latest one no longer has the draft,
35
+ * nor for a request without a conversation or a share request (no trial). While the conversation
36
+ * runs, the notice lands at the current position (built after the tool results) and applies at
37
+ * the next step. A removal does not guess an org skill of the same name (it was replaced in the
38
+ * snapshot); the next run start's resolution brings it back.
39
+ */
40
+ export declare function revertTrialCatalog(deps: ConversationCatalogDeps, req: Selectable<SkillChangeRequestsTable>): Promise<number | null>;
41
+ /**
42
+ * In the caller's transaction (the request row locked), cancels a pending request: status
43
+ * `cancelled`, audit `skill.change_decided` with the reason. A version draft also gets a
44
+ * reverting snapshot in its conversation (unless `revert` is false: the caller appends the next
45
+ * one) and its prefix is queued in `out` for deletion after the commit; a share request points at
46
+ * the skill's own version files, which stay.
47
+ */
48
+ export declare function cancelRequestIn(deps: ConversationCatalogDeps, req: Selectable<SkillChangeRequestsTable>, out: CancelledDrafts, how: {
49
+ actorAccountId: string;
50
+ actorTokenId?: string;
51
+ reason: string;
52
+ revert?: boolean;
53
+ }): Promise<void>;
@@ -0,0 +1,149 @@
1
+ import { sql } from "kysely";
2
+ import { audit } from "../identity/audit.js";
3
+ import { sameSnapshot, } from "../runtime/context/skills.js";
4
+ import { fitPinned, invocationOf, lockLatestCatalog } from "./catalog.js";
5
+ const byName = (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
6
+ /**
7
+ * In the caller's transaction, derives the conversation's next skills catalog snapshot from its
8
+ * latest one and appends it (only when different). It first locks the conversation row (the lock
9
+ * `EventLog.append` takes) and reads the latest snapshot under it, so no other snapshot slips in
10
+ * between "read latest" and "append". Returns the seq written, or null when nothing was written;
11
+ * the caller calls `eventLog.notify` after the commit.
12
+ * `derive` gets the whole latest payload, so `pinned` and `toolMap` are carried forward; a pinned
13
+ * entry is kept only while `skills` has the same id, version and source (pinned ⊆ skills), and the
14
+ * pinned bodies still fit the size limit. The tool map is only recomputed at run start.
15
+ */
16
+ export async function appendConversationCatalog(deps, conversationId, derive, idemKey, runId) {
17
+ const base = (await lockLatestCatalog(deps.db.db, conversationId)) ?? { skills: [] };
18
+ const derived = derive(base);
19
+ if (derived === null)
20
+ return null;
21
+ const skills = [...derived.skills].sort(byName);
22
+ const live = (p) => skills.some((s) => s.id === p.id && s.version === p.version && s.source === p.source);
23
+ const pinned = fitPinned((derived.pinned ?? []).filter(live), deps.log, { conversationId });
24
+ const next = {
25
+ skills,
26
+ ...(pinned.length > 0 && { pinned }),
27
+ ...(derived.toolMap !== undefined && { toolMap: derived.toolMap }),
28
+ };
29
+ if (sameSnapshot(base, next))
30
+ return null;
31
+ const written = await deps.eventLog.appendIn(conversationId, [
32
+ {
33
+ type: "skills_catalog",
34
+ ...(runId !== undefined && runId !== null && { runId }),
35
+ idemKey,
36
+ payload: next,
37
+ },
38
+ ]);
39
+ return Math.max(...written.map((e) => e.seq));
40
+ }
41
+ /**
42
+ * When a draft stops applying (rejected / cancelled), appends a reverting snapshot to the
43
+ * requesting conversation in the caller's transaction: derived from that conversation's latest
44
+ * snapshot, the draft's entry is replaced by the skill's current real version (already the
45
+ * post-decision state in the transaction; for a version proposal the org skill it targets), or
46
+ * removed when there is none. No snapshot is written when the latest one no longer has the draft,
47
+ * nor for a request without a conversation or a share request (no trial). While the conversation
48
+ * runs, the notice lands at the current position (built after the tool results) and applies at
49
+ * the next step. A removal does not guess an org skill of the same name (it was replaced in the
50
+ * snapshot); the next run start's resolution brings it back.
51
+ */
52
+ export async function revertTrialCatalog(deps, req) {
53
+ const conversationId = req.requested_in_conversation;
54
+ if (conversationId === null || req.kind === "share")
55
+ return null;
56
+ const name = req.proposed_version.manifest.name;
57
+ let q = deps.db
58
+ .selectFrom("skills")
59
+ .innerJoin("skill_versions", (j) => j
60
+ .onRef("skill_versions.skill_id", "=", "skills.id")
61
+ .onRef("skill_versions.version", "=", "skills.current_version"))
62
+ .select([
63
+ "skills.id",
64
+ "skills.current_version",
65
+ "skill_versions.manifest",
66
+ "skill_versions.object_prefix",
67
+ ])
68
+ .where("skills.org_id", "=", req.org_id)
69
+ .where("skills.deleted_at", "is", null);
70
+ // A proposal on a shared org skill goes back to that skill's current version; one served from
71
+ // a platform skill (promoted or linked without a pin) is removed instead, and the next run start
72
+ // resolves it again
73
+ q =
74
+ req.skill_id !== null
75
+ ? q
76
+ .where("skills.id", "=", req.skill_id)
77
+ .where("skills.scope", "=", "org")
78
+ .where((eb) => eb.or([
79
+ eb("skills.platform_skill_id", "is", null),
80
+ eb("skills.pinned_version", "is not", null),
81
+ ]))
82
+ : q
83
+ .where("skills.scope", "=", "agent")
84
+ .where("skills.agent_id", "=", req.agent_id)
85
+ .where("skills.name", "=", name);
86
+ const real = await q.executeTakeFirst();
87
+ return appendConversationCatalog(deps, conversationId, (base) => {
88
+ if (!base.skills.some((s) => s.source === req.object_prefix))
89
+ return null;
90
+ const rest = base.skills.filter((s) => s.source !== req.object_prefix);
91
+ // The draft's pinned entry goes (Decision 8): the next run start pins the real version
92
+ return {
93
+ ...base,
94
+ // A manual-only current version (phase D) stays out of the model's catalog
95
+ skills: real && invocationOf(real.manifest).model
96
+ ? [
97
+ ...rest,
98
+ {
99
+ id: real.id,
100
+ name,
101
+ description: real.manifest.description,
102
+ version: real.current_version,
103
+ source: real.object_prefix,
104
+ },
105
+ ]
106
+ : rest,
107
+ ...(base.pinned && {
108
+ pinned: base.pinned.filter((p) => p.source !== req.object_prefix),
109
+ }),
110
+ };
111
+ }, `skills_catalog:revert:${req.id}`);
112
+ }
113
+ /**
114
+ * In the caller's transaction (the request row locked), cancels a pending request: status
115
+ * `cancelled`, audit `skill.change_decided` with the reason. A version draft also gets a
116
+ * reverting snapshot in its conversation (unless `revert` is false: the caller appends the next
117
+ * one) and its prefix is queued in `out` for deletion after the commit; a share request points at
118
+ * the skill's own version files, which stay.
119
+ */
120
+ export async function cancelRequestIn(deps, req, out, how) {
121
+ await deps.db
122
+ .updateTable("skill_change_requests")
123
+ .set({ status: "cancelled", decided_at: sql `now()` })
124
+ .where("id", "=", req.id)
125
+ .execute();
126
+ await audit(deps.db.db, {
127
+ actorAccountId: how.actorAccountId,
128
+ ...(how.actorTokenId !== undefined && { actorTokenId: how.actorTokenId }),
129
+ orgId: req.org_id,
130
+ action: "skill.change_decided",
131
+ target: req.id,
132
+ detail: {
133
+ decision: "cancelled",
134
+ reason: how.reason,
135
+ kind: req.kind,
136
+ agentId: req.agent_id,
137
+ name: req.proposed_version.manifest.name,
138
+ },
139
+ });
140
+ if (req.kind === "share")
141
+ return;
142
+ if (how.revert !== false) {
143
+ const seq = await revertTrialCatalog(deps, req);
144
+ if (seq !== null && req.requested_in_conversation !== null) {
145
+ out.seqs.push([req.requested_in_conversation, seq]);
146
+ }
147
+ }
148
+ out.prefixes.push(req.object_prefix);
149
+ }
@@ -2,7 +2,7 @@
2
2
  * The SKILL.md invocation keys (package ecosystem phase D) and their lenient reading: readers of
3
3
  * stored files (catalog install and upgrade) and the Claude Code translation drop a key the strict
4
4
  * `parseSkillMd` checks would refuse, with the reason, instead of refusing the skill or storing an
5
- * invocation nobody can use. Uploads, native imports and `SkillWrite` stay strict.
5
+ * invocation nobody can use. Uploads and native imports stay strict.
6
6
  */
7
7
  /** The frontmatter keys that make up a skill's invocation metadata. */
8
8
  export declare const INVOCATION_KEYS: readonly ["disable-model-invocation", "user-invocable", "context", "agent", "argument-hint", "arguments"];
@@ -3,7 +3,7 @@ import { parseSkillMd } from "@capybara-hq/plugin-sdk";
3
3
  * The SKILL.md invocation keys (package ecosystem phase D) and their lenient reading: readers of
4
4
  * stored files (catalog install and upgrade) and the Claude Code translation drop a key the strict
5
5
  * `parseSkillMd` checks would refuse, with the reason, instead of refusing the skill or storing an
6
- * invocation nobody can use. Uploads, native imports and `SkillWrite` stay strict.
6
+ * invocation nobody can use. Uploads and native imports stay strict.
7
7
  */
8
8
  /** The frontmatter keys that make up a skill's invocation metadata. */
9
9
  export const INVOCATION_KEYS = [
@@ -86,11 +86,6 @@ export declare function checkSkillFiles(files: SkillPackageFile[], opts?: {
86
86
  * 不合法 → {@link SkillArchiveError}。
87
87
  */
88
88
  export declare function readZipFiles(bytes: Uint8Array, maxBytes: number): Promise<SkillPackageFile[]>;
89
- /**
90
- * Checks skill files that are already unpacked (paths relative to the skill root), e.g. the ones
91
- * built from text, with the rules of {@link readSkillPackage}. Invalid -> `skill.invalid_package`.
92
- */
93
- export declare function checkBuiltSkillFiles(files: SkillPackageFile[]): SkillPackage;
94
89
  /** A validated skill package: its name, description, files and invocation. */
95
90
  export interface SkillPackageInput {
96
91
  name: string;
@@ -225,20 +225,6 @@ export async function readZipFiles(bytes, maxBytes) {
225
225
  }
226
226
  return raw;
227
227
  }
228
- /**
229
- * Checks skill files that are already unpacked (paths relative to the skill root), e.g. the ones
230
- * built from text, with the rules of {@link readSkillPackage}. Invalid -> `skill.invalid_package`.
231
- */
232
- export function checkBuiltSkillFiles(files) {
233
- try {
234
- return { ...checkSkillFiles(files), files };
235
- }
236
- catch (e) {
237
- if (e instanceof SkillArchiveError)
238
- throw invalid(e.problems);
239
- throw e;
240
- }
241
- }
242
228
  /**
243
229
  * The stored manifest of a skill package (the invocation only when it is not the default); org
244
230
  * skills and platform skills store the same shape.
@@ -70,16 +70,7 @@ let SkillsController = class SkillsController {
70
70
  async create(principal, org, body) {
71
71
  const p = requireUser(principal);
72
72
  const tokenId = patTokenId(p);
73
- if (body.uploadId !== undefined) {
74
- return this.skills.upload(org.orgId, p.account.id, body.uploadId, tokenId, body.shareable);
75
- }
76
- return this.skills.createFromText(org.orgId, p.account.id, {
77
- name: body.name,
78
- description: body.description,
79
- body: body.body,
80
- ...(body.shareable !== undefined && { shareable: body.shareable }),
81
- ...(tokenId !== undefined && { actorTokenId: tokenId }),
82
- });
73
+ return this.skills.upload(org.orgId, p.account.id, body.uploadId, tokenId, body.shareable);
83
74
  }
84
75
  async list(principal, org) {
85
76
  requireUser(principal);
@@ -11,6 +11,7 @@ import { AttachmentsModule } from "../conversations/attachments.module.js";
11
11
  import { EventLogModule } from "../conversations/event-log.js";
12
12
  import { OrgLockModule } from "../orgs/org-lock.js";
13
13
  import { StorageModule } from "../storage/storage.module.js";
14
+ import { AgentSkillsService } from "./agent-skills.service.js";
14
15
  import { SkillsCatalogService } from "./catalog.js";
15
16
  import { SkillChangesService } from "./changes.service.js";
16
17
  import { SkillInvoker } from "./invoke.js";
@@ -32,8 +33,20 @@ SkillsModule = __decorate([
32
33
  // the owner's new version of a skill it promoted goes to the platform row (ruling P3-4)
33
34
  CatalogImportModule,
34
35
  ],
35
- providers: [SkillsService, SkillChangesService, SkillsCatalogService, SkillInvoker],
36
- exports: [SkillsService, SkillChangesService, SkillsCatalogService, SkillInvoker],
36
+ providers: [
37
+ AgentSkillsService,
38
+ SkillsService,
39
+ SkillChangesService,
40
+ SkillsCatalogService,
41
+ SkillInvoker,
42
+ ],
43
+ exports: [
44
+ AgentSkillsService,
45
+ SkillsService,
46
+ SkillChangesService,
47
+ SkillsCatalogService,
48
+ SkillInvoker,
49
+ ],
37
50
  })
38
51
  ], SkillsModule);
39
52
  export { SkillsModule };
@@ -1,21 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import type { RouteSchemas } from "../platform/route-schemas.js";
3
- /**
4
- * The fields of `POST /skills` before the either-or refinement: zod 4 cannot `pick` / `omit` a
5
- * refined object, so operations build from these and the route validates {@link SkillCreateBody}.
6
- */
7
- export declare const SkillCreateFields: z.ZodObject<{
8
- uploadId: z.ZodOptional<z.ZodString>;
9
- name: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
10
- description: z.ZodOptional<z.ZodString>;
11
- body: z.ZodOptional<z.ZodString>;
12
- shareable: z.ZodOptional<z.ZodBoolean>;
13
- }, z.core.$strict>;
3
+ /** `POST /skills`: an uploaded skill package (`POST /skills/uploads` gives the id); no text form. */
14
4
  export declare const SkillCreateBody: z.ZodObject<{
15
- uploadId: z.ZodOptional<z.ZodString>;
16
- name: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
17
- description: z.ZodOptional<z.ZodString>;
18
- body: z.ZodOptional<z.ZodString>;
5
+ uploadId: z.ZodString;
19
6
  shareable: z.ZodOptional<z.ZodBoolean>;
20
7
  }, z.core.$strict>;
21
8
  /** `:id` where the route validates it (`@ZParam`). */
@@ -41,6 +28,15 @@ export declare const AgentSkillPathParams: z.ZodObject<{
41
28
  agentId: z.ZodString;
42
29
  skillId: z.ZodString;
43
30
  }, z.core.$strip>;
31
+ /** `POST /agents/:agentId/skills/uploads`: a skill package upload onto the agent. */
32
+ export declare const AgentSkillUploadBody: z.ZodObject<{
33
+ name: z.ZodString;
34
+ size: z.ZodNumber;
35
+ }, z.core.$strict>;
36
+ /** `POST /agents/:agentId/skills`: an uploaded package registered as one of the agent's skills. */
37
+ export declare const AgentSkillRegisterBody: z.ZodObject<{
38
+ uploadId: z.ZodString;
39
+ }, z.core.$strict>;
44
40
  /** `?conversationId=` of `GET /agents/:agentId/invocable-skills`. */
45
41
  export declare const InvocableSkillsQuerySchema: z.ZodObject<{
46
42
  conversationId: z.ZodOptional<z.ZodString>;
@@ -1,25 +1,10 @@
1
- import { isSkillName, SKILL_LIMITS } from "@capybara-hq/plugin-sdk";
1
+ import { hasControlChars, MEDIA_NAME_MAX_CHARS } from "@capybara-hq/plugin-sdk";
2
2
  import { z } from "zod";
3
- import { SKILL_WRITE_MAX_BYTES } from "../tools/builtins/skill-write.js";
4
- /**
5
- * The fields of `POST /skills` before the either-or refinement: zod 4 cannot `pick` / `omit` a
6
- * refined object, so operations build from these and the route validates {@link SkillCreateBody}.
7
- */
8
- export const SkillCreateFields = z
9
- .object({
10
- uploadId: z.string().min(1).max(100).optional(),
11
- name: z
12
- .string()
13
- .refine(isSkillName, "name must be 1-64 lowercase letters, digits and single hyphens")
14
- .optional(),
15
- description: z.string().min(1).max(SKILL_LIMITS.descriptionMaxChars).optional(),
16
- body: z.string().max(SKILL_WRITE_MAX_BYTES).optional(),
17
- shareable: z.boolean().optional(),
18
- })
3
+ import { SKILL_PACKAGE_UPLOAD_MAX_BYTES } from "../conversations/attachments.service.js";
4
+ /** `POST /skills`: an uploaded skill package (`POST /skills/uploads` gives the id); no text form. */
5
+ export const SkillCreateBody = z
6
+ .object({ uploadId: z.string().min(1).max(100), shareable: z.boolean().optional() })
19
7
  .strict();
20
- export const SkillCreateBody = SkillCreateFields.refine((v) => v.uploadId !== undefined
21
- ? v.name === undefined && v.description === undefined && v.body === undefined
22
- : v.name !== undefined && v.description !== undefined && v.body !== undefined, "send either { uploadId } or { name, description, body }");
23
8
  /** `:id` where the route validates it (`@ZParam`). */
24
9
  export const SkillIdParams = z.object({ id: z.string().min(1).max(100) });
25
10
  /** `:id` where the route takes it as is (`@Param`). */
@@ -32,6 +17,19 @@ export const AgentSkillsListParams = z.object({ agentId: z.string().min(1).max(1
32
17
  export const AgentSkillPathParams = AgentSkillsListParams.extend({
33
18
  skillId: z.string().min(1).max(100),
34
19
  });
20
+ /** `POST /agents/:agentId/skills/uploads`: a skill package upload onto the agent. */
21
+ export const AgentSkillUploadBody = z
22
+ .object({
23
+ name: z
24
+ .string()
25
+ .min(1)
26
+ .max(MEDIA_NAME_MAX_CHARS)
27
+ .refine((s) => !hasControlChars(s), "name must not contain control characters"),
28
+ size: z.number().int().min(1).max(SKILL_PACKAGE_UPLOAD_MAX_BYTES),
29
+ })
30
+ .strict();
31
+ /** `POST /agents/:agentId/skills`: an uploaded package registered as one of the agent's skills. */
32
+ export const AgentSkillRegisterBody = z.object({ uploadId: z.string().min(1).max(100) }).strict();
35
33
  /** `?conversationId=` of `GET /agents/:agentId/invocable-skills`. */
36
34
  export const InvocableSkillsQuerySchema = z
37
35
  .object({ conversationId: z.string().min(1).max(100).optional() })
@@ -39,10 +37,19 @@ export const InvocableSkillsQuerySchema = z
39
37
  export const SKILL_ROUTES = [
40
38
  { route: "GET /api/v1/orgs/:orgId/skill-changes/:id", params: SkillChangePathParams },
41
39
  { route: "GET /api/v1/orgs/:orgId/agents/:agentId/skills", params: AgentSkillsListParams },
40
+ {
41
+ route: "POST /api/v1/orgs/:orgId/agents/:agentId/skills",
42
+ params: AgentSkillsListParams,
43
+ body: AgentSkillRegisterBody,
44
+ },
42
45
  {
43
46
  route: "DELETE /api/v1/orgs/:orgId/agents/:agentId/skills/:skillId",
44
47
  params: AgentSkillPathParams,
45
48
  },
49
+ {
50
+ route: "POST /api/v1/orgs/:orgId/agents/:agentId/skills/:skillId/share",
51
+ params: AgentSkillPathParams,
52
+ },
46
53
  {
47
54
  route: "GET /api/v1/orgs/:orgId/agents/:agentId/invocable-skills",
48
55
  params: AgentSkillsListParams,
@@ -1,11 +1,13 @@
1
1
  import type { AgentDefinition } from "../agents/definition.js";
2
2
  import { type Owner } from "../catalog/ownership.js";
3
- import { PlatformComponents } from "../catalog/platform-components.js";
3
+ import { type OwnerVersion, PlatformComponents } from "../catalog/platform-components.js";
4
4
  import { AttachmentsService, type CreatedAttachment } from "../conversations/attachments.service.js";
5
+ import { EventLog } from "../conversations/event-log.js";
5
6
  import type { KernelSchemaDb, SkillManifest, SourceFormat } from "../db/kernel-schema.js";
6
7
  import { ApiError } from "../errors.js";
7
8
  import { KernelDb } from "../infra/database/kernel-db.js";
8
9
  import { TransactionHost } from "../infra/database/transaction-host.js";
10
+ import type { KernelLogger } from "../log.js";
9
11
  import { OrgLock } from "../orgs/org-lock.js";
10
12
  import { StorageService } from "../storage/storage.service.js";
11
13
  import { type SkillPackageInput } from "./package.js";
@@ -149,9 +151,12 @@ export declare class SkillsService {
149
151
  private readonly orgLock;
150
152
  /** The owner's new versions of a skill it promoted go to the platform row (ruling P3-4). */
151
153
  private readonly platform;
154
+ private readonly eventLog;
155
+ private readonly log;
152
156
  constructor(db: KernelDb, storage: StorageService, attachments: AttachmentsService, txHost: TransactionHost, orgLock: OrgLock,
153
157
  /** The owner's new versions of a skill it promoted go to the platform row (ruling P3-4). */
154
- platform: PlatformComponents);
158
+ platform: PlatformComponents, eventLog: EventLog, log: KernelLogger);
159
+ private readonly catalogDeps;
155
160
  /**
156
161
  * Writes a package's files where its version will live: for a new version of a skill the org
157
162
  * promoted (ruling P3-3: a live org skill of the name linked without a pin to a platform skill
@@ -188,31 +193,6 @@ export declare class SkillsService {
188
193
  * ({@link createVersionIn} joins it).
189
194
  */
190
195
  private createUploadedVersionIn;
191
- /**
192
- * The read-only checks of {@link createFromText}, for the `Platform` tool's precheck (before the
193
- * card): custom skills allowed, a valid `SKILL.md`, and no live org skill of the name. Throws the
194
- * same `ApiError` the create would; the create checks the name again under its lock.
195
- */
196
- checkCreateFromText(orgId: string, input: {
197
- name: string;
198
- description: string;
199
- body: string;
200
- }): Promise<void>;
201
- /**
202
- * Creates an org skill from text (spec §14, P3-24): `SKILL.md` is built from the name,
203
- * description and body ({@link buildSkillWrite}, the description a YAML string) and checked like
204
- * an uploaded package. The same policy and name rules as an upload, except that a live org skill
205
- * of that name is `skill.name_exists` (409) instead of a new version.
206
- */
207
- createFromText(orgId: string, actorAccountId: string, input: {
208
- name: string;
209
- description: string;
210
- body: string;
211
- shareable?: boolean;
212
- actorTokenId?: string;
213
- /** Merged into the audit event's detail (the `Platform` tool's `via: "agent"`). */
214
- auditDetail?: Record<string, unknown>;
215
- }): Promise<UploadedSkill>;
216
196
  /**
217
197
  * 用一组已校验的技能文件创建组织技能或给同名技能新增版本(上传与平台目录安装共用):
218
198
  * 1. 把文件写到 `kernel/<orgId>/<prefix>`(缺省 `skills/<新 id>/`);
@@ -230,8 +210,6 @@ export declare class SkillsService {
230
210
  auditDetail?: Record<string, unknown>;
231
211
  /** The owner shares a new skill with other orgs (default false). */
232
212
  shareable?: boolean;
233
- /** A live org skill of the name is `skill.name_exists` instead of getting a new version. */
234
- failIfExists?: boolean;
235
213
  /** Catalog provenance; `approvedBy` is the item whose policy approves it (default `itemId`). */
236
214
  catalog?: {
237
215
  itemId: string;
@@ -242,7 +220,24 @@ export declare class SkillsService {
242
220
  /** The plugin manifest suggests pinning this skill (spec §9.1). */
243
221
  suggestPinned?: boolean;
244
222
  };
223
+ /**
224
+ * The files are already stored (no storage I/O here, so it may join a transaction): at
225
+ * `prefix` in `kernel/<orgId>/`, or, for a skill the org promoted, the platform version
226
+ * {@link prepareStoredVersion} wrote. The caller discards that one when this fails.
227
+ */
228
+ stored?: {
229
+ owner?: OwnerVersion;
230
+ };
245
231
  }): Promise<UploadedSkill>;
232
+ /**
233
+ * For a new version whose files are already in `kernel/<orgId>/` (a proposed version's draft):
234
+ * when the org skill of that name is one the org promoted, writes the files as the platform
235
+ * row's next version (never inside a transaction) and returns it for
236
+ * `createVersionFromFiles({ stored: { owner } })`; else nothing is written.
237
+ */
238
+ prepareStoredVersion(orgId: string, pkg: SkillPackageInput): Promise<OwnerVersion | undefined>;
239
+ /** Deletes a platform version {@link prepareStoredVersion} wrote that was not used. */
240
+ discardStoredVersion(owner: OwnerVersion): Promise<void>;
246
241
  /** Writes a skill package's files to `kernel/<orgId>/<prefix>` (a few at a time). */
247
242
  private putFiles;
248
243
  /** The transaction of {@link createVersionFromFiles}. */
@@ -266,6 +261,16 @@ export declare class SkillsService {
266
261
  * `usableSkills`; `origin` tells them apart), each with the ids of those copies (`linkedIds`).
267
262
  */
268
263
  list(orgId: string): Promise<SkillSummary[]>;
264
+ /**
265
+ * The agent's skills as the agent page lists them, by name: {@link listForAgent} (`shared:
266
+ * false`) plus `sharePending`, true while a share request of the skill waits for an org admin,
267
+ * and the org skills the agent shared (`origin_agent_id`; `scope: 'org'`, `shared: true`, same
268
+ * id as before sharing): the agent keeps them without selecting them.
269
+ */
270
+ listOwnForAgent(orgId: string, agentId: string): Promise<(SkillSummary & {
271
+ sharePending: boolean;
272
+ shared: boolean;
273
+ })[]>;
269
274
  /** 对该 agent 自动生效的技能(`scope=agent`、未删除),按名字排序。 */
270
275
  listForAgent(orgId: string, agentId: string): Promise<SkillSummary[]>;
271
276
  /**
@@ -292,7 +297,9 @@ export declare class SkillsService {
292
297
  /**
293
298
  * In the caller's transaction (`mandatory`: the caller holds the org row lock and checked
294
299
  * `skills.manage`), soft-deletes an org-scope skill of the org: what {@link softDelete} writes
295
- * (audit `skill.deleted`); missing or deleted -> `skill.not_found`. The owner delete of a
300
+ * (audit `skill.deleted`); missing or deleted -> `skill.not_found`. Its pending version
301
+ * proposals are cancelled in the same transaction (trials reverted; drafts deleted and events
302
+ * published after the commit). The owner delete of a
296
303
  * promoted skill (`OwnedItems.deleteSkill`) calls it after its platform row was dealt with.
297
304
  */
298
305
  deleteOrgSkillIn(orgId: string, skillId: string, actorAccountId: string, actorTokenId: string | undefined, auditDetail?: Record<string, unknown>): Promise<void>;