@capybara-hq/kernel 0.1.0-rc.2 → 0.1.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/admin.controller.d.ts +9 -4
- package/dist/admin/admin.controller.js +13 -4
- package/dist/admin/extensions.controller.d.ts +8 -1
- package/dist/admin/extensions.controller.js +22 -2
- package/dist/agents/agent-lookup.d.ts +3 -0
- package/dist/agents/agent-lookup.js +20 -2
- package/dist/agents/agent-profile.service.d.ts +29 -0
- package/dist/agents/agent-profile.service.js +117 -0
- package/dist/agents/agents.controller.d.ts +93 -3
- package/dist/agents/agents.controller.js +114 -4
- package/dist/agents/agents.module.js +6 -2
- package/dist/agents/agents.service.d.ts +13 -5
- package/dist/agents/agents.service.js +49 -13
- package/dist/agents/definition.d.ts +35 -8
- package/dist/agents/definition.js +203 -17
- package/dist/agents/instruction-files.d.ts +60 -0
- package/dist/agents/instruction-files.js +111 -0
- package/dist/agents/models.controller.d.ts +9 -3
- package/dist/agents/models.controller.js +19 -4
- package/dist/agents/profile.d.ts +96 -0
- package/dist/agents/profile.js +51 -0
- package/dist/agents/readiness.controller.d.ts +26 -0
- package/dist/agents/readiness.controller.js +56 -0
- package/dist/agents/templates.js +20 -5
- package/dist/background/background.controller.d.ts +38 -0
- package/dist/background/background.controller.js +87 -0
- package/dist/background/background.module.d.ts +10 -0
- package/dist/background/background.module.js +32 -0
- package/dist/background/finish.d.ts +43 -0
- package/dist/background/finish.js +94 -0
- package/dist/background/launch-lock.d.ts +14 -0
- package/dist/background/launch-lock.js +39 -0
- package/dist/background/launch.d.ts +36 -0
- package/dist/background/launch.js +70 -0
- package/dist/background/poll.job.d.ts +53 -0
- package/dist/background/poll.job.js +182 -0
- package/dist/background/render.d.ts +23 -0
- package/dist/background/render.js +48 -0
- package/dist/background/session.d.ts +37 -0
- package/dist/background/session.js +66 -0
- package/dist/background/status.d.ts +35 -0
- package/dist/background/status.js +57 -0
- package/dist/background/stop.d.ts +62 -0
- package/dist/background/stop.js +225 -0
- package/dist/background/store.d.ts +150 -0
- package/dist/background/store.js +351 -0
- package/dist/background/store.module.d.ts +6 -0
- package/dist/background/store.module.js +22 -0
- package/dist/background/tasks.service.d.ts +67 -0
- package/dist/background/tasks.service.js +186 -0
- package/dist/background/wake-rules.d.ts +57 -0
- package/dist/background/wake-rules.js +83 -0
- package/dist/background/wake.d.ts +28 -0
- package/dist/background/wake.js +57 -0
- package/dist/catalog/catalog-content.js +4 -1
- package/dist/catalog/claude/agents.js +15 -10
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/plugin-import.js +1 -1
- package/dist/catalog/plugin-upgrade.js +1 -1
- package/dist/channels/cards.d.ts +8 -2
- package/dist/channels/cards.js +17 -9
- package/dist/channels/connections.service.d.ts +16 -1
- package/dist/channels/connections.service.js +38 -4
- package/dist/channels/threads.d.ts +5 -0
- package/dist/channels/threads.js +8 -0
- package/dist/config/schema.d.ts +18 -0
- package/dist/config/schema.js +104 -2
- package/dist/conversations/access.js +2 -0
- package/dist/conversations/artifacts.d.ts +1 -1
- package/dist/conversations/artifacts.js +1 -1
- package/dist/conversations/attachments.service.d.ts +30 -0
- package/dist/conversations/attachments.service.js +130 -5
- package/dist/conversations/conversations.controller.d.ts +36 -5
- package/dist/conversations/conversations.controller.js +62 -12
- package/dist/conversations/conversations.module.js +3 -0
- package/dist/conversations/conversations.service.d.ts +44 -2
- package/dist/conversations/conversations.service.js +140 -12
- package/dist/conversations/event-log.d.ts +12 -1
- package/dist/conversations/event-log.js +29 -1
- package/dist/conversations/events.d.ts +81 -3
- package/dist/conversations/events.js +83 -16
- package/dist/conversations/queue.d.ts +3 -1
- package/dist/conversations/queue.js +13 -2
- package/dist/conversations/queue.service.js +3 -1
- package/dist/conversations/titles.d.ts +7 -0
- package/dist/conversations/titles.js +15 -0
- package/dist/db/kernel-schema.d.ts +60 -4
- package/dist/db/pool.d.ts +5 -0
- package/dist/db/pool.js +3 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +8 -0
- package/dist/evals/recorded-tools.d.ts +27 -1
- package/dist/evals/recorded-tools.js +85 -10
- package/dist/evals/run-replay.d.ts +10 -2
- package/dist/evals/run-replay.js +49 -12
- package/dist/evals/step-replay.js +12 -6
- package/dist/evals/trajectory.d.ts +2 -2
- package/dist/evals/trajectory.js +1 -1
- package/dist/extensions/admin-ops.js +2 -0
- package/dist/extensions/admin-view.d.ts +3 -1
- package/dist/extensions/admin-view.js +10 -2
- package/dist/extensions/blobs.js +1 -0
- package/dist/extensions/config-issues.d.ts +13 -1
- package/dist/extensions/config-issues.js +22 -5
- package/dist/extensions/config-json-schema.d.ts +43 -7
- package/dist/extensions/config-json-schema.js +124 -58
- package/dist/extensions/deployment-config-store.d.ts +6 -0
- package/dist/extensions/deployment-config.d.ts +50 -3
- package/dist/extensions/deployment-config.js +125 -28
- package/dist/extensions/display-name.d.ts +22 -0
- package/dist/extensions/display-name.js +77 -0
- package/dist/extensions/extensions.module.js +6 -0
- package/dist/extensions/host.d.ts +26 -1
- package/dist/extensions/host.js +82 -9
- package/dist/extensions/org-config.controller.d.ts +11 -3
- package/dist/extensions/org-config.controller.js +14 -6
- package/dist/extensions/org-config.d.ts +6 -0
- package/dist/extensions/org-config.js +12 -13
- package/dist/extensions/package.d.ts +14 -1
- package/dist/extensions/package.js +51 -2
- package/dist/extensions/permission-view.d.ts +3 -0
- package/dist/extensions/point-defaults.controller.d.ts +34 -0
- package/dist/extensions/point-defaults.controller.js +71 -0
- package/dist/extensions/point-defaults.d.ts +64 -0
- package/dist/extensions/point-defaults.js +206 -0
- package/dist/extensions/registry.d.ts +1 -1
- package/dist/extensions/registry.js +2 -0
- package/dist/http/api.module.js +8 -0
- package/dist/http/nest-logger.d.ts +6 -5
- package/dist/http/nest-logger.js +24 -10
- package/dist/identity/auth.controller.js +9 -5
- package/dist/identity/cookies.d.ts +20 -5
- package/dist/identity/cookies.js +44 -6
- package/dist/identity/me.controller.d.ts +5 -1
- package/dist/identity/me.controller.js +12 -3
- package/dist/identity/sessions.d.ts +12 -0
- package/dist/identity/sessions.js +45 -0
- package/dist/identity/settings.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/infra/config/kernel-config.service.js +6 -0
- package/dist/infra/database/database.module.js +1 -0
- package/dist/infra/events/kernel-events.d.ts +8 -0
- package/dist/infra/lifecycle/process-guards.js +4 -0
- package/dist/infra/logging/logging.module.d.ts +4 -2
- package/dist/infra/logging/logging.module.js +24 -6
- package/dist/infra/queue/redis-connection.d.ts +6 -0
- package/dist/infra/queue/redis-connection.js +7 -0
- package/dist/infra/redis/redis.module.js +6 -2
- package/dist/infra/tokens.d.ts +6 -1
- package/dist/infra/tokens.js +6 -1
- package/dist/lifecycle/kernel-lifecycle.js +5 -0
- package/dist/log.d.ts +13 -1
- package/dist/log.js +93 -17
- package/dist/models/purposes.d.ts +23 -0
- package/dist/models/purposes.js +31 -10
- package/dist/orgs/offboarding.d.ts +3 -1
- package/dist/orgs/offboarding.js +26 -16
- package/dist/orgs/settings.service.d.ts +7 -2
- package/dist/orgs/settings.service.js +15 -4
- package/dist/roles/worker.module.js +6 -0
- package/dist/runtime/context/build.d.ts +15 -13
- package/dist/runtime/context/build.js +45 -27
- package/dist/runtime/context/claude-tools.d.ts +3 -1
- package/dist/runtime/context/claude-tools.js +12 -12
- package/dist/runtime/context/compaction.js +4 -0
- package/dist/runtime/context/legacy-text.d.ts +22 -0
- package/dist/runtime/context/legacy-text.js +37 -0
- package/dist/runtime/context/skills.d.ts +29 -11
- package/dist/runtime/context/skills.js +37 -30
- package/dist/runtime/langgraph-loop.js +3 -2
- package/dist/runtime/loop.d.ts +5 -4
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +17 -5
- package/dist/runtime/model-call.d.ts +17 -1
- package/dist/runtime/model-call.js +4 -3
- package/dist/runtime/providers.d.ts +6 -0
- package/dist/runtime/providers.js +8 -0
- package/dist/runtime/run-finish.d.ts +1 -1
- package/dist/runtime/run-queue.d.ts +28 -5
- package/dist/runtime/run-queue.js +131 -35
- package/dist/runtime/run-worker.d.ts +2 -2
- package/dist/runtime/run-worker.js +20 -7
- package/dist/runtime/runtime.module.js +8 -3
- package/dist/runtime/step-log.d.ts +6 -0
- package/dist/runtime/step-log.js +16 -2
- package/dist/runtime/subagents/definition.d.ts +1 -6
- package/dist/runtime/subagents/definition.js +14 -13
- package/dist/runtime/subagents/dispatch.d.ts +2 -2
- package/dist/runtime/subagents/dispatch.js +2 -2
- package/dist/runtime/subagents/org-agents.js +2 -1
- package/dist/runtime/subagents/roster.d.ts +1 -1
- package/dist/runtime/subagents/roster.js +1 -1
- package/dist/runtime/subagents/skill-fork.d.ts +2 -2
- package/dist/runtime/subagents/skill-fork.js +2 -2
- package/dist/runtime/subagents/subagents.service.d.ts +3 -3
- package/dist/runtime/subagents/subagents.service.js +3 -3
- package/dist/runtime/usage-report.d.ts +2 -1
- package/dist/runtime/usage-report.js +2 -1
- package/dist/runtime/waits.d.ts +13 -7
- package/dist/runtime/waits.js +42 -16
- package/dist/sandbox/sandbox.module.js +6 -1
- package/dist/sandbox/sandbox.service.d.ts +61 -3
- package/dist/sandbox/sandbox.service.js +141 -7
- package/dist/skills/catalog.d.ts +4 -4
- package/dist/skills/catalog.js +4 -4
- package/dist/skills/changes.service.d.ts +2 -2
- package/dist/skills/changes.service.js +5 -4
- package/dist/skills/invocation-keys.d.ts +1 -1
- package/dist/skills/invocation-keys.js +1 -1
- package/dist/skills/invoke.d.ts +1 -1
- package/dist/skills/invoke.js +5 -4
- package/dist/tools/approvals.service.d.ts +6 -0
- package/dist/tools/approvals.service.js +21 -1
- package/dist/tools/builtin-names.d.ts +91 -0
- package/dist/tools/builtin-names.js +143 -0
- package/dist/tools/builtins/agent.d.ts +7 -7
- package/dist/tools/builtins/agent.js +7 -11
- package/dist/tools/builtins/ask-user.d.ts +1 -1
- package/dist/tools/builtins/ask-user.js +2 -2
- package/dist/tools/builtins/builtin-tools.d.ts +17 -3
- package/dist/tools/builtins/builtin-tools.js +36 -4
- package/dist/tools/builtins/read-tool-output.d.ts +1 -1
- package/dist/tools/builtins/read-tool-output.js +2 -2
- package/dist/tools/builtins/sandbox/bash.d.ts +4 -4
- package/dist/tools/builtins/sandbox/bash.js +259 -29
- package/dist/tools/builtins/sandbox/common.d.ts +30 -7
- package/dist/tools/builtins/sandbox/common.js +9 -5
- package/dist/tools/builtins/sandbox/deliver.d.ts +1 -1
- package/dist/tools/builtins/sandbox/deliver.js +12 -12
- package/dist/tools/builtins/sandbox/edit.d.ts +1 -1
- package/dist/tools/builtins/sandbox/edit.js +41 -30
- package/dist/tools/builtins/sandbox/file-state.d.ts +34 -0
- package/dist/tools/builtins/sandbox/file-state.js +34 -0
- package/dist/tools/builtins/sandbox/glob-match.d.ts +1 -1
- package/dist/tools/builtins/sandbox/glob-match.js +1 -1
- package/dist/tools/builtins/sandbox/glob.d.ts +1 -1
- package/dist/tools/builtins/sandbox/glob.js +4 -4
- package/dist/tools/builtins/sandbox/grep.d.ts +7 -4
- package/dist/tools/builtins/sandbox/grep.js +147 -30
- package/dist/tools/builtins/sandbox/index.d.ts +3 -1
- package/dist/tools/builtins/sandbox/index.js +7 -1
- package/dist/tools/builtins/sandbox/read.d.ts +2 -2
- package/dist/tools/builtins/sandbox/read.js +21 -14
- package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
- package/dist/tools/builtins/sandbox/task-output.js +304 -0
- package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
- package/dist/tools/builtins/sandbox/task-stop.js +112 -0
- package/dist/tools/builtins/sandbox/write.d.ts +1 -1
- package/dist/tools/builtins/sandbox/write.js +37 -33
- package/dist/tools/builtins/skill-load.d.ts +3 -3
- package/dist/tools/builtins/skill-load.js +32 -16
- package/dist/tools/builtins/skill-write.d.ts +4 -4
- package/dist/tools/builtins/skill-write.js +10 -10
- package/dist/tools/builtins/tool-search.d.ts +13 -9
- package/dist/tools/builtins/tool-search.js +25 -14
- package/dist/tools/builtins/web/errors.d.ts +8 -0
- package/dist/tools/builtins/web/errors.js +53 -0
- package/dist/tools/builtins/web/extract.d.ts +62 -0
- package/dist/tools/builtins/web/extract.js +267 -0
- package/dist/tools/builtins/web/fetch.d.ts +55 -0
- package/dist/tools/builtins/web/fetch.js +174 -0
- package/dist/tools/builtins/web/index.d.ts +13 -0
- package/dist/tools/builtins/web/index.js +12 -0
- package/dist/tools/builtins/web/page-cache.d.ts +25 -0
- package/dist/tools/builtins/web/page-cache.js +77 -0
- package/dist/tools/builtins/web/search.d.ts +25 -0
- package/dist/tools/builtins/web/search.js +209 -0
- package/dist/tools/builtins/web/url.d.ts +17 -0
- package/dist/tools/builtins/web/url.js +43 -0
- package/dist/tools/connectors.service.js +4 -4
- package/dist/tools/display-names.d.ts +9 -0
- package/dist/tools/display-names.js +24 -0
- package/dist/tools/drift.js +5 -5
- package/dist/tools/elicitation.js +1 -1
- package/dist/tools/info.d.ts +20 -4
- package/dist/tools/info.js +55 -32
- package/dist/tools/legacy-data.d.ts +34 -0
- package/dist/tools/legacy-data.js +76 -0
- package/dist/tools/legacy-keys.d.ts +51 -0
- package/dist/tools/legacy-keys.js +115 -0
- package/dist/tools/naming.d.ts +1 -5
- package/dist/tools/naming.js +1 -7
- package/dist/tools/oauth/flow-paths.d.ts +1 -1
- package/dist/tools/oauth/flow-paths.js +1 -1
- package/dist/tools/outputs.js +1 -1
- package/dist/tools/pipeline.d.ts +13 -7
- package/dist/tools/pipeline.js +112 -39
- package/dist/tools/policy.d.ts +1 -14
- package/dist/tools/policy.js +13 -19
- package/dist/tools/questions.controller.d.ts +1 -1
- package/dist/tools/questions.controller.js +1 -1
- package/dist/tools/questions.service.d.ts +1 -1
- package/dist/tools/registry.d.ts +38 -24
- package/dist/tools/registry.js +54 -47
- package/dist/tools/resources.d.ts +1 -1
- package/dist/tools/resources.js +1 -1
- package/dist/tools/tools.module.js +13 -1
- package/dist/tools/validate.js +1 -1
- package/package.json +3 -3
- package/src/migrations/0063_tool_keys_claude_code.sql +220 -0
- package/src/migrations/0064_account_last_org.sql +7 -0
- package/src/migrations/0065_conversation_archive.sql +8 -0
- package/src/migrations/0066_extension_display_name.sql +4 -0
- package/src/migrations/0067_agent_profiles.sql +20 -0
- package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
- package/src/migrations/0069_attachments_agent_idx.sql +3 -0
- package/src/migrations/0070_background_tasks.sql +37 -0
- package/src/migrations/0071_background_tasks_reserve.sql +6 -0
- package/src/migrations/0072_background_tasks_stopping.sql +5 -0
- package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
- package/dist/tools/builtins/sandbox/names.d.ts +0 -7
- package/dist/tools/builtins/sandbox/names.js +0 -20
|
@@ -11,6 +11,7 @@ import { randomUUID } from "node:crypto";
|
|
|
11
11
|
import { isSandboxGoneError, } from "@capybara-hq/plugin-sdk";
|
|
12
12
|
import { Inject, Injectable } from "@nestjs/common";
|
|
13
13
|
import { sql, } from "kysely";
|
|
14
|
+
import { BackgroundTaskStore } from "../background/store.js";
|
|
14
15
|
import { DEFAULT_MAX_COMMAND_SECONDS, DEFAULT_MAX_RUN_SECONDS, } from "../config/schema.js";
|
|
15
16
|
import { ApiError } from "../errors.js";
|
|
16
17
|
import { readSandboxLimitOverride } from "../identity/settings.js";
|
|
@@ -36,6 +37,10 @@ const SWEEP_BATCH = 200;
|
|
|
36
37
|
const SWEEP_CONCURRENCY = 4;
|
|
37
38
|
/** destroyFor 等锁的上限:持有者在跑命令时最多等这么久。 */
|
|
38
39
|
const DESTROY_FOR_WAIT_MS = 10 * 60_000;
|
|
40
|
+
/** The time limit of the exec that kills lost background tasks after a resume (ms). */
|
|
41
|
+
const PENDING_KILL_TIMEOUT_MS = 30_000;
|
|
42
|
+
/** A background task's session id as `echo $!` printed it; anything else is never put in a command. */
|
|
43
|
+
const SID_RE = /^[1-9][0-9]{0,9}$/;
|
|
39
44
|
/** 维护里一次提供方操作的 signal:超时或锁丢失时中止。 */
|
|
40
45
|
const opSignal = (lease) => AbortSignal.any([lease.signal, AbortSignal.timeout(SWEEP_OP_TIMEOUT_MS)]);
|
|
41
46
|
/**
|
|
@@ -180,17 +185,21 @@ let SandboxService = class SandboxService {
|
|
|
180
185
|
providers;
|
|
181
186
|
config;
|
|
182
187
|
log;
|
|
188
|
+
background;
|
|
183
189
|
constructor(db,
|
|
184
190
|
/** The record of the instance each holder last used (`capk:sandbox-seen:…`). */
|
|
185
191
|
redis, leases,
|
|
186
192
|
/** The `sandbox@1` provider (read at every call: providers are ready after the extension load). */
|
|
187
|
-
providers, config, log
|
|
193
|
+
providers, config, log,
|
|
194
|
+
/** Background tasks lost while their process may live on (killed after a resume). */
|
|
195
|
+
background) {
|
|
188
196
|
this.db = db;
|
|
189
197
|
this.redis = redis;
|
|
190
198
|
this.leases = leases;
|
|
191
199
|
this.providers = providers;
|
|
192
200
|
this.config = config;
|
|
193
201
|
this.log = log;
|
|
202
|
+
this.background = background;
|
|
194
203
|
}
|
|
195
204
|
/** Poll interval while waiting for a lease (the leases' own). */
|
|
196
205
|
get pollMs() {
|
|
@@ -219,6 +228,82 @@ let SandboxService = class SandboxService {
|
|
|
219
228
|
return null;
|
|
220
229
|
return { status: row.status, lastUsedAt: row.last_used_at };
|
|
221
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* The instance the conversation's sandbox records now (its `created_at`), while it has one that
|
|
233
|
+
* runs or is suspended; null otherwise. Read without a lease: a caller inside `withSandbox`
|
|
234
|
+
* reads the instance its own lease keeps.
|
|
235
|
+
*/
|
|
236
|
+
async currentInstance(conversationId) {
|
|
237
|
+
const row = await this.db
|
|
238
|
+
.selectFrom("sandboxes")
|
|
239
|
+
.select(["status", "handle", "created_at"])
|
|
240
|
+
.where("conversation_id", "=", conversationId)
|
|
241
|
+
.executeTakeFirst();
|
|
242
|
+
if (!row || row.handle === null)
|
|
243
|
+
return null;
|
|
244
|
+
if (row.status !== "running" && row.status !== "suspended")
|
|
245
|
+
return null;
|
|
246
|
+
return { createdAt: row.created_at };
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Runs `fn` on the conversation's sandbox only while it runs the instance created at
|
|
250
|
+
* `instance.createdAt` (background tasks, spec 2026-11-05-background-bash-design §4.3). It never
|
|
251
|
+
* creates, resumes or rebuilds a sandbox: a sandbox that is missing, suspended, being created or
|
|
252
|
+
* destroyed (or recorded with another provider) is `not_running`, one that records another
|
|
253
|
+
* instance is `other_instance`. Otherwise `fn` runs under a shared lease, as a `withSandbox`
|
|
254
|
+
* holder does (its tag on every exec, crashed holders' commands killed), and `last_used_at`
|
|
255
|
+
* moves forward when it ends. Waits for the lease up to `sandbox.lockWaitSeconds`
|
|
256
|
+
* (`sandbox.busy`); `signal` (optional) ends the wait with its abort reason. The signal passed
|
|
257
|
+
* to `fn` aborts with `signal` or when the lease is lost. Errors of `fn` (`SandboxGoneError`
|
|
258
|
+
* too) are thrown as they are.
|
|
259
|
+
*/
|
|
260
|
+
async ifRunning(conversationId, instance, fn, signal = new AbortController().signal) {
|
|
261
|
+
const p = this.require();
|
|
262
|
+
const conv = conversationId;
|
|
263
|
+
const cap = this.effectiveCap(p);
|
|
264
|
+
const deadline = Date.now() + this.config.get().sandbox.lockWaitSeconds * 1000;
|
|
265
|
+
const token = randomUUID();
|
|
266
|
+
for (;;) {
|
|
267
|
+
if (signal.aborted)
|
|
268
|
+
throw abortReason(signal);
|
|
269
|
+
const row = await this.db
|
|
270
|
+
.selectFrom("sandboxes")
|
|
271
|
+
.selectAll()
|
|
272
|
+
.where("conversation_id", "=", conv)
|
|
273
|
+
.executeTakeFirst();
|
|
274
|
+
if (!row)
|
|
275
|
+
return { kind: "not_running", status: "gone" };
|
|
276
|
+
if (row.created_at.getTime() !== instance.createdAt.getTime()) {
|
|
277
|
+
return { kind: "other_instance" };
|
|
278
|
+
}
|
|
279
|
+
if (!this.usable(row, p, null))
|
|
280
|
+
return { kind: "not_running", status: row.status };
|
|
281
|
+
// the instance this check saw: its handle plus `created_at` (never `generation`, which
|
|
282
|
+
// every lease bumps)
|
|
283
|
+
const want = { handle: row.handle, createdAt: row.created_at.getTime() };
|
|
284
|
+
if (!(await this.leases.legacyMarker(conv))) {
|
|
285
|
+
const lease = await this.leases.tryShared(conv, cap, token);
|
|
286
|
+
if (lease) {
|
|
287
|
+
const e = await this.asShared({ orgId: row.org_id, conversationId: conv }, p, lease, (r) => this.usable(r, p, null) && isInstance(r, want), signal);
|
|
288
|
+
if (e) {
|
|
289
|
+
try {
|
|
290
|
+
const impl = tagged(p.impl, lease.token, () => this.register(conv, lease.token));
|
|
291
|
+
const sig = AbortSignal.any([signal, lease.signal]);
|
|
292
|
+
return { kind: "ran", value: await fn(impl, e.handle, sig) };
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
await this.leave(conv, e);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// the row changed between the check and the lease: classify it again
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (Date.now() + this.pollMs > deadline)
|
|
303
|
+
throw sandboxBusy();
|
|
304
|
+
await sleep(this.pollMs + Math.floor(Math.random() * this.pollMs), signal);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
222
307
|
/**
|
|
223
308
|
* A short lease on one path of the conversation's sandbox around the kernel's own
|
|
224
309
|
* read-modify-write of that file (spec §10.4); waits up to 30 s, then throws
|
|
@@ -318,7 +403,7 @@ let SandboxService = class SandboxService {
|
|
|
318
403
|
if (intent)
|
|
319
404
|
await this.clearIntent(conv, token);
|
|
320
405
|
intent = false;
|
|
321
|
-
const e = await this.asShared(ctx, p, lease, gone, signal);
|
|
406
|
+
const e = await this.asShared(ctx, p, lease, (r) => this.usable(r, p, gone), signal);
|
|
322
407
|
if (e)
|
|
323
408
|
return e;
|
|
324
409
|
}
|
|
@@ -365,14 +450,14 @@ let SandboxService = class SandboxService {
|
|
|
365
450
|
!isInstance(row, gone));
|
|
366
451
|
}
|
|
367
452
|
/**
|
|
368
|
-
* Under a just-taken shared lease: confirms the row is still
|
|
369
|
-
* null), records the holder and kills crashed holders' commands.
|
|
453
|
+
* Under a just-taken shared lease: confirms the row is still `accept`ed (else releases and
|
|
454
|
+
* returns null), records the holder and kills crashed holders' commands.
|
|
370
455
|
*/
|
|
371
|
-
async asShared(ctx, p, lease,
|
|
456
|
+
async asShared(ctx, p, lease, accept, signal) {
|
|
372
457
|
const conv = ctx.conversationId;
|
|
373
458
|
try {
|
|
374
459
|
const row = await this.readRow(ctx);
|
|
375
|
-
if (!
|
|
460
|
+
if (!accept(row)) {
|
|
376
461
|
await lease.release();
|
|
377
462
|
return null;
|
|
378
463
|
}
|
|
@@ -429,6 +514,9 @@ let SandboxService = class SandboxService {
|
|
|
429
514
|
if (!s.info.fresh && (s.resumed || dead.legacyMarker || dead.tags.length > 0)) {
|
|
430
515
|
await this.killLeftovers(conv, s.handle, p, sig, s.resumed && !dead.legacyMarker && dead.tags.length === 0 ? "resume" : "takeover");
|
|
431
516
|
}
|
|
517
|
+
if (s.resumed || s.info.fresh) {
|
|
518
|
+
await this.killPendingTasks(conv, s.handle, s.createdAt, p, lease.token, sig);
|
|
519
|
+
}
|
|
432
520
|
const shared = await lease.downgrade();
|
|
433
521
|
await this.register(conv, shared.token);
|
|
434
522
|
return { lease: shared, handle: s.handle, createdAt: s.createdAt, info: s.info };
|
|
@@ -618,6 +706,51 @@ let SandboxService = class SandboxService {
|
|
|
618
706
|
}
|
|
619
707
|
await this.kill(conversationId, why, () => killAll.call(p.impl, handle, signal), signal);
|
|
620
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* Under the exclusive lease, right after a resume or a (re)build: kills the sessions of the
|
|
711
|
+
* background tasks marked lost while their process may live on (`kill_pending`), in one exec of
|
|
712
|
+
* `pkill -KILL -s <sid>`, then clears their flags. Tasks of another instance have no process
|
|
713
|
+
* here and are only cleared. A gone instance and an abort are thrown; another failure is warned
|
|
714
|
+
* and leaves the flags of this instance's tasks for the next resume.
|
|
715
|
+
*/
|
|
716
|
+
async killPendingTasks(conversationId, handle, createdAt, p, tag, signal) {
|
|
717
|
+
const rows = await this.background.pendingKills(conversationId);
|
|
718
|
+
if (rows.length === 0)
|
|
719
|
+
return;
|
|
720
|
+
const mine = rows.filter((r) => r.sandboxCreatedAt?.getTime() === createdAt && r.sid !== null && SID_RE.test(r.sid));
|
|
721
|
+
let cleared = rows;
|
|
722
|
+
if (mine.length > 0) {
|
|
723
|
+
// pkill exits 1 when the session has no process left: only a higher status is a failure
|
|
724
|
+
const command = `rc=0; for sid in ${mine.map((r) => r.sid).join(" ")}; do pkill -KILL -s "$sid"; c=$?; [ "$c" -le 1 ] || rc=$c; done; exit $rc`;
|
|
725
|
+
try {
|
|
726
|
+
const r = await p.impl.exec(handle, {
|
|
727
|
+
command,
|
|
728
|
+
timeoutMs: PENDING_KILL_TIMEOUT_MS,
|
|
729
|
+
maxOutputBytes: 4096,
|
|
730
|
+
signal,
|
|
731
|
+
tag,
|
|
732
|
+
});
|
|
733
|
+
if (r.exitCode !== 0) {
|
|
734
|
+
throw new Error(`pkill exited with ${r.exitCode === null ? "no status" : r.exitCode}: ${r.stderr.slice(0, 500)}`);
|
|
735
|
+
}
|
|
736
|
+
this.log.info("lost background tasks killed after a sandbox resume", {
|
|
737
|
+
conversationId,
|
|
738
|
+
tasks: mine.map((t) => t.id),
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
catch (err) {
|
|
742
|
+
if (signal.aborted || isSandboxGoneError(err))
|
|
743
|
+
throw err;
|
|
744
|
+
this.log.warn("cannot kill lost background tasks after a sandbox resume", {
|
|
745
|
+
conversationId,
|
|
746
|
+
err,
|
|
747
|
+
});
|
|
748
|
+
const kept = new Set(mine.map((r) => r.id));
|
|
749
|
+
cleared = rows.filter((r) => !kept.has(r.id));
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
await this.background.clearKill(cleared.map((r) => r.id));
|
|
753
|
+
}
|
|
621
754
|
require() {
|
|
622
755
|
const p = this.providers.current();
|
|
623
756
|
if (!p)
|
|
@@ -934,6 +1067,7 @@ SandboxService = __decorate([
|
|
|
934
1067
|
__param(2, Inject(SandboxLeases)),
|
|
935
1068
|
__param(3, Inject(SandboxProviders)),
|
|
936
1069
|
__param(4, Inject(KernelConfigService)),
|
|
937
|
-
__param(5, Inject(KERNEL_LOGGER))
|
|
1070
|
+
__param(5, Inject(KERNEL_LOGGER)),
|
|
1071
|
+
__param(6, Inject(BackgroundTaskStore))
|
|
938
1072
|
], SandboxService);
|
|
939
1073
|
export { SandboxService };
|
package/dist/skills/catalog.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { type CatalogSkill, type PinnedSkill, type SkillsSnapshot } from "../run
|
|
|
11
11
|
import { type PrefixedStorage, StorageService } from "../storage/storage.service.js";
|
|
12
12
|
/**
|
|
13
13
|
* 技能目录快照(M5 T14,Ruling M17):运行开始时解析「此刻对这个对话生效的技能」,与日志里最新一份快照不同才写
|
|
14
|
-
* `skills_catalog`。模型看到的目录与 `
|
|
14
|
+
* `skills_catalog`。模型看到的目录与 `Skill` 的名字解析只来自日志,不读实时的库。
|
|
15
15
|
*/
|
|
16
16
|
/**
|
|
17
17
|
* Pinned skill limits (spec §9.1): at most `count` pinned skills per agent, whose current SKILL.md
|
|
@@ -103,9 +103,9 @@ export declare function loadPinnedBodies(storage: PrefixedStorage, orgId: string
|
|
|
103
103
|
export declare function fitPinned(pinned: readonly PinnedSkill[], log: Pick<Logger, "warn"> | undefined, input: Partial<Pick<EffectiveSkillsInput, "orgId" | "agentId" | "conversationId">>): PinnedSkill[];
|
|
104
104
|
/** `skills_catalog` 的幂等键:每次运行最多写一份。 */
|
|
105
105
|
/**
|
|
106
|
-
* 是否给模型 `
|
|
106
|
+
* 是否给模型 `Skill`:日志里有过技能目录快照(Ruling M17),**即使最新一份是空的**。构建请求与执行工具调用
|
|
107
107
|
* 用同一判定。不因空快照收回这个工具:工具集合一变缓存前缀就失效;而且 agent 没有别的工具时会发出「没有工具
|
|
108
|
-
* 但历史里有 tool_use」的请求,Anthropic 会拒绝。空目录下调用
|
|
108
|
+
* 但历史里有 tool_use」的请求,Anthropic 会拒绝。空目录下调用 Skill 由 `NO_SKILLS_TEXT` 给出可执行的说明。
|
|
109
109
|
*/
|
|
110
110
|
export declare function skillLoadOffered(latest: Extract<ConversationEvent, {
|
|
111
111
|
type: "skills_catalog";
|
|
@@ -124,7 +124,7 @@ export declare const skillsCatalogIdemKey: (runId: string) => string;
|
|
|
124
124
|
/**
|
|
125
125
|
* 在调用方事务里锁会话行(与 `EventLog.append` 同一把锁)并读最新一份技能目录快照(没有为 undefined)。
|
|
126
126
|
* `db` is the caller's transaction handle (`KernelDb.db` inside `@Transactional`).
|
|
127
|
-
* 「读最新 → 推出下一份 → 追加」都在这把锁内,别的写快照的事务(运行开始、
|
|
127
|
+
* 「读最新 → 推出下一份 → 追加」都在这把锁内,别的写快照的事务(运行开始、SkillWrite、拒绝时的回退)插不进来。
|
|
128
128
|
*/
|
|
129
129
|
export declare function lockLatestCatalog(db: KernelSchemaDb, conversationId: string): Promise<SkillsSnapshot | undefined>;
|
|
130
130
|
/**
|
package/dist/skills/catalog.js
CHANGED
|
@@ -23,7 +23,7 @@ import { sameSnapshot, } from "../runtime/context/skills.js";
|
|
|
23
23
|
import { StorageService } from "../storage/storage.service.js";
|
|
24
24
|
/**
|
|
25
25
|
* 技能目录快照(M5 T14,Ruling M17):运行开始时解析「此刻对这个对话生效的技能」,与日志里最新一份快照不同才写
|
|
26
|
-
* `skills_catalog`。模型看到的目录与 `
|
|
26
|
+
* `skills_catalog`。模型看到的目录与 `Skill` 的名字解析只来自日志,不读实时的库。
|
|
27
27
|
*/
|
|
28
28
|
/**
|
|
29
29
|
* Pinned skill limits (spec §9.1): at most `count` pinned skills per agent, whose current SKILL.md
|
|
@@ -303,9 +303,9 @@ export function fitPinned(pinned, log, input) {
|
|
|
303
303
|
}
|
|
304
304
|
/** `skills_catalog` 的幂等键:每次运行最多写一份。 */
|
|
305
305
|
/**
|
|
306
|
-
* 是否给模型 `
|
|
306
|
+
* 是否给模型 `Skill`:日志里有过技能目录快照(Ruling M17),**即使最新一份是空的**。构建请求与执行工具调用
|
|
307
307
|
* 用同一判定。不因空快照收回这个工具:工具集合一变缓存前缀就失效;而且 agent 没有别的工具时会发出「没有工具
|
|
308
|
-
* 但历史里有 tool_use」的请求,Anthropic 会拒绝。空目录下调用
|
|
308
|
+
* 但历史里有 tool_use」的请求,Anthropic 会拒绝。空目录下调用 Skill 由 `NO_SKILLS_TEXT` 给出可执行的说明。
|
|
309
309
|
*/
|
|
310
310
|
export function skillLoadOffered(latest,
|
|
311
311
|
/**
|
|
@@ -336,7 +336,7 @@ export const skillsCatalogIdemKey = (runId) => `skills_catalog:${runId}`;
|
|
|
336
336
|
/**
|
|
337
337
|
* 在调用方事务里锁会话行(与 `EventLog.append` 同一把锁)并读最新一份技能目录快照(没有为 undefined)。
|
|
338
338
|
* `db` is the caller's transaction handle (`KernelDb.db` inside `@Transactional`).
|
|
339
|
-
* 「读最新 → 推出下一份 → 追加」都在这把锁内,别的写快照的事务(运行开始、
|
|
339
|
+
* 「读最新 → 推出下一份 → 追加」都在这把锁内,别的写快照的事务(运行开始、SkillWrite、拒绝时的回退)插不进来。
|
|
340
340
|
*/
|
|
341
341
|
export async function lockLatestCatalog(db, conversationId) {
|
|
342
342
|
await db
|
|
@@ -97,9 +97,9 @@ export interface CancelledDrafts {
|
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
99
|
* agent 自写技能与共享 agent 的技能变更审批(M5 T15,R4):
|
|
100
|
-
* - `write`(工具 `
|
|
100
|
+
* - `write`(工具 `SkillWrite` 调用):私有 agent 直接生成新版本;共享 agent 生成一条待负责人批准的申请,
|
|
101
101
|
* 期间只有发起对话试用(`resolveEffectiveSkills` 按对话合并);两种情况都在同一事务里往发起对话追加一份
|
|
102
|
-
* 技能目录快照(Ruling M17:日志是唯一事实源),同一运行的下一步即可 `
|
|
102
|
+
* 技能目录快照(Ruling M17:日志是唯一事实源),同一运行的下一步即可 `Skill`;
|
|
103
103
|
* - `decide`(HTTP,只有该 agent 当前的负责人):批准 → 版本对所有对话生效(各对话下一次运行开始时写新快照);
|
|
104
104
|
* 拒绝 → 删除草稿对象,发起对话下一次运行开始时目录回到当前版本;
|
|
105
105
|
* - 同一 agent 的同名技能最多一条待处理申请:同一对话再写取代旧的(旧的 cancelled、草稿删除、技能 id 不变),
|
|
@@ -22,6 +22,7 @@ import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
|
22
22
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
23
23
|
import { sameSnapshot, } from "../runtime/context/skills.js";
|
|
24
24
|
import { StorageService } from "../storage/storage.service.js";
|
|
25
|
+
import { BUILTIN } from "../tools/builtin-names.js";
|
|
25
26
|
import { fitPinned, invocationOf, lockLatestCatalog } from "./catalog.js";
|
|
26
27
|
/**
|
|
27
28
|
* 每个 agent 自己的技能(`scope = agent`)上限,含待批准的新技能。远低于目录快照的上限(`SKILLS_CATALOG_MAX`),
|
|
@@ -42,7 +43,7 @@ const AGENT_GONE_TEXT = "This agent no longer exists, so the skill cannot be sav
|
|
|
42
43
|
const iso = (v) => new Date(v).toISOString();
|
|
43
44
|
const byName = (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
44
45
|
const notFound = () => new ApiError("skill_change.not_found", "Skill change not found");
|
|
45
|
-
/** The body of the SKILL.md a
|
|
46
|
+
/** The body of the SKILL.md a SkillWrite just stored (as run start would read it); null if unparsable. */
|
|
46
47
|
function writtenBody(pkg) {
|
|
47
48
|
const md = pkg.files.find((f) => f.path === SKILL_ENTRY_FILE);
|
|
48
49
|
if (!md)
|
|
@@ -57,9 +58,9 @@ function writtenBody(pkg) {
|
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
59
60
|
* agent 自写技能与共享 agent 的技能变更审批(M5 T15,R4):
|
|
60
|
-
* - `write`(工具 `
|
|
61
|
+
* - `write`(工具 `SkillWrite` 调用):私有 agent 直接生成新版本;共享 agent 生成一条待负责人批准的申请,
|
|
61
62
|
* 期间只有发起对话试用(`resolveEffectiveSkills` 按对话合并);两种情况都在同一事务里往发起对话追加一份
|
|
62
|
-
* 技能目录快照(Ruling M17:日志是唯一事实源),同一运行的下一步即可 `
|
|
63
|
+
* 技能目录快照(Ruling M17:日志是唯一事实源),同一运行的下一步即可 `Skill`;
|
|
63
64
|
* - `decide`(HTTP,只有该 agent 当前的负责人):批准 → 版本对所有对话生效(各对话下一次运行开始时写新快照);
|
|
64
65
|
* 拒绝 → 删除草稿对象,发起对话下一次运行开始时目录回到当前版本;
|
|
65
66
|
* - 同一 agent 的同名技能最多一条待处理申请:同一对话再写取代旧的(旧的 cancelled、草稿删除、技能 id 不变),
|
|
@@ -245,7 +246,7 @@ let SkillChangesService = class SkillChangesService {
|
|
|
245
246
|
files: pkg.manifest.files.length,
|
|
246
247
|
agentId: ctx.agentId,
|
|
247
248
|
conversationId: ctx.conversationId,
|
|
248
|
-
via:
|
|
249
|
+
via: BUILTIN.SkillWrite,
|
|
249
250
|
},
|
|
250
251
|
});
|
|
251
252
|
outcome = { kind: "version", skillId, version, created: !existing };
|
|
@@ -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 `
|
|
5
|
+
* invocation nobody can use. Uploads, native imports and `SkillWrite` 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 `
|
|
6
|
+
* invocation nobody can use. Uploads, native imports and `SkillWrite` 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/invoke.d.ts
CHANGED
|
@@ -121,7 +121,7 @@ export declare class SkillInvoker {
|
|
|
121
121
|
* `storage.unavailable` (`skill_unreadable`) when the version is gone or its SKILL.md cannot be
|
|
122
122
|
* read, plain `storage.unavailable` when storage is down. `task` renders the text for the
|
|
123
123
|
* sub-agent's effective definition: the shell-injection line only when the sub-agent has
|
|
124
|
-
* `
|
|
124
|
+
* `Bash` (batch 3 review m1).
|
|
125
125
|
*/
|
|
126
126
|
renderTask(a: {
|
|
127
127
|
orgId: string;
|
package/dist/skills/invoke.js
CHANGED
|
@@ -15,6 +15,7 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
15
15
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
16
16
|
import { isGeneralAlias, resolveSkillForkType } from "../runtime/subagents/roster.js";
|
|
17
17
|
import { StorageService } from "../storage/storage.service.js";
|
|
18
|
+
import { BUILTIN } from "../tools/builtin-names.js";
|
|
18
19
|
import { findSkillVersion, readSkillFile, SkillIntegrityError, } from "../tools/builtins/skill-load.js";
|
|
19
20
|
import { invocationOf, resolveSkills } from "./catalog.js";
|
|
20
21
|
/** A leading `/<name>` or `/<plugin>:<name>`, then whitespace or the end (spec §3.2). */
|
|
@@ -142,10 +143,10 @@ let SkillInvoker = class SkillInvoker {
|
|
|
142
143
|
const body = await this.readBody(a.orgId, found);
|
|
143
144
|
const others = found.manifest.files.some((f) => f.path !== SKILL_ENTRY_FILE);
|
|
144
145
|
const lines = {
|
|
145
|
-
// the invocation itself makes the conversation's next run offer
|
|
146
|
+
// the invocation itself makes the conversation's next run offer Skill (skillLoadOffered)
|
|
146
147
|
...(others && { files: "load" }),
|
|
147
148
|
...(hasShellInjection(body) &&
|
|
148
|
-
hasBuiltin(a.agent,
|
|
149
|
+
hasBuiltin(a.agent, BUILTIN.Bash) && { shellHint: true }),
|
|
149
150
|
};
|
|
150
151
|
const input = {
|
|
151
152
|
name: a.name,
|
|
@@ -192,7 +193,7 @@ let SkillInvoker = class SkillInvoker {
|
|
|
192
193
|
* `storage.unavailable` (`skill_unreadable`) when the version is gone or its SKILL.md cannot be
|
|
193
194
|
* read, plain `storage.unavailable` when storage is down. `task` renders the text for the
|
|
194
195
|
* sub-agent's effective definition: the shell-injection line only when the sub-agent has
|
|
195
|
-
* `
|
|
196
|
+
* `Bash` (batch 3 review m1).
|
|
196
197
|
*/
|
|
197
198
|
async renderTask(a) {
|
|
198
199
|
const { skill } = a;
|
|
@@ -225,7 +226,7 @@ let SkillInvoker = class SkillInvoker {
|
|
|
225
226
|
lines: {
|
|
226
227
|
...(others && { files: "load" }),
|
|
227
228
|
...(hasShellInjection(body) &&
|
|
228
|
-
hasBuiltin(child,
|
|
229
|
+
hasBuiltin(child, BUILTIN.Bash) && { shellHint: true }),
|
|
229
230
|
},
|
|
230
231
|
lead: false,
|
|
231
232
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LocalizedName } from "@capybara-hq/plugin-sdk";
|
|
1
2
|
import { EventLog } from "../conversations/event-log.js";
|
|
2
3
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
3
4
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
@@ -43,6 +44,11 @@ export interface ApprovalView extends ApprovalRecord {
|
|
|
43
44
|
name: string;
|
|
44
45
|
slug: string;
|
|
45
46
|
} | null;
|
|
47
|
+
/** An extension tool's plugin (`plugin__<plugin>__<tool>`) and its display name (display only). */
|
|
48
|
+
extension: {
|
|
49
|
+
name: string;
|
|
50
|
+
displayName: LocalizedName;
|
|
51
|
+
} | null;
|
|
46
52
|
agent: {
|
|
47
53
|
id: string;
|
|
48
54
|
name: string;
|
|
@@ -8,6 +8,7 @@ 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 { connectorToolKey, parseToolKey } from "@capybara-hq/plugin-sdk";
|
|
11
12
|
import { Inject, Injectable } from "@nestjs/common";
|
|
12
13
|
import { sql } from "kysely";
|
|
13
14
|
import { EventLog } from "../conversations/event-log.js";
|
|
@@ -19,6 +20,7 @@ import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
|
19
20
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
20
21
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
21
22
|
import { ORG_ROLES, roleAtLeast } from "../orgs/roles.js";
|
|
23
|
+
import { pluginDisplayNames } from "./display-names.js";
|
|
22
24
|
export { APPROVAL_DENY_REASON_MAX_CHARS } from "../conversations/events.js";
|
|
23
25
|
/** `list` 一次最多返回的审批数(收件箱据此判断这个来源是否取满)。 */
|
|
24
26
|
export const APPROVALS_LIST_MAX = 100;
|
|
@@ -149,6 +151,8 @@ let ApprovalsService = class ApprovalsService {
|
|
|
149
151
|
toolKey: rec.toolKey,
|
|
150
152
|
expiresAt: rec.expiresAt,
|
|
151
153
|
...(input.policy !== undefined && { policy: input.policy }),
|
|
154
|
+
requestedFor: rec.requestedFor,
|
|
155
|
+
approverRole: rec.approverRole,
|
|
152
156
|
},
|
|
153
157
|
},
|
|
154
158
|
]);
|
|
@@ -579,12 +583,22 @@ let ApprovalsService = class ApprovalsService {
|
|
|
579
583
|
.where("id", "in", connectorIds)
|
|
580
584
|
.execute();
|
|
581
585
|
const connectorById = new Map(apps.map((a) => [a.id, a]));
|
|
586
|
+
const pluginOf = (r) => {
|
|
587
|
+
if (r.connectorId !== null)
|
|
588
|
+
return null;
|
|
589
|
+
const p = parseToolKey(r.toolKey);
|
|
590
|
+
return p?.kind === "extension" ? p.plugin : null;
|
|
591
|
+
};
|
|
592
|
+
const displayNames = await pluginDisplayNames(db.db, recs.flatMap((r) => {
|
|
593
|
+
const p = pluginOf(r);
|
|
594
|
+
return p === null ? [] : [p];
|
|
595
|
+
}));
|
|
582
596
|
return recs.flatMap((r) => {
|
|
583
597
|
const c = ctx.get(r.runId);
|
|
584
598
|
if (!c)
|
|
585
599
|
return [];
|
|
586
600
|
const app = r.connectorId === null ? undefined : connectorById.get(r.connectorId);
|
|
587
|
-
const prefix = app ?
|
|
601
|
+
const prefix = app ? connectorToolKey(app.slug, "") : undefined;
|
|
588
602
|
return [
|
|
589
603
|
{
|
|
590
604
|
...r,
|
|
@@ -592,6 +606,12 @@ let ApprovalsService = class ApprovalsService {
|
|
|
592
606
|
? r.toolKey.slice(prefix.length)
|
|
593
607
|
: r.toolKey,
|
|
594
608
|
connector: app ? { id: app.id, name: app.name, slug: app.slug } : null,
|
|
609
|
+
extension: (() => {
|
|
610
|
+
const p = pluginOf(r);
|
|
611
|
+
return p === null
|
|
612
|
+
? null
|
|
613
|
+
: { name: p, displayName: displayNames.get(p) ?? { zh: p, en: p } };
|
|
614
|
+
})(),
|
|
595
615
|
agent: { id: c.agent_id, name: names.get(c.agent_id) ?? "" },
|
|
596
616
|
...(c.parent_conversation_id !== null
|
|
597
617
|
? {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { AgentToolRef } from "../agents/definition.js";
|
|
2
|
+
import type { ToolKind } from "./info.js";
|
|
3
|
+
/**
|
|
4
|
+
* The names (model-visible keys) of the kernel's builtin tools, following Claude Code (tool naming
|
|
5
|
+
* spec §4.1). Plain constants: `BuiltinTools`, the tool factories, the registry, policy and the
|
|
6
|
+
* runtime import them from here. The web keeps its own copy (`apps/web/src/lib/agents.ts`,
|
|
7
|
+
* `lib/timeline.ts`), checked equal by `test/tools/builtin-names.test.ts`. The release-candidate
|
|
8
|
+
* names are in `legacy-keys.ts`, never here.
|
|
9
|
+
*/
|
|
10
|
+
export declare const BUILTIN: {
|
|
11
|
+
readonly Bash: "Bash";
|
|
12
|
+
readonly Read: "Read";
|
|
13
|
+
readonly Write: "Write";
|
|
14
|
+
readonly Edit: "Edit";
|
|
15
|
+
readonly Glob: "Glob";
|
|
16
|
+
readonly Grep: "Grep";
|
|
17
|
+
readonly Deliver: "Deliver";
|
|
18
|
+
readonly Skill: "Skill";
|
|
19
|
+
readonly SkillWrite: "SkillWrite";
|
|
20
|
+
readonly AskUserQuestion: "AskUserQuestion";
|
|
21
|
+
readonly ReadToolOutput: "ReadToolOutput";
|
|
22
|
+
readonly ToolSearch: "ToolSearch";
|
|
23
|
+
readonly Agent: "Agent";
|
|
24
|
+
readonly TaskOutput: "TaskOutput";
|
|
25
|
+
readonly TaskStop: "TaskStop";
|
|
26
|
+
readonly WebSearch: "WebSearch";
|
|
27
|
+
readonly WebFetch: "WebFetch";
|
|
28
|
+
};
|
|
29
|
+
export type BuiltinName = (typeof BUILTIN)[keyof typeof BUILTIN];
|
|
30
|
+
/**
|
|
31
|
+
* The builtin tools that run in the conversation's sandbox. `TaskOutput` and `TaskStop` read and
|
|
32
|
+
* stop the background tasks `Bash` started (background Bash spec §2.2, §2.3).
|
|
33
|
+
*/
|
|
34
|
+
export declare const SANDBOX_TOOL_NAMES: readonly BuiltinName[];
|
|
35
|
+
/** The builtin tools that reach the web through the org's web-search / web-fetch providers. */
|
|
36
|
+
export declare const WEB_TOOL_NAMES: readonly BuiltinName[];
|
|
37
|
+
/**
|
|
38
|
+
* The tools offered with `Bash` whenever it is offered (background Bash: a model that can start a
|
|
39
|
+
* background task can read and stop it). `ToolRegistry` adds them; an explicit ref is harmless.
|
|
40
|
+
*/
|
|
41
|
+
export declare const BASH_COMPANION_TOOLS: readonly BuiltinName[];
|
|
42
|
+
/**
|
|
43
|
+
* Sandbox tools that need approval when the org setting `sandboxToolPolicy` is `ask` (not the
|
|
44
|
+
* read-only ones). Not `TaskOutput` or `TaskStop`: they only read or stop a task whose launch
|
|
45
|
+
* already went through `Bash`'s policy (background Bash spec §4.5).
|
|
46
|
+
*/
|
|
47
|
+
export declare const SANDBOX_ASK_TOOL_NAMES: ReadonlySet<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Every builtin name. Saving an agent checks builtin refs against this list, not against what is
|
|
50
|
+
* registered now, so the order the implementations land in never affects a saved definition.
|
|
51
|
+
* `SkillWrite` is available only when a definition names it; `ToolSearch` and `Skill` are added
|
|
52
|
+
* by the registry; `Agent` dispatches sub-agents and its description is built per agent.
|
|
53
|
+
*/
|
|
54
|
+
export declare const BUILTIN_TOOL_NAMES: readonly BuiltinName[];
|
|
55
|
+
/** The builtin that loads a skill (added by the registry when the log has a skills catalog). */
|
|
56
|
+
export declare const SKILL_LOAD: "Skill";
|
|
57
|
+
/** The builtin that searches the tool catalog (added by the registry by org setting and threshold). */
|
|
58
|
+
export declare const TOOL_SEARCH: "ToolSearch";
|
|
59
|
+
/** The builtin that dispatches sub-agents. */
|
|
60
|
+
export declare const AGENT_TOOL: "Agent";
|
|
61
|
+
/**
|
|
62
|
+
* Tool keys on the spec 3.6 R3 fixed list ("allowed is not confirmed"): their policy is at least
|
|
63
|
+
* `ask` and remembered grants never let them through. `SkillWrite` changes shared skills.
|
|
64
|
+
* Connector and extension tools express the same rule with `annotations.requiresUserInteraction`.
|
|
65
|
+
*/
|
|
66
|
+
export declare const R3_FIXED_ASK_TOOL_KEYS: ReadonlySet<string>;
|
|
67
|
+
/**
|
|
68
|
+
* R3 keys that apply only to shared agents: a private agent's skills are not shared, so its
|
|
69
|
+
* `SkillWrite` takes effect without asking (R4).
|
|
70
|
+
*/
|
|
71
|
+
export declare const R3_SHARED_AGENT_ONLY_TOOL_KEYS: ReadonlySet<string>;
|
|
72
|
+
/**
|
|
73
|
+
* Tools a sub-agent never gets (sub-agents spec §5.3, §5.5): `Agent` (depth 1), `AskUserQuestion`
|
|
74
|
+
* (the person does not see the child), `Deliver` (delivering files is the parent's job) and
|
|
75
|
+
* `SkillWrite`.
|
|
76
|
+
*/
|
|
77
|
+
export declare const CHILD_REMOVED_TOOLS: readonly ["Agent", "AskUserQuestion", "Deliver", "SkillWrite"];
|
|
78
|
+
/**
|
|
79
|
+
* The tools of a new agent created without `tools` (`ToolSearch` is added by the registry, never
|
|
80
|
+
* by a definition). Only for create requests; updates and stored versions get no defaults.
|
|
81
|
+
*/
|
|
82
|
+
export declare const DEFAULT_AGENT_TOOLS: AgentToolRef[];
|
|
83
|
+
/** Kinds of the builtin tools (display only); a builtin missing here is a plain `tool`. */
|
|
84
|
+
export declare const BUILTIN_TOOL_KINDS: Readonly<Record<string, ToolKind>>;
|
|
85
|
+
/**
|
|
86
|
+
* Input fields left out when matching a replayed call with a recorded one (full-run replay), per
|
|
87
|
+
* tool: display-only arguments the model writes that do not change what the tool does. `Bash`'s
|
|
88
|
+
* `description` became required in UI refresh B, so a recording without it and a replayed call
|
|
89
|
+
* with one (or a different wording) still match.
|
|
90
|
+
*/
|
|
91
|
+
export declare const MATCH_IGNORED_FIELDS: Readonly<Record<string, readonly string[]>>;
|