@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.
- package/bootstrap.json +25 -25
- package/dist/admin/admin-agents.controller.d.ts +28 -0
- package/dist/admin/admin-agents.controller.js +79 -0
- package/dist/admin/admin-connectors.controller.d.ts +29 -0
- package/dist/admin/admin-connectors.controller.js +79 -0
- package/dist/admin/admin-items.schemas.d.ts +42 -0
- package/dist/admin/admin-items.schemas.js +48 -0
- package/dist/admin/admin.controller.d.ts +11 -20
- package/dist/admin/admin.controller.js +28 -34
- package/dist/admin/admin.module.d.ts +4 -1
- package/dist/admin/admin.module.js +50 -4
- package/dist/admin/admin.schemas.d.ts +4 -0
- package/dist/admin/admin.schemas.js +4 -1
- package/dist/admin/audit.controller.d.ts +14 -0
- package/dist/admin/audit.controller.js +42 -0
- package/dist/admin/audit.schemas.d.ts +8 -0
- package/dist/admin/audit.schemas.js +17 -0
- package/dist/admin/health.service.d.ts +104 -0
- package/dist/admin/health.service.js +246 -0
- package/dist/admin/overview.controller.d.ts +31 -0
- package/dist/admin/overview.controller.js +78 -0
- package/dist/admin/overview.schemas.d.ts +8 -0
- package/dist/admin/overview.schemas.js +13 -0
- package/dist/admin/overview.service.d.ts +112 -0
- package/dist/admin/overview.service.js +223 -0
- package/dist/admin/platform-audit.service.d.ts +41 -0
- package/dist/admin/platform-audit.service.js +70 -0
- package/dist/admin/usage.service.d.ts +149 -0
- package/dist/admin/usage.service.js +321 -0
- package/dist/agents/admin-agents.d.ts +92 -0
- package/dist/agents/admin-agents.js +170 -0
- package/dist/agents/agents.module.d.ts +4 -3
- package/dist/agents/agents.module.js +7 -3
- package/dist/agents/agents.service.d.ts +41 -3
- package/dist/agents/agents.service.js +113 -27
- package/dist/agents/definition.d.ts +16 -0
- package/dist/agents/definition.js +32 -2
- package/dist/agents/org-templates.js +4 -0
- package/dist/background/launch.d.ts +1 -1
- package/dist/background/launch.js +3 -3
- package/dist/background/tasks.service.d.ts +35 -3
- package/dist/background/tasks.service.js +91 -36
- package/dist/catalog/catalog.module.js +8 -2
- package/dist/channels/channels.module.js +11 -2
- package/dist/channels/supervisor.d.ts +2 -0
- package/dist/channels/supervisor.js +4 -0
- package/dist/conversations/events.js +1 -1
- package/dist/conversations/titles.js +4 -8
- package/dist/db/kernel-schema.d.ts +8 -1
- package/dist/devices/devices.module.js +5 -2
- package/dist/errors.d.ts +22 -2
- package/dist/errors.js +22 -2
- package/dist/evals/evals.module.js +11 -2
- package/dist/extensions/permission-view.js +3 -3
- package/dist/http/api.module.js +10 -1
- package/dist/identity/settings.js +3 -2
- package/dist/identity/write-actor.d.ts +11 -0
- package/dist/identity/write-actor.js +7 -0
- package/dist/inbox/inbox.service.d.ts +2 -1
- package/dist/inbox/platform-inbox.service.d.ts +7 -1
- package/dist/inbox/platform-inbox.service.js +20 -2
- package/dist/infra/presence/presence.module.d.ts +6 -0
- package/dist/infra/presence/presence.module.js +22 -0
- package/dist/infra/presence/replica-presence.d.ts +87 -0
- package/dist/infra/presence/replica-presence.js +189 -0
- package/dist/kernel.module.js +3 -0
- package/dist/lifecycle/kernel-lifecycle.d.ts +3 -1
- package/dist/lifecycle/kernel-lifecycle.js +17 -3
- package/dist/local-devices/local-device-gateway.d.ts +6 -0
- package/dist/local-devices/local-device-gateway.js +12 -0
- package/dist/overrides.d.ts +7 -1
- package/dist/overrides.js +4 -0
- package/dist/platform/exclusions.js +15 -0
- package/dist/platform/operation.d.ts +1 -1
- package/dist/platform/operation.js +1 -1
- package/dist/platform/ops/admin-items.ops.d.ts +37 -0
- package/dist/platform/ops/admin-items.ops.js +278 -0
- package/dist/platform/ops/admin.ops.d.ts +10 -10
- package/dist/platform/ops/admin.ops.js +111 -42
- package/dist/platform/ops/agent-skills.ops.d.ts +29 -3
- package/dist/platform/ops/agent-skills.ops.js +290 -7
- package/dist/platform/ops/inbox.ops.js +7 -4
- package/dist/platform/ops/platform.ops.js +1 -1
- package/dist/platform/ops/skills.ops.js +1 -57
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +5 -0
- package/dist/platform/route-schemas.js +8 -0
- package/dist/roles/gateway-role.d.ts +4 -0
- package/dist/roles/gateway-role.js +8 -0
- package/dist/runtime/run-jobs.js +4 -2
- package/dist/sandbox/sandbox-file-reader.d.ts +35 -0
- package/dist/sandbox/sandbox-file-reader.js +98 -0
- package/dist/skills/agent-skills.controller.d.ts +48 -0
- package/dist/skills/agent-skills.controller.js +131 -0
- package/dist/skills/agent-skills.service.d.ts +262 -0
- package/dist/skills/agent-skills.service.js +996 -0
- package/dist/skills/catalog.d.ts +8 -6
- package/dist/skills/catalog.js +8 -4
- package/dist/skills/changes.controller.d.ts +5 -22
- package/dist/skills/changes.controller.js +7 -55
- package/dist/skills/changes.service.d.ts +69 -115
- package/dist/skills/changes.service.js +249 -599
- package/dist/skills/conversation-catalog.d.ts +53 -0
- package/dist/skills/conversation-catalog.js +149 -0
- package/dist/skills/invocation-keys.d.ts +1 -1
- package/dist/skills/invocation-keys.js +1 -1
- package/dist/skills/package.d.ts +0 -5
- package/dist/skills/package.js +0 -14
- package/dist/skills/skills.controller.js +1 -10
- package/dist/skills/skills.module.js +15 -2
- package/dist/skills/skills.schemas.d.ts +11 -15
- package/dist/skills/skills.schemas.js +27 -20
- package/dist/skills/skills.service.d.ts +37 -30
- package/dist/skills/skills.service.js +107 -59
- package/dist/skills/usable.d.ts +4 -3
- package/dist/skills/usable.js +13 -4
- package/dist/tools/admin-connectors.d.ts +69 -0
- package/dist/tools/admin-connectors.js +135 -0
- package/dist/tools/builtin-names.d.ts +9 -14
- package/dist/tools/builtin-names.js +13 -22
- package/dist/tools/builtins/builtin-tools.d.ts +2 -3
- package/dist/tools/builtins/builtin-tools.js +21 -26
- package/dist/tools/builtins/sandbox/bash.js +2 -2
- package/dist/tools/builtins/skill-load.js +1 -0
- package/dist/tools/connectors.controller.d.ts +5 -1
- package/dist/tools/connectors.controller.js +17 -3
- package/dist/tools/connectors.service.d.ts +20 -1
- package/dist/tools/connectors.service.js +61 -0
- package/dist/tools/legacy-keys.js +0 -1
- package/dist/tools/org-tool-switches.d.ts +3 -2
- package/dist/tools/org-tool-switches.js +4 -4
- package/dist/tools/policy.d.ts +7 -9
- package/dist/tools/policy.js +11 -22
- package/dist/tools/tools.module.js +3 -0
- package/package.json +1 -1
- package/src/migrations/0109_audit_platform_idx.sql +5 -0
- package/src/migrations/0110_usage_time_idx.sql +8 -0
- package/src/migrations/0111_agent_skill_sharing.sql +22 -0
- package/src/migrations/0112_skill_share_requests.sql +4 -0
- package/dist/tools/builtins/skill-write.d.ts +0 -59
- 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
|
|
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
|
|
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 = [
|
package/dist/skills/package.d.ts
CHANGED
|
@@ -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;
|
package/dist/skills/package.js
CHANGED
|
@@ -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
|
-
|
|
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: [
|
|
36
|
-
|
|
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.
|
|
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 {
|
|
1
|
+
import { hasControlChars, MEDIA_NAME_MAX_CHARS } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
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`.
|
|
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>;
|