@capybara-hq/kernel 0.1.0-rc.20 → 0.1.0-rc.22
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 +10 -1
- package/dist/agents/admin-agents.d.ts +16 -3
- package/dist/agents/admin-agents.js +65 -7
- package/dist/agents/agents.schemas.d.ts +24 -6
- package/dist/agents/agents.service.d.ts +4 -3
- package/dist/agents/agents.service.js +39 -26
- package/dist/agents/definition.d.ts +44 -6
- package/dist/agents/definition.js +86 -18
- package/dist/agents/from-template.d.ts +6 -3
- package/dist/agents/from-template.js +57 -44
- package/dist/agents/org-templates.js +97 -41
- package/dist/agents/templates.d.ts +6 -0
- package/dist/agents/templates.js +15 -1
- package/dist/catalog/admin-catalog.controller.d.ts +2 -4
- package/dist/catalog/admin-catalog.controller.js +2 -6
- package/dist/catalog/catalog.module.js +3 -0
- package/dist/catalog/claude/report.d.ts +9 -2
- package/dist/catalog/claude/report.js +1 -0
- package/dist/catalog/claude/translate.js +1 -1
- package/dist/catalog/component-promotion.js +1 -2
- package/dist/catalog/component-state.d.ts +13 -1
- package/dist/catalog/component-state.js +14 -5
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/marketplaces.service.js +2 -1
- package/dist/catalog/org-catalog.controller.d.ts +13 -3
- package/dist/catalog/org-catalog.controller.js +26 -4
- package/dist/catalog/org-catalog.schemas.d.ts +13 -0
- package/dist/catalog/org-catalog.schemas.js +34 -0
- package/dist/catalog/org-plugin-uploads.controller.js +0 -1
- package/dist/catalog/org-plugin-uploads.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.js +0 -3
- package/dist/catalog/org-plugin-uploads.schemas.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.schemas.js +0 -2
- package/dist/catalog/platform-components.d.ts +0 -1
- package/dist/catalog/platform-components.js +3 -8
- package/dist/catalog/plugin-import.d.ts +0 -8
- package/dist/catalog/plugin-import.js +4 -35
- package/dist/catalog/plugin-install.d.ts +2 -2
- package/dist/catalog/plugin-install.js +2 -2
- package/dist/catalog/plugin-package.d.ts +7 -0
- package/dist/catalog/plugin-package.js +27 -1
- package/dist/catalog/plugin-upgrade.d.ts +5 -6
- package/dist/catalog/plugin-upgrade.js +5 -23
- package/dist/catalog/requests.service.d.ts +66 -10
- package/dist/catalog/requests.service.js +196 -15
- package/dist/channels/actions.d.ts +2 -0
- package/dist/channels/actions.js +19 -4
- package/dist/channels/cards.d.ts +16 -2
- package/dist/channels/cards.js +40 -7
- package/dist/channels/channels.module.js +3 -0
- package/dist/channels/outbound.d.ts +22 -1
- package/dist/channels/outbound.js +116 -7
- package/dist/channels/phrases.d.ts +25 -0
- package/dist/channels/phrases.js +19 -0
- package/dist/channels/threads.d.ts +12 -0
- package/dist/channels/threads.js +34 -0
- package/dist/config/schema.d.ts +23 -1
- package/dist/config/schema.js +105 -5
- package/dist/conversations/conversations.service.d.ts +42 -9
- package/dist/conversations/conversations.service.js +57 -25
- package/dist/conversations/events.d.ts +106 -11
- package/dist/conversations/events.js +121 -18
- package/dist/conversations/message-context.d.ts +6 -3
- package/dist/conversations/message-context.js +14 -10
- package/dist/conversations/queue.d.ts +15 -2
- package/dist/conversations/queue.js +22 -3
- package/dist/conversations/queue.service.d.ts +12 -6
- package/dist/conversations/queue.service.js +36 -13
- package/dist/db/kernel-schema.d.ts +105 -3
- package/dist/devices/conversation-devices.d.ts +8 -1
- package/dist/devices/conversation-devices.js +8 -0
- package/dist/devices/devices.controller.d.ts +21 -4
- package/dist/devices/devices.controller.js +46 -8
- package/dist/devices/devices.schemas.d.ts +9 -1
- package/dist/devices/devices.schemas.js +11 -3
- package/dist/devices/devices.service.d.ts +176 -4
- package/dist/devices/devices.service.js +341 -6
- package/dist/devices/dto.d.ts +56 -3
- package/dist/devices/dto.js +51 -1
- package/dist/devices/public-suffix.d.ts +6 -0
- package/dist/devices/public-suffix.js +79 -0
- package/dist/errors.d.ts +46 -5
- package/dist/errors.js +46 -5
- package/dist/evals/reconstruct.d.ts +18 -13
- package/dist/evals/reconstruct.js +50 -36
- package/dist/evals/recorded-tools.d.ts +7 -7
- package/dist/evals/recorded-tools.js +4 -3
- package/dist/evals/trajectory.d.ts +6 -1
- package/dist/evals/trajectory.js +7 -0
- package/dist/extensions/bootstrap.d.ts +49 -10
- package/dist/extensions/bootstrap.js +104 -27
- package/dist/extensions/package.js +2 -2
- package/dist/http/api.module.js +5 -0
- package/dist/inbox/inbox.module.js +2 -1
- package/dist/inbox/inbox.service.d.ts +19 -4
- package/dist/inbox/inbox.service.js +14 -3
- package/dist/infra/config/kernel-config.service.js +11 -0
- package/dist/infra/events/kernel-events.d.ts +2 -1
- package/dist/lifecycle/kernel-lifecycle.d.ts +5 -1
- package/dist/lifecycle/kernel-lifecycle.js +16 -4
- package/dist/local-devices/device-policy.service.d.ts +65 -0
- package/dist/local-devices/device-policy.service.js +217 -0
- package/dist/local-devices/local-device-gateway.js +4 -1
- package/dist/local-devices/local-device-provider.d.ts +40 -3
- package/dist/local-devices/local-device-provider.js +109 -7
- package/dist/local-devices/local-devices.module.d.ts +2 -1
- package/dist/local-devices/local-devices.module.js +19 -3
- package/dist/local-devices/machines.d.ts +14 -3
- package/dist/local-devices/machines.js +41 -3
- package/dist/local-devices/network-grants.d.ts +104 -0
- package/dist/local-devices/network-grants.js +288 -0
- package/dist/local-devices/policy-summary.d.ts +20 -0
- package/dist/local-devices/policy-summary.js +63 -0
- package/dist/orgs/orgs.module.js +4 -10
- package/dist/orgs/roles.d.ts +1 -1
- package/dist/orgs/roles.js +4 -0
- package/dist/orgs/settings.controller.js +2 -1
- package/dist/orgs/settings.service.d.ts +164 -12
- package/dist/orgs/settings.service.js +212 -24
- package/dist/platform/exclusions.js +11 -1
- package/dist/platform/operation.js +4 -0
- package/dist/platform/ops/admin-items.ops.js +1 -1
- package/dist/platform/ops/agent-skills.ops.js +2 -2
- package/dist/platform/ops/agent-templates.ops.js +2 -2
- package/dist/platform/ops/agents.ops.js +11 -15
- package/dist/platform/ops/devices.ops.js +3 -1
- package/dist/platform/ops/inbox.ops.js +19 -0
- package/dist/platform/ops/org-settings.ops.js +14 -5
- package/dist/platform/ops/platform.ops.d.ts +9 -1
- package/dist/platform/ops/platform.ops.js +34 -12
- package/dist/platform/ops/requests.ops.js +3 -2
- package/dist/platform/ops/schedules.ops.d.ts +21 -0
- package/dist/platform/ops/schedules.ops.js +470 -0
- package/dist/platform/ops/skills.ops.js +3 -3
- 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 +3 -0
- package/dist/roles/worker.module.js +3 -0
- package/dist/runtime/context/build.js +13 -4
- package/dist/runtime/context/compaction.js +6 -3
- package/dist/runtime/context/device-paths.d.ts +43 -21
- package/dist/runtime/context/device-paths.js +104 -21
- package/dist/runtime/context/run-context.d.ts +9 -0
- package/dist/runtime/context/run-context.js +8 -0
- package/dist/runtime/context/skills.d.ts +14 -1
- package/dist/runtime/context/skills.js +36 -5
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +16 -5
- package/dist/runtime/run-finish.d.ts +6 -1
- package/dist/runtime/run-finish.js +16 -2
- package/dist/runtime/run-queue.js +21 -2
- package/dist/runtime/run-worker.js +14 -33
- package/dist/runtime/runtime.module.js +3 -0
- package/dist/runtime/subagents/definition.d.ts +32 -14
- package/dist/runtime/subagents/definition.js +106 -109
- package/dist/runtime/subagents/dispatch.js +4 -3
- package/dist/runtime/subagents/preamble.d.ts +2 -1
- package/dist/runtime/subagents/preamble.js +2 -1
- package/dist/runtime/waits.js +14 -4
- package/dist/sandbox/sandbox.service.d.ts +120 -10
- package/dist/sandbox/sandbox.service.js +335 -28
- package/dist/sandbox/tiers.d.ts +86 -0
- package/dist/sandbox/tiers.js +96 -0
- package/dist/schedules/effective-settings.d.ts +16 -0
- package/dist/schedules/effective-settings.js +17 -0
- package/dist/schedules/errors.d.ts +8 -0
- package/dist/schedules/errors.js +8 -0
- package/dist/schedules/fire.d.ts +118 -0
- package/dist/schedules/fire.js +625 -0
- package/dist/schedules/grant-rules.d.ts +94 -0
- package/dist/schedules/grant-rules.js +126 -0
- package/dist/schedules/job-ids.d.ts +32 -0
- package/dist/schedules/job-ids.js +27 -0
- package/dist/schedules/pause.d.ts +9 -0
- package/dist/schedules/pause.js +33 -0
- package/dist/schedules/run-tracker.d.ts +81 -0
- package/dist/schedules/run-tracker.js +333 -0
- package/dist/schedules/scan.job.d.ts +64 -0
- package/dist/schedules/scan.job.js +303 -0
- package/dist/schedules/schedule-math.d.ts +31 -0
- package/dist/schedules/schedule-math.js +223 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +351 -0
- package/dist/schedules/scheduled-tasks.service.js +1193 -0
- package/dist/schedules/schedules.controller.d.ts +68 -0
- package/dist/schedules/schedules.controller.js +242 -0
- package/dist/schedules/schedules.module.d.ts +16 -0
- package/dist/schedules/schedules.module.js +65 -0
- package/dist/schedules/schedules.schemas.d.ts +316 -0
- package/dist/schedules/schedules.schemas.js +186 -0
- package/dist/schedules/store.d.ts +81 -0
- package/dist/schedules/store.js +390 -0
- package/dist/schedules/store.module.d.ts +6 -0
- package/dist/schedules/store.module.js +21 -0
- package/dist/schedules/types.d.ts +108 -0
- package/dist/schedules/types.js +1 -0
- package/dist/schedules/unattended.d.ts +117 -0
- package/dist/schedules/unattended.js +228 -0
- package/dist/setup/oauth-token-probe.js +8 -4
- package/dist/skills/agent-skills.service.d.ts +4 -8
- package/dist/skills/agent-skills.service.js +18 -53
- package/dist/skills/catalog.d.ts +65 -62
- package/dist/skills/catalog.js +109 -206
- package/dist/skills/conversation-catalog.d.ts +3 -3
- package/dist/skills/conversation-catalog.js +5 -12
- package/dist/skills/invoke.d.ts +2 -2
- package/dist/skills/skills.controller.d.ts +3 -1
- package/dist/skills/skills.service.d.ts +38 -37
- package/dist/skills/skills.service.js +168 -107
- package/dist/skills/usable.d.ts +57 -8
- package/dist/skills/usable.js +172 -38
- package/dist/tools/approvals.controller.d.ts +4 -2
- package/dist/tools/approvals.controller.js +17 -2
- package/dist/tools/approvals.schemas.d.ts +1 -0
- package/dist/tools/approvals.schemas.js +3 -1
- package/dist/tools/approvals.service.d.ts +182 -7
- package/dist/tools/approvals.service.js +597 -13
- package/dist/tools/builtin-names.d.ts +25 -4
- package/dist/tools/builtin-names.js +45 -4
- package/dist/tools/builtins/builtin-tools.d.ts +8 -0
- package/dist/tools/builtins/builtin-tools.js +23 -2
- package/dist/tools/builtins/sandbox/bash.js +16 -7
- package/dist/tools/builtins/sandbox/common.d.ts +1 -1
- package/dist/tools/builtins/sandbox/common.js +33 -10
- package/dist/tools/builtins/sandbox/edit.js +7 -3
- package/dist/tools/builtins/sandbox/file-state.d.ts +31 -1
- package/dist/tools/builtins/sandbox/file-state.js +33 -3
- package/dist/tools/builtins/sandbox/write.js +8 -4
- package/dist/tools/builtins/schedules.d.ts +107 -0
- package/dist/tools/builtins/schedules.js +789 -0
- package/dist/tools/elicitation.d.ts +2 -0
- package/dist/tools/elicitation.js +1 -1
- package/dist/tools/fixed-ask.d.ts +7 -0
- package/dist/tools/fixed-ask.js +9 -0
- package/dist/tools/network-asks.d.ts +109 -0
- package/dist/tools/network-asks.js +221 -0
- package/dist/tools/pipeline.d.ts +53 -6
- package/dist/tools/pipeline.js +202 -13
- package/dist/tools/policy.d.ts +21 -8
- package/dist/tools/policy.js +63 -9
- package/dist/tools/registry.d.ts +10 -1
- package/dist/tools/tools.module.js +12 -0
- package/package.json +2 -2
- package/src/migrations/0113_scheduled_tasks.sql +108 -0
- package/src/migrations/0114_network_asks.sql +23 -0
- package/src/migrations/0115_network_requests.sql +28 -0
- package/src/migrations/0116_skills_default_on.sql +37 -0
- package/src/migrations/0117_sandbox_tiers.sql +11 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
2
|
+
import { readScheduleSettings } from "./effective-settings.js";
|
|
3
|
+
/**
|
|
4
|
+
* The scheduled task run a kernel run serves (scheduled tasks spec §4, §7), or null for an attended
|
|
5
|
+
* run: `runs.scheduled_task_run_id`, set on the run a task's message triggered and inherited by the
|
|
6
|
+
* runs descending from it without a person (sub-agent runs, wake runs). A run whose task run row is
|
|
7
|
+
* gone is treated as attended.
|
|
8
|
+
*/
|
|
9
|
+
export async function unattendedTaskRunOf(db, runId) {
|
|
10
|
+
const row = await db
|
|
11
|
+
.selectFrom("runs as r")
|
|
12
|
+
.innerJoin("scheduled_task_runs as tr", "tr.id", "r.scheduled_task_run_id")
|
|
13
|
+
.select(["tr.id", "tr.task_id", "tr.trial"])
|
|
14
|
+
.where("r.id", "=", runId)
|
|
15
|
+
.executeTakeFirst();
|
|
16
|
+
return row ? { taskRunId: row.id, taskId: row.task_id, trial: row.trial } : null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The `scheduled_task_run_id` a new run whose boundary (`runs.trigger_seq`) is event `seq` of
|
|
20
|
+
* `conversationId` gets (scheduled tasks spec §4), read in the caller's transaction:
|
|
21
|
+
* - a `user_message`: its schedule marker's task run; a sub-agent's task or `SendMessage` message
|
|
22
|
+
* (`dispatch`): what its parent run has; a person's message: null;
|
|
23
|
+
* - a `background_task_finished`: what the run that launched the task has;
|
|
24
|
+
* - a `subagent_delivery`: what the child run that delivered it has (it inherited from its parent);
|
|
25
|
+
* - anything else, or no such event: null.
|
|
26
|
+
*/
|
|
27
|
+
export async function scheduledTaskRunAt(t, conversationId, seq) {
|
|
28
|
+
const ev = await t
|
|
29
|
+
.selectFrom("conversation_events")
|
|
30
|
+
.select(["type", "payload"])
|
|
31
|
+
.where("conversation_id", "=", conversationId)
|
|
32
|
+
.where("seq", "=", String(seq))
|
|
33
|
+
.executeTakeFirst();
|
|
34
|
+
if (!ev)
|
|
35
|
+
return null;
|
|
36
|
+
const p = ev.payload;
|
|
37
|
+
let runId;
|
|
38
|
+
if (ev.type === "user_message") {
|
|
39
|
+
if (typeof p.schedule?.taskRunId === "string")
|
|
40
|
+
return p.schedule.taskRunId;
|
|
41
|
+
if (typeof p.dispatch?.parentRunId === "string")
|
|
42
|
+
runId = p.dispatch.parentRunId;
|
|
43
|
+
}
|
|
44
|
+
else if (ev.type === "background_task_finished" && typeof p.taskId === "string") {
|
|
45
|
+
const task = await t
|
|
46
|
+
.selectFrom("background_tasks")
|
|
47
|
+
.select("run_id")
|
|
48
|
+
.where("id", "=", p.taskId)
|
|
49
|
+
.where("conversation_id", "=", conversationId)
|
|
50
|
+
.executeTakeFirst();
|
|
51
|
+
runId = task?.run_id;
|
|
52
|
+
}
|
|
53
|
+
else if (ev.type === "subagent_delivery" && typeof p.childRunId === "string") {
|
|
54
|
+
runId = p.childRunId;
|
|
55
|
+
}
|
|
56
|
+
if (runId === undefined)
|
|
57
|
+
return null;
|
|
58
|
+
const run = await t
|
|
59
|
+
.selectFrom("runs")
|
|
60
|
+
.select("scheduled_task_run_id")
|
|
61
|
+
.where("id", "=", runId)
|
|
62
|
+
.executeTakeFirst();
|
|
63
|
+
return run?.scheduled_task_run_id ?? null;
|
|
64
|
+
}
|
|
65
|
+
/** The tool result of an ask outside the task's grants in an unattended run, `fail` mode (§7). */
|
|
66
|
+
export const UNATTENDED_ASK_ERROR = (tool) => `This scheduled run cannot ask for approval; the task's owner can add ${tool} to the task's approved tools.`;
|
|
67
|
+
/**
|
|
68
|
+
* The Platform operations that change an agent's skills: an unattended run never calls them
|
|
69
|
+
* without a person (spec §7), even where a private agent's owner would not be asked. Each is a
|
|
70
|
+
* fixed ask there, outside every grant.
|
|
71
|
+
*/
|
|
72
|
+
export const UNATTENDED_SKILL_OPERATIONS = new Set([
|
|
73
|
+
"agents.register-skill",
|
|
74
|
+
"agents.delete-skill",
|
|
75
|
+
"agents.share-skill",
|
|
76
|
+
]);
|
|
77
|
+
/** Whether a call's policy key (`Platform:<operation>`) is one of {@link UNATTENDED_SKILL_OPERATIONS}. */
|
|
78
|
+
export function changesAgentSkills(policyKey) {
|
|
79
|
+
const prefix = "Platform:";
|
|
80
|
+
return (policyKey.startsWith(prefix) && UNATTENDED_SKILL_OPERATIONS.has(policyKey.slice(prefix.length)));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The result of a fixed ask (R3, `requiresUserInteraction`, a skill change unattended, a per-call
|
|
84
|
+
* policy that never remembers) in an unattended run, `fail` mode: no grant can ever cover it, so
|
|
85
|
+
* it is not a missing grant either.
|
|
86
|
+
*/
|
|
87
|
+
export const UNATTENDED_FIXED_ASK_ERROR = (tool) => `${tool} always needs a person's approval, which a scheduled run cannot ask for; state what you wanted to do in your final answer.`;
|
|
88
|
+
/** The result of `AskUserQuestion` (or any other input request) in an unattended run, `fail` mode. */
|
|
89
|
+
export const UNATTENDED_QUESTION_ERROR = "No one is present in this scheduled run; decide with the information you have, or state the open question in your final answer.";
|
|
90
|
+
/** The result of a call that needs the creator's personal connection, unattended, `fail` mode. */
|
|
91
|
+
export const UNATTENDED_CONNECTION_ERROR = (connector) => `This scheduled run cannot ask the user to connect ${connector}; the task's owner can connect it before the next run.`;
|
|
92
|
+
/** The note on a connector call whose elicitation was declined in an unattended run (`fail`). */
|
|
93
|
+
export const UNATTENDED_ELICITATION_NOTE = "[The app asked for user input, but no one is present in this scheduled run, so the request was declined. Decide with the information you have, or state the open question in your final answer.]";
|
|
94
|
+
/** The `missing_grants` entry of a personal connector the creator has not connected (§7). */
|
|
95
|
+
export const connectionGrantKey = (connectorSlug) => `connection:${connectorSlug}`;
|
|
96
|
+
/** The `missing_grants` entry of a refused network host (network ask-to-allow spec §8.1, §8.3). */
|
|
97
|
+
export const networkGrantKey = (host) => `network:${host}`;
|
|
98
|
+
/** An unattended run that is not a trial run: the §7 rules apply. */
|
|
99
|
+
export const unattendedRules = (u) => u !== null && u !== undefined && !u.trial;
|
|
100
|
+
/** In `fail` mode, an unattended run never waits for a person (§7). */
|
|
101
|
+
export const unattendedFails = (u) => unattendedRules(u) && u.unattendedAsk === "fail";
|
|
102
|
+
/**
|
|
103
|
+
* Whether the task's creator joined the run (scheduled tasks rulings, Task 7 and Task 8): the run's
|
|
104
|
+
* conversation holds a `user_message` after the run's boundary (`trigger_seq`) that the creator
|
|
105
|
+
* wrote (no schedule marker, no sub-agent dispatch, `authorAccountId` = the task's account) and
|
|
106
|
+
* that the run already absorbed (at or below the `absorbedUpToSeq` of its latest model call). The
|
|
107
|
+
* rest of such a run is attended. Another person's message (someone replying in a group thread the
|
|
108
|
+
* task posted into) does not make it attended: the task's grants are the creator's, and only the
|
|
109
|
+
* creator is the one asked.
|
|
110
|
+
*/
|
|
111
|
+
async function personJoined(db, runId) {
|
|
112
|
+
const row = await db
|
|
113
|
+
.selectFrom("runs as r")
|
|
114
|
+
.innerJoin("scheduled_task_runs as tr", "tr.id", "r.scheduled_task_run_id")
|
|
115
|
+
.innerJoin("scheduled_tasks as st", "st.id", "tr.task_id")
|
|
116
|
+
.select(sql `EXISTS (
|
|
117
|
+
SELECT 1 FROM capybara.conversation_events u
|
|
118
|
+
WHERE u.conversation_id = r.conversation_id
|
|
119
|
+
AND u.type = 'user_message'
|
|
120
|
+
AND u.seq > r.trigger_seq
|
|
121
|
+
AND NOT (u.payload ? 'schedule')
|
|
122
|
+
AND NOT (u.payload ? 'dispatch')
|
|
123
|
+
AND u.payload->>'authorAccountId' = st.account_id
|
|
124
|
+
AND u.seq <= (
|
|
125
|
+
SELECT COALESCE((m.payload->'request'->>'absorbedUpToSeq')::bigint, m.seq)
|
|
126
|
+
FROM capybara.conversation_events m
|
|
127
|
+
WHERE m.conversation_id = r.conversation_id AND m.run_id = r.id
|
|
128
|
+
AND m.type = 'model_call'
|
|
129
|
+
ORDER BY m.seq DESC
|
|
130
|
+
LIMIT 1
|
|
131
|
+
)
|
|
132
|
+
)`.as("joined"))
|
|
133
|
+
.where("r.id", "=", runId)
|
|
134
|
+
.executeTakeFirst();
|
|
135
|
+
return row?.joined === true;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The scheduled task context of a run's tool calls (`PolicyContext.unattended`), or null when the
|
|
139
|
+
* run is attended: it serves no task run, or a person's message joined it ({@link personJoined}).
|
|
140
|
+
* A trial run is returned with `trial: true` (attended; its approvals become task grants).
|
|
141
|
+
*/
|
|
142
|
+
export async function unattendedRunOf(db, config, orgId, runId) {
|
|
143
|
+
const run = await unattendedTaskRunOf(db, runId);
|
|
144
|
+
if (run === null)
|
|
145
|
+
return null;
|
|
146
|
+
if (!run.trial && (await personJoined(db, runId)))
|
|
147
|
+
return null;
|
|
148
|
+
const s = await readScheduleSettings(db, config, orgId);
|
|
149
|
+
return { ...run, unattendedAsk: s.unattendedAsk, forbiddenTools: s.forbiddenTools };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The scheduled task whose grants a run's actions use (spec §7; network ask-to-allow spec §8.1),
|
|
153
|
+
* or null: the run serves a task run that is no trial run and no person joined
|
|
154
|
+
* ({@link personJoined}): the same rule as {@link unattendedRules} on {@link unattendedRunOf},
|
|
155
|
+
* without the org's settings. `accountId`: the task's creator; `orgId`: the task's org.
|
|
156
|
+
*/
|
|
157
|
+
export async function taskGrantsRunOf(db, runId) {
|
|
158
|
+
const run = await unattendedTaskRunOf(db, runId);
|
|
159
|
+
if (run === null || run.trial)
|
|
160
|
+
return null;
|
|
161
|
+
if (await personJoined(db, runId))
|
|
162
|
+
return null;
|
|
163
|
+
const task = await db
|
|
164
|
+
.selectFrom("scheduled_tasks")
|
|
165
|
+
.select(["id", "account_id", "org_id"])
|
|
166
|
+
.where("id", "=", run.taskId)
|
|
167
|
+
.where("deleted_at", "is", null)
|
|
168
|
+
.executeTakeFirst();
|
|
169
|
+
return task ? { taskId: task.id, accountId: task.account_id, orgId: task.org_id } : null;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Appends a key to a task run's `missing_grants` (a tool key, or `connection:<slug>`), once: one
|
|
173
|
+
* atomic update, so parallel calls of a run and of its sub-agents never lose or repeat a key.
|
|
174
|
+
*/
|
|
175
|
+
export async function appendMissingGrant(db, taskRunId, key) {
|
|
176
|
+
await db
|
|
177
|
+
.updateTable("scheduled_task_runs")
|
|
178
|
+
.set({
|
|
179
|
+
missing_grants: sql `CASE WHEN missing_grants ? ${key} THEN missing_grants
|
|
180
|
+
ELSE missing_grants || jsonb_build_array(${key}::text) END`,
|
|
181
|
+
})
|
|
182
|
+
.where("id", "=", taskRunId)
|
|
183
|
+
.execute();
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The task grant that lets an unattended run use the open sandbox tier (sandbox dev tiers spec
|
|
187
|
+
* §11.4): without it a run of a scheduled task is capped at `dev` on a local device, and refused
|
|
188
|
+
* on an unsandboxed local device or a cloud device whose tier is open.
|
|
189
|
+
*/
|
|
190
|
+
export const TIER_OPEN_GRANT_KEY = "tier:open";
|
|
191
|
+
/** The `spec_hash` the `tier:open` grant is stored with (no tool definition behind it). */
|
|
192
|
+
export const TIER_GRANT_SPEC_HASH = "tier";
|
|
193
|
+
/**
|
|
194
|
+
* The tier rule of an unattended run on `deviceId` (spec §11.4), or null when the run is attended
|
|
195
|
+
* (no task run, a trial run, or a person joined: the same rule as {@link taskGrantsRunOf}).
|
|
196
|
+
* `openGranted`: the task holds the `tier:open` grant and `deviceId` is a live device of the task's
|
|
197
|
+
* org owned by its creator (a creator approves for their own devices only, as with network
|
|
198
|
+
* grants). A deleted task grants nothing.
|
|
199
|
+
*/
|
|
200
|
+
export async function unattendedTierOf(db, runId, deviceId) {
|
|
201
|
+
const run = await unattendedTaskRunOf(db, runId);
|
|
202
|
+
if (run === null || run.trial)
|
|
203
|
+
return null;
|
|
204
|
+
if (await personJoined(db, runId))
|
|
205
|
+
return null;
|
|
206
|
+
const granted = await db
|
|
207
|
+
.selectFrom("scheduled_task_grants as g")
|
|
208
|
+
.innerJoin("scheduled_tasks as st", "st.id", "g.task_id")
|
|
209
|
+
.innerJoin("devices as d", (j) => j.onRef("d.org_id", "=", "st.org_id").onRef("d.owner_account_id", "=", "st.account_id"))
|
|
210
|
+
.select("g.task_id")
|
|
211
|
+
.where("g.task_id", "=", run.taskId)
|
|
212
|
+
.where("g.tool_key", "=", TIER_OPEN_GRANT_KEY)
|
|
213
|
+
.where("st.deleted_at", "is", null)
|
|
214
|
+
.where("d.id", "=", deviceId)
|
|
215
|
+
.where("d.deleted_at", "is", null)
|
|
216
|
+
.executeTakeFirst();
|
|
217
|
+
return { taskRunId: run.taskRunId, taskId: run.taskId, openGranted: granted !== undefined };
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The tool error of a scheduled run on a device whose tier is open (cloud) without the task's
|
|
221
|
+
* `tier:open` grant (spec §11.4).
|
|
222
|
+
*/
|
|
223
|
+
export const UNATTENDED_OPEN_TIER_ERROR = "This device's sandbox level is open, which a scheduled run may use only when its task allows the open sandbox level; the task's owner can add tier:open to the task's approved tools, or lower the device's sandbox level.";
|
|
224
|
+
/**
|
|
225
|
+
* The tool error of a scheduled run on a device that runs commands without a sandbox, without
|
|
226
|
+
* the task's `tier:open` grant (spec §11.4).
|
|
227
|
+
*/
|
|
228
|
+
export const UNATTENDED_UNSANDBOXED_ERROR = "This device runs commands without a sandbox, which a scheduled run may use only when its task allows the open sandbox level; the task's owner can add tier:open to the task's approved tools, or the device's owner can turn the sandbox back on with `capybara-device policy set isolation sandbox`.";
|
|
@@ -7,14 +7,18 @@ export const PROBE_CODE = "capybara-setup-probe";
|
|
|
7
7
|
const PROBE_VERIFIER = "capybara-setup-probe-verifier-0000000000000";
|
|
8
8
|
/** The most of a token endpoint answer the probe reads (an error body is a few hundred bytes). */
|
|
9
9
|
const PROBE_MAX_BYTES = 64 * 1024;
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Codes that say the client authenticated and only the code was bad, as intended (RFC 6749,
|
|
12
|
+
* Slack's `invalid_code`, GitHub's `bad_verification_code`).
|
|
13
|
+
*/
|
|
14
|
+
const ACCEPTED = new Set(["invalid_grant", "invalid_code", "bad_verification_code"]);
|
|
15
|
+
/** Codes that say the client id or secret is wrong (RFC 6749, Slack's and GitHub's). */
|
|
13
16
|
const CLIENT_REFUSED = new Set([
|
|
14
17
|
"invalid_client",
|
|
15
18
|
"unauthorized_client",
|
|
16
19
|
"invalid_client_id",
|
|
17
20
|
"bad_client_secret",
|
|
21
|
+
"incorrect_client_credentials",
|
|
18
22
|
]);
|
|
19
23
|
/** Codes that say the redirect URI is not registered (RFC 6749 servers and Slack's). */
|
|
20
24
|
const REDIRECT_REFUSED = new Set(["redirect_uri_mismatch", "bad_redirect_uri"]);
|
|
@@ -23,7 +27,7 @@ const text = (en, zh) => ({ en, zh });
|
|
|
23
27
|
export const PROBE_TEXTS = {
|
|
24
28
|
accepted: text("The token endpoint accepted the client.", "令牌端点接受了这个客户端。"),
|
|
25
29
|
clientRefused: text("The token endpoint refused the client id or secret.", "令牌端点拒绝了客户端 ID 或密钥。"),
|
|
26
|
-
clientFix: text("Copy the
|
|
30
|
+
clientFix: text("Copy the client ID and client secret again from the app's settings page.", "从应用的设置页面重新复制 Client ID 和 Client Secret。"),
|
|
27
31
|
redirectRefused: text("The app does not list the callback URL.", "应用的重定向地址里没有这个回调地址。"),
|
|
28
32
|
unknown: text("Could not verify the client; retry later.", "无法验证这个客户端,请稍后重试。"),
|
|
29
33
|
netDenied: text("The token endpoint's address is refused by this deployment's network rules; ask your super admin.", "这个部署的网络规则拒绝访问令牌端点的地址,请联系超级管理员。"),
|
|
@@ -71,11 +71,6 @@ export declare function shareSkillIn(db: KernelDb, v: {
|
|
|
71
71
|
agentId: string;
|
|
72
72
|
maintainerAccountId: string;
|
|
73
73
|
}): Promise<void>;
|
|
74
|
-
/** The body of a skill's SKILL.md (as run start would read it); null if unparsable. */
|
|
75
|
-
export declare function skillMdBody(files: readonly {
|
|
76
|
-
path: string;
|
|
77
|
-
data: Uint8Array;
|
|
78
|
-
}[]): string | null;
|
|
79
74
|
/**
|
|
80
75
|
* Takes a KEY SHARE lock on the org row before the agent row is locked: the order `AgentsService`
|
|
81
76
|
* uses (org row FOR UPDATE -> agent row). Without it, a transaction holding the agent row that
|
|
@@ -238,9 +233,10 @@ export declare class AgentSkillsService {
|
|
|
238
233
|
/** The transaction of {@link register}. */
|
|
239
234
|
private registerIn;
|
|
240
235
|
/**
|
|
241
|
-
* Spec §5.2: an agent's own skill may not take the name of an org skill the agent uses (
|
|
242
|
-
*
|
|
243
|
-
* registration is a version of
|
|
236
|
+
* Spec §5.2: an agent's own skill may not take the name of an org skill the agent uses (one it
|
|
237
|
+
* has on: every org skill its current definition does not turn off, skills default-on spec §3,
|
|
238
|
+
* and the ones it shared) unless the actor maintains it (then the registration is a version of
|
|
239
|
+
* it): 409 `skill.name_exists`.
|
|
244
240
|
*/
|
|
245
241
|
private assertNoUsedOrgSkillNamed;
|
|
246
242
|
/**
|
|
@@ -8,10 +8,10 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
|
-
import { parseSkillMd, SKILL_ENTRY_FILE } from "@capybara-hq/plugin-sdk";
|
|
12
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
13
12
|
import { sql } from "kysely";
|
|
14
13
|
import { visibleTo } from "../agents/agent-lookup.js";
|
|
14
|
+
import { normalizeStoredDefinition } from "../agents/definition.js";
|
|
15
15
|
import { offeredNamed } from "../catalog/component-state.js";
|
|
16
16
|
import { AttachmentsService, SKILL_PACKAGE_CONTENT_TYPE, skillFilesPrefix, } from "../conversations/attachments.service.js";
|
|
17
17
|
import { EventLog } from "../conversations/event-log.js";
|
|
@@ -29,6 +29,7 @@ import { invocationOf } from "./catalog.js";
|
|
|
29
29
|
import { appendConversationCatalog, cancelRequestIn, } from "./conversation-catalog.js";
|
|
30
30
|
import { readSkillPackage, skillManifest, } from "./package.js";
|
|
31
31
|
import { SkillsService } from "./skills.service.js";
|
|
32
|
+
import { agentSkills } from "./usable.js";
|
|
32
33
|
/**
|
|
33
34
|
* The cap on an agent's own skills (`scope = agent`). Far below the catalog snapshot's cap
|
|
34
35
|
* (`SKILLS_CATALOG_MAX`), so normal use never truncates the catalog. Requests never add agent
|
|
@@ -90,8 +91,8 @@ export async function shareSkillIn(db, v) {
|
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
92
93
|
* The catalog snapshot `base` with skill `name` replaced by the version just written or proposed
|
|
93
|
-
* (`source` = its prefix), or removed when that version is manual-only.
|
|
94
|
-
*
|
|
94
|
+
* (`source` = its prefix), or removed when that version is manual-only. Pinned skills of an old
|
|
95
|
+
* snapshot are not carried (skills default-on spec §8).
|
|
95
96
|
*/
|
|
96
97
|
function withVersion(base, v) {
|
|
97
98
|
return {
|
|
@@ -110,32 +111,8 @@ function withVersion(base, v) {
|
|
|
110
111
|
]
|
|
111
112
|
: []),
|
|
112
113
|
],
|
|
113
|
-
...(base.pinned && {
|
|
114
|
-
pinned: base.pinned.map((p) => p.id === v.skillId && v.pinnedBody !== null
|
|
115
|
-
? {
|
|
116
|
-
id: v.skillId,
|
|
117
|
-
version: v.version,
|
|
118
|
-
name: v.name,
|
|
119
|
-
source: v.source,
|
|
120
|
-
body: v.pinnedBody,
|
|
121
|
-
}
|
|
122
|
-
: p),
|
|
123
|
-
}),
|
|
124
114
|
};
|
|
125
115
|
}
|
|
126
|
-
/** The body of a skill's SKILL.md (as run start would read it); null if unparsable. */
|
|
127
|
-
export function skillMdBody(files) {
|
|
128
|
-
const md = files.find((f) => f.path === SKILL_ENTRY_FILE);
|
|
129
|
-
if (!md)
|
|
130
|
-
return null;
|
|
131
|
-
try {
|
|
132
|
-
const parsed = parseSkillMd(new TextDecoder("utf-8", { fatal: true }).decode(md.data));
|
|
133
|
-
return "errors" in parsed ? null : parsed.body;
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
116
|
/**
|
|
140
117
|
* Takes a KEY SHARE lock on the org row before the agent row is locked: the order `AgentsService`
|
|
141
118
|
* uses (org row FOR UPDATE -> agent row). Without it, a transaction holding the agent row that
|
|
@@ -539,7 +516,6 @@ let AgentSkillsService = class AgentSkillsService {
|
|
|
539
516
|
.executeTakeFirst();
|
|
540
517
|
if (!v)
|
|
541
518
|
return null;
|
|
542
|
-
const pinnedBody = skillMdBody(pkg.files);
|
|
543
519
|
return appendConversationCatalog(this.catalogDeps, conversationId, (base) => withVersion(base, {
|
|
544
520
|
skillId: out.skill.id,
|
|
545
521
|
name: pkg.name,
|
|
@@ -547,7 +523,6 @@ let AgentSkillsService = class AgentSkillsService {
|
|
|
547
523
|
version: out.version,
|
|
548
524
|
source: v.object_prefix,
|
|
549
525
|
forModel,
|
|
550
|
-
pinnedBody,
|
|
551
526
|
}), `skills_catalog:register:${v.object_prefix}`, actor.runId);
|
|
552
527
|
}
|
|
553
528
|
/**
|
|
@@ -644,7 +619,6 @@ let AgentSkillsService = class AgentSkillsService {
|
|
|
644
619
|
if (actor.conversationId !== undefined) {
|
|
645
620
|
// The conversation sees the new version at once: this skill replaced / added in its latest
|
|
646
621
|
// snapshot (source = this version's prefix)
|
|
647
|
-
const pinnedBody = skillMdBody(pkg.files);
|
|
648
622
|
toSeq = await appendConversationCatalog(this.catalogDeps, actor.conversationId, (base) => withVersion(base, {
|
|
649
623
|
skillId,
|
|
650
624
|
name: pkg.name,
|
|
@@ -652,7 +626,6 @@ let AgentSkillsService = class AgentSkillsService {
|
|
|
652
626
|
version,
|
|
653
627
|
source: prefix,
|
|
654
628
|
forModel,
|
|
655
|
-
pinnedBody,
|
|
656
629
|
}), `skills_catalog:register:${prefix}`, actor.runId);
|
|
657
630
|
}
|
|
658
631
|
return {
|
|
@@ -669,9 +642,10 @@ let AgentSkillsService = class AgentSkillsService {
|
|
|
669
642
|
};
|
|
670
643
|
}
|
|
671
644
|
/**
|
|
672
|
-
* Spec §5.2: an agent's own skill may not take the name of an org skill the agent uses (
|
|
673
|
-
*
|
|
674
|
-
* registration is a version of
|
|
645
|
+
* Spec §5.2: an agent's own skill may not take the name of an org skill the agent uses (one it
|
|
646
|
+
* has on: every org skill its current definition does not turn off, skills default-on spec §3,
|
|
647
|
+
* and the ones it shared) unless the actor maintains it (then the registration is a version of
|
|
648
|
+
* it): 409 `skill.name_exists`.
|
|
675
649
|
*/
|
|
676
650
|
async assertNoUsedOrgSkillNamed(actor, name) {
|
|
677
651
|
const def = await this.db
|
|
@@ -682,23 +656,16 @@ let AgentSkillsService = class AgentSkillsService {
|
|
|
682
656
|
.select("agent_versions.definition")
|
|
683
657
|
.where("agents.id", "=", actor.agentId)
|
|
684
658
|
.executeTakeFirst();
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
.
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
.
|
|
695
|
-
.where("name", "=", name)
|
|
696
|
-
.where("deleted_at", "is", null)
|
|
697
|
-
.where((eb) => eb.or([
|
|
698
|
-
eb("origin_agent_id", "=", actor.agentId),
|
|
699
|
-
...(ids.length > 0 ? [eb("id", "in", ids)] : []),
|
|
700
|
-
]))
|
|
701
|
-
.executeTakeFirst();
|
|
659
|
+
const disabledSkills = def === undefined ? [] : (normalizeStoredDefinition(def.definition).disabledSkills ?? []);
|
|
660
|
+
const used = (await agentSkills(this.db.db, {
|
|
661
|
+
orgId: actor.orgId,
|
|
662
|
+
agentId: actor.agentId,
|
|
663
|
+
disabledSkills,
|
|
664
|
+
})).find((r) => r.scope === "org" &&
|
|
665
|
+
r.name === name &&
|
|
666
|
+
// an org skill, or the org's copy linked to a platform skill (listed under the copy's id);
|
|
667
|
+
// a platform skill itself is not the org's to clash with
|
|
668
|
+
(r.origin === "org" || (r.platformSkillId !== undefined && r.id !== r.platformSkillId)));
|
|
702
669
|
if (used) {
|
|
703
670
|
throw new ApiError("skill.name_exists", `This agent uses the org skill "${name}", which only its maintainer can update; register the package under another name, or ask the skill's maintainer.`);
|
|
704
671
|
}
|
|
@@ -780,7 +747,6 @@ let AgentSkillsService = class AgentSkillsService {
|
|
|
780
747
|
const forModel = invocationOf(manifest).model;
|
|
781
748
|
let toSeq = null;
|
|
782
749
|
if (actor.conversationId !== undefined) {
|
|
783
|
-
const pinnedBody = skillMdBody(pkg.files);
|
|
784
750
|
toSeq = await appendConversationCatalog(this.catalogDeps, actor.conversationId, (base) => withVersion(base, {
|
|
785
751
|
skillId: shared.id,
|
|
786
752
|
name: pkg.name,
|
|
@@ -788,7 +754,6 @@ let AgentSkillsService = class AgentSkillsService {
|
|
|
788
754
|
version,
|
|
789
755
|
source: prefix,
|
|
790
756
|
forModel,
|
|
791
|
-
pinnedBody,
|
|
792
757
|
}), `skills_catalog:propose:${prefix}`, actor.runId);
|
|
793
758
|
}
|
|
794
759
|
return {
|
package/dist/skills/catalog.d.ts
CHANGED
|
@@ -1,33 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Logger, SkillInvocation } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { AgentDefinition } from "../agents/definition.js";
|
|
3
3
|
import { EventLog } from "../conversations/event-log.js";
|
|
4
4
|
import { type ConversationEvent } from "../conversations/events.js";
|
|
5
5
|
import type { KernelSchemaDb, SkillManifest, SourceFormat } from "../db/kernel-schema.js";
|
|
6
|
-
import {
|
|
6
|
+
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
7
7
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
8
8
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
9
9
|
import type { KernelLogger } from "../log.js";
|
|
10
|
-
import { type CatalogSkill, type
|
|
11
|
-
import { type
|
|
10
|
+
import { type CatalogSkill, type SkillsSnapshot } from "../runtime/context/skills.js";
|
|
11
|
+
import { type SkillVersionRow, type UsableSkill } from "./usable.js";
|
|
12
12
|
/**
|
|
13
13
|
* 技能目录快照(M5 T14,Ruling M17):运行开始时解析「此刻对这个对话生效的技能」,与日志里最新一份快照不同才写
|
|
14
14
|
* `skills_catalog`。模型看到的目录与 `Skill` 的名字解析只来自日志,不读实时的库。
|
|
15
15
|
*/
|
|
16
|
-
/**
|
|
17
|
-
* Pinned skill limits (spec §9.1): at most `count` pinned skills per agent, whose current SKILL.md
|
|
18
|
-
* sizes total at most `totalBytes`. Saving checks both; at run time the pinned bodies are kept in
|
|
19
|
-
* the agent's order while they fit.
|
|
20
|
-
*/
|
|
21
|
-
export declare const PINNED_SKILLS_LIMITS: {
|
|
22
|
-
readonly count: 3;
|
|
23
|
-
readonly totalBytes: 32768;
|
|
24
|
-
};
|
|
25
16
|
/** {@link resolveEffectiveSkills} 的输入。 */
|
|
26
17
|
export interface EffectiveSkillsInput {
|
|
27
18
|
orgId: string;
|
|
28
19
|
agentId: string;
|
|
29
|
-
/**
|
|
30
|
-
agent: Pick<AgentDefinition, "
|
|
20
|
+
/** The definition of the run's agent version (`disabledSkills`: what it turns off). */
|
|
21
|
+
agent: Pick<AgentDefinition, "disabledSkills">;
|
|
31
22
|
/**
|
|
32
23
|
* The conversation id: a pending skill change of the agent (`skill_change_requests`, a version
|
|
33
24
|
* draft) applies only in the conversation that proposed it (a trial) and is overlaid here.
|
|
@@ -35,12 +26,15 @@ export interface EffectiveSkillsInput {
|
|
|
35
26
|
conversationId: string;
|
|
36
27
|
}
|
|
37
28
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* -
|
|
41
|
-
* -
|
|
42
|
-
*
|
|
43
|
-
*
|
|
29
|
+
* The skills in effect for this conversation now, sorted by name (code point order), as
|
|
30
|
+
* {@link agentSkills} resolves them (skills default-on spec §3):
|
|
31
|
+
* - the agent's own skills (`scope='agent'`) and the org skills it shared;
|
|
32
|
+
* - plus every skill on in the org, minus what the run's agent version's `disabledSkills` turns
|
|
33
|
+
* off (never a builtin plugin's skill);
|
|
34
|
+
* - deleted skills are skipped;
|
|
35
|
+
* - the version is `current_version`, the description the one in that version's manifest.
|
|
36
|
+
* When an org skill and the agent's own skill share a name only the agent's own is kept (more
|
|
37
|
+
* specific; neither `/skills/<name>/` nor loading by name can have two).
|
|
44
38
|
*
|
|
45
39
|
* Trials: a pending version proposal of this agent made in this conversation replaces, by name,
|
|
46
40
|
* the version of its skill (the id of the existing skill, the version it would become on
|
|
@@ -58,6 +52,13 @@ export interface ResolvedSkill {
|
|
|
58
52
|
format: SourceFormat;
|
|
59
53
|
/** Its version's file manifest (a trial draft's proposed one). */
|
|
60
54
|
manifest: SkillManifest;
|
|
55
|
+
/**
|
|
56
|
+
* Where it comes from, for the catalog budget's ranking: `scope` `agent` is the agent's own
|
|
57
|
+
* skill, `origin` `platform` a platform skill (or an org copy linked to one). A trial draft takes
|
|
58
|
+
* the entry it replaces (an agent's own skill when it replaces none).
|
|
59
|
+
*/
|
|
60
|
+
scope: SkillVersionRow["scope"];
|
|
61
|
+
origin: UsableSkill["origin"];
|
|
61
62
|
}
|
|
62
63
|
/**
|
|
63
64
|
* Every effective skill with its format and manifest, sorted by name, manual-only versions
|
|
@@ -67,43 +68,42 @@ export interface ResolvedSkill {
|
|
|
67
68
|
export declare function resolveSkills(db: KernelSchemaDb, input: Omit<EffectiveSkillsInput, "conversationId"> & {
|
|
68
69
|
conversationId?: string;
|
|
69
70
|
}, log?: Pick<Logger, "warn">): Promise<ResolvedSkill[]>;
|
|
70
|
-
/** A pinned skill to read: an effective skill the agent version pins. */
|
|
71
|
-
interface PinnedCandidate {
|
|
72
|
-
id: string;
|
|
73
|
-
version: number;
|
|
74
|
-
name: string;
|
|
75
|
-
source: string;
|
|
76
|
-
manifest: SkillManifest;
|
|
77
|
-
}
|
|
78
71
|
/**
|
|
79
|
-
* The
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* else is a transient storage failure.
|
|
72
|
+
* The rank of a skill in the catalog budget (skills default-on spec §7): 0 the agent's own skills
|
|
73
|
+
* (and the org skills it shared), 1 builtin skills, 2 the org's own skills (no plugin), 3 plugin
|
|
74
|
+
* skills.
|
|
83
75
|
*/
|
|
84
|
-
export
|
|
76
|
+
export type BudgetTier = 0 | 1 | 2 | 3;
|
|
85
77
|
/**
|
|
86
|
-
*
|
|
87
|
-
* `
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* - a read error is retried once ({@link PINNED_READ_ATTEMPTS}), then is transient (the user
|
|
91
|
-
* sends again);
|
|
92
|
-
* - a manifest without SKILL.md, a missing object, an integrity mismatch, non-UTF-8 or an
|
|
93
|
-
* unparsable file is permanent: logged at `error` with the skill id and source, and the error
|
|
94
|
-
* carries `details: [{ reason: "pinned_unreadable", skillId, name, source }]`
|
|
95
|
-
* ({@link pinnedFailureText} turns it into an owner-facing message).
|
|
78
|
+
* The {@link BudgetTier} of a resolved skill: the agent's own (`scope` `agent`, or an org skill it
|
|
79
|
+
* shared, `known.shared`: always on, never turned off, spec §3), else builtin (a platform skill of
|
|
80
|
+
* a builtin plugin, `known.plugins`, or one of `known.ids`, as `builtinSkillIds` gives them), else
|
|
81
|
+
* the org's own (no plugin), else a plugin skill.
|
|
96
82
|
*/
|
|
97
|
-
export declare function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
83
|
+
export declare function budgetTier(r: Pick<ResolvedSkill, "skill" | "scope" | "origin">, known: {
|
|
84
|
+
plugins: ReadonlySet<string>;
|
|
85
|
+
ids: ReadonlySet<string>;
|
|
86
|
+
shared: ReadonlySet<string>;
|
|
87
|
+
}): BudgetTier;
|
|
88
|
+
/** The characters a described entry takes in the listing: its line and the line break. */
|
|
89
|
+
export declare const catalogLineChars: (s: Pick<CatalogSkill, "name" | "description">) => number;
|
|
90
|
+
/** What {@link applyCatalogBudget} decided. */
|
|
91
|
+
export interface CatalogBudgetResult {
|
|
92
|
+
/** The entries in their input order, those past the budget with `nameOnly: true`. */
|
|
93
|
+
skills: CatalogSkill[];
|
|
94
|
+
/** The characters every entry's described line would take ({@link catalogLineChars}). */
|
|
95
|
+
characters: number;
|
|
96
|
+
/** How many entries are name-only (0: everything fits). */
|
|
97
|
+
nameOnly: number;
|
|
98
|
+
}
|
|
101
99
|
/**
|
|
102
|
-
* The
|
|
103
|
-
*
|
|
104
|
-
*
|
|
100
|
+
* The catalog budget (skills default-on spec §7): the entries ranked by `tierOf` (then by name, in
|
|
101
|
+
* code point order) keep their descriptions while the running total of their rendered lines
|
|
102
|
+
* ({@link catalogLineChars}) stays within `budget`; the first one past it and every one after it
|
|
103
|
+
* are name-only. Pure and deterministic; `tierOf` is only called when the whole listing does not
|
|
104
|
+
* fit. Entries keep their input order; any `nameOnly` they carry is replaced.
|
|
105
105
|
*/
|
|
106
|
-
export declare function
|
|
106
|
+
export declare function applyCatalogBudget(skills: readonly CatalogSkill[], budget: number, tierOf: () => Promise<(s: CatalogSkill) => BudgetTier>): Promise<CatalogBudgetResult>;
|
|
107
107
|
/** `skills_catalog` 的幂等键:每次运行最多写一份。 */
|
|
108
108
|
/**
|
|
109
109
|
* 是否给模型 `Skill`:日志里有过技能目录快照(Ruling M17),**即使最新一份是空的**。构建请求与执行工具调用
|
|
@@ -136,20 +136,24 @@ export declare function lockLatestCatalog(db: KernelSchemaDb, conversationId: st
|
|
|
136
136
|
* 解析、比较、追加在一个事务里、先锁会话行:否则在解析与追加之间提交的拒绝 + 回退快照会被过时的草稿条目盖回去。
|
|
137
137
|
* 解析只读 skills / agents / 申请(不加锁),不会与别的路径形成死锁。
|
|
138
138
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
139
|
+
* The snapshot also carries each skill's `plugin` and the Claude Code tool-name map (`toolMap`,
|
|
140
|
+
* iff an effective skill has source format `claude-code`). It never carries `pinned` any more
|
|
141
|
+
* (skills default-on spec §8): a conversation whose latest snapshot has pins gets one without them,
|
|
142
|
+
* and the request tells the model they were removed. Run start reads no object storage.
|
|
143
|
+
*
|
|
144
|
+
* The catalog budget (`skills.catalogBudgetChars`, spec §7) is applied here and recorded in the
|
|
145
|
+
* snapshot: entries past it carry `nameOnly: true` ({@link applyCatalogBudget}); the cut is logged
|
|
146
|
+
* as `skills.catalog_over_budget`. A changed budget writes a new snapshot at the next run start
|
|
147
|
+
* only when an entry crosses it. Snapshots appended elsewhere (a skill registration, a trial's
|
|
148
|
+
* revert) carry the entries forward as they are; the next run start applies the budget again.
|
|
145
149
|
*/
|
|
146
150
|
export declare class SkillsCatalogService {
|
|
147
151
|
private readonly db;
|
|
148
|
-
private readonly storage;
|
|
149
152
|
private readonly eventLog;
|
|
150
153
|
private readonly log;
|
|
151
154
|
readonly txHost: TransactionHost;
|
|
152
|
-
|
|
155
|
+
private readonly config;
|
|
156
|
+
constructor(db: KernelDb, eventLog: EventLog, log: KernelLogger, txHost: TransactionHost, config: KernelConfigService);
|
|
153
157
|
/**
|
|
154
158
|
* Records the run start's snapshot (see the class comment). `opts.log`: the run's logger
|
|
155
159
|
* (default the kernel logger).
|
|
@@ -159,7 +163,6 @@ export declare class SkillsCatalogService {
|
|
|
159
163
|
}, opts?: {
|
|
160
164
|
log?: Pick<Logger, "warn" | "error">;
|
|
161
165
|
}): Promise<void>;
|
|
162
|
-
/**
|
|
166
|
+
/** Under the conversation row lock, resolves the skills and appends a changed snapshot. */
|
|
163
167
|
private append;
|
|
164
168
|
}
|
|
165
|
-
export {};
|