@capybara-hq/kernel 0.1.0-rc.2 → 0.1.0-rc.3
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/agents/agent-lookup.js +10 -2
- package/dist/agents/agents.service.js +19 -6
- package/dist/agents/definition.d.ts +31 -8
- package/dist/agents/definition.js +193 -17
- package/dist/agents/templates.js +19 -4
- package/dist/catalog/catalog-content.js +4 -1
- package/dist/catalog/claude/agents.js +13 -10
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/channels/cards.d.ts +8 -2
- package/dist/channels/cards.js +16 -8
- package/dist/config/schema.js +2 -2
- package/dist/conversations/artifacts.d.ts +1 -1
- package/dist/conversations/artifacts.js +1 -1
- package/dist/conversations/event-log.d.ts +11 -0
- package/dist/conversations/event-log.js +19 -0
- package/dist/conversations/events.d.ts +23 -3
- package/dist/conversations/events.js +26 -14
- package/dist/db/kernel-schema.d.ts +2 -2
- package/dist/evals/recorded-tools.d.ts +27 -1
- package/dist/evals/recorded-tools.js +85 -10
- package/dist/evals/run-replay.d.ts +5 -0
- package/dist/evals/run-replay.js +19 -5
- package/dist/evals/trajectory.d.ts +2 -2
- package/dist/identity/settings.js +1 -1
- package/dist/lifecycle/kernel-lifecycle.js +5 -0
- package/dist/orgs/settings.service.d.ts +2 -2
- package/dist/orgs/settings.service.js +2 -2
- package/dist/runtime/context/build.d.ts +6 -6
- package/dist/runtime/context/build.js +18 -8
- package/dist/runtime/context/claude-tools.d.ts +2 -1
- package/dist/runtime/context/claude-tools.js +9 -10
- 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/loop.d.ts +1 -1
- package/dist/runtime/model-call.js +1 -1
- package/dist/runtime/run-finish.d.ts +1 -1
- package/dist/runtime/run-queue.d.ts +3 -3
- package/dist/runtime/run-queue.js +2 -2
- package/dist/runtime/run-worker.d.ts +2 -2
- package/dist/runtime/run-worker.js +4 -4
- package/dist/runtime/subagents/definition.d.ts +1 -6
- package/dist/runtime/subagents/definition.js +11 -12
- 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/waits.d.ts +5 -5
- package/dist/runtime/waits.js +5 -5
- 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 +19 -1
- package/dist/tools/builtin-names.d.ts +73 -0
- package/dist/tools/builtin-names.js +116 -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 +2 -2
- package/dist/tools/builtins/builtin-tools.js +2 -2
- 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 +5 -5
- package/dist/tools/builtins/sandbox/bash.js +31 -28
- package/dist/tools/builtins/sandbox/common.d.ts +3 -3
- package/dist/tools/builtins/sandbox/common.js +2 -2
- package/dist/tools/builtins/sandbox/deliver.d.ts +1 -1
- package/dist/tools/builtins/sandbox/deliver.js +11 -11
- 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 +1 -1
- package/dist/tools/builtins/sandbox/index.js +1 -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/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/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/outputs.js +1 -1
- package/dist/tools/pipeline.d.ts +13 -7
- package/dist/tools/pipeline.js +97 -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 +16 -21
- package/dist/tools/registry.js +29 -37
- package/dist/tools/resources.d.ts +1 -1
- package/dist/tools/resources.js +1 -1
- package/dist/tools/tools.module.js +1 -1
- package/package.json +2 -2
- package/src/migrations/0063_tool_keys_claude_code.sql +220 -0
- package/dist/tools/builtins/sandbox/names.d.ts +0 -7
- package/dist/tools/builtins/sandbox/names.js +0 -20
|
@@ -8,7 +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 { ToolError, } from "@capybara-hq/plugin-sdk";
|
|
11
|
+
import { connectorToolKey, ToolError, } from "@capybara-hq/plugin-sdk";
|
|
12
12
|
import { Inject, Injectable } from "@nestjs/common";
|
|
13
13
|
import { sql } from "kysely";
|
|
14
14
|
import { z } from "zod";
|
|
@@ -25,7 +25,7 @@ import { OrgLock } from "../orgs/org-lock.js";
|
|
|
25
25
|
import { CredentialMissing, Credentials, isOAuth, } from "./credentials.js";
|
|
26
26
|
import { snapshot, ToolDrift, toolIssue, } from "./drift.js";
|
|
27
27
|
import { endpointCredentialIssues, endpointView } from "./endpoint.js";
|
|
28
|
-
import { deriveSlug, isValidSlug, SLUG_RULE
|
|
28
|
+
import { deriveSlug, isValidSlug, SLUG_RULE } from "./naming.js";
|
|
29
29
|
import { checkOAuthUrl, discoverMcp, OAuthSetupError, oauthHttpOptions, pickAuthMethod, registerClient, } from "./oauth/discovery.js";
|
|
30
30
|
import { callbackUrl, publicUrlRequired } from "./oauth/flow-paths.js";
|
|
31
31
|
import { OAuthGrantError, OAuthUnavailable, tokenRequest, } from "./oauth/tokens.js";
|
|
@@ -619,7 +619,7 @@ let ConnectorsService = class ConnectorsService {
|
|
|
619
619
|
issues = snap.issues;
|
|
620
620
|
tools = snap.rows.map((r) => ({
|
|
621
621
|
name: r.name,
|
|
622
|
-
key:
|
|
622
|
+
key: connectorToolKey(slug, r.name),
|
|
623
623
|
description: r.spec.description,
|
|
624
624
|
inputSchema: r.spec.inputSchema,
|
|
625
625
|
annotations: r.spec.annotations ?? null,
|
|
@@ -720,7 +720,7 @@ let ConnectorsService = class ConnectorsService {
|
|
|
720
720
|
const next = t.pending_spec;
|
|
721
721
|
return {
|
|
722
722
|
name: t.name,
|
|
723
|
-
key:
|
|
723
|
+
key: connectorToolKey(r.slug, t.name),
|
|
724
724
|
description: spec.description,
|
|
725
725
|
inputSchema: spec.inputSchema,
|
|
726
726
|
annotations: spec.annotations ?? null,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LocalizedName } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* The display names of plugins, for naming their extensions' tools (`<display name> [<tool>]`,
|
|
5
|
+
* tool naming spec §7): the live catalog item's `displayName`, else the plugin name in both
|
|
6
|
+
* languages (a bootstrap or uploaded plugin without a catalog item, or one without a display name).
|
|
7
|
+
* Display only.
|
|
8
|
+
*/
|
|
9
|
+
export declare function pluginDisplayNames(db: KernelSchemaDb, names: readonly string[]): Promise<Map<string, LocalizedName>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The display names of plugins, for naming their extensions' tools (`<display name> [<tool>]`,
|
|
3
|
+
* tool naming spec §7): the live catalog item's `displayName`, else the plugin name in both
|
|
4
|
+
* languages (a bootstrap or uploaded plugin without a catalog item, or one without a display name).
|
|
5
|
+
* Display only.
|
|
6
|
+
*/
|
|
7
|
+
export async function pluginDisplayNames(db, names) {
|
|
8
|
+
const unique = [...new Set(names)];
|
|
9
|
+
const out = new Map(unique.map((n) => [n, { zh: n, en: n }]));
|
|
10
|
+
if (unique.length === 0)
|
|
11
|
+
return out;
|
|
12
|
+
const rows = await db
|
|
13
|
+
.selectFrom("catalog_items")
|
|
14
|
+
.select(["name", "display_name"])
|
|
15
|
+
.where("name", "in", unique)
|
|
16
|
+
.where("deleted_at", "is", null)
|
|
17
|
+
.execute();
|
|
18
|
+
for (const r of rows) {
|
|
19
|
+
const d = r.display_name;
|
|
20
|
+
if (d && typeof d.zh === "string" && typeof d.en === "string" && d.zh !== "" && d.en !== "")
|
|
21
|
+
out.set(r.name, { zh: d.zh, en: d.en });
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
package/dist/tools/drift.js
CHANGED
|
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { TOOL_TITLE_MAX, ToolError, validateToolPresentation, } from "@capybara-hq/plugin-sdk";
|
|
10
|
+
import { connectorToolKey, TOOL_TITLE_MAX, ToolError, validateToolPresentation, } from "@capybara-hq/plugin-sdk";
|
|
11
11
|
import { getQueueToken } from "@nestjs/bullmq";
|
|
12
12
|
import { Inject, Injectable } from "@nestjs/common";
|
|
13
13
|
import { sql } from "kysely";
|
|
@@ -21,7 +21,7 @@ import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
|
21
21
|
import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
|
|
22
22
|
import { HIDDEN_FORMAT_RE } from "../text.js";
|
|
23
23
|
import { CredentialMissing, Credentials, isOAuth } from "./credentials.js";
|
|
24
|
-
import {
|
|
24
|
+
import { toolKeyIssue } from "./naming.js";
|
|
25
25
|
import { redactDeep, redactorFor } from "./redact.js";
|
|
26
26
|
import { canonicalJson, policyFromAnnotations, specHash, ToolRegistry } from "./registry.js";
|
|
27
27
|
import { resourceToolSpecs } from "./resources.js";
|
|
@@ -105,7 +105,7 @@ export function snapshot(slug, specs, resources) {
|
|
|
105
105
|
...(isObj(s.connectorData) && { connectorData: s.connectorData }),
|
|
106
106
|
...displayFields(s),
|
|
107
107
|
};
|
|
108
|
-
let issue = toolKeyIssue(
|
|
108
|
+
let issue = toolKeyIssue(connectorToolKey(slug, name));
|
|
109
109
|
if (!issue && !isObj(s.inputSchema))
|
|
110
110
|
issue = "Tool inputSchema must be an object";
|
|
111
111
|
if (issue)
|
|
@@ -122,7 +122,7 @@ export function snapshot(slug, specs, resources) {
|
|
|
122
122
|
});
|
|
123
123
|
continue;
|
|
124
124
|
}
|
|
125
|
-
const issue = toolKeyIssue(
|
|
125
|
+
const issue = toolKeyIssue(connectorToolKey(slug, spec.name));
|
|
126
126
|
if (issue)
|
|
127
127
|
issues.push({ name: spec.name, reason: issue });
|
|
128
128
|
rows.push({ name: spec.name, spec, specHash: specHash(spec), issue, source: "resources" });
|
|
@@ -172,7 +172,7 @@ const displayOf = (spec) => {
|
|
|
172
172
|
};
|
|
173
173
|
/** 工具当前不能启用的原因(视图、启用与复核校验共用)。 */
|
|
174
174
|
export function toolIssue(slug, spec) {
|
|
175
|
-
const keyIssue = toolKeyIssue(
|
|
175
|
+
const keyIssue = toolKeyIssue(connectorToolKey(slug, spec.name));
|
|
176
176
|
if (keyIssue)
|
|
177
177
|
return keyIssue;
|
|
178
178
|
if (!isObj(spec.inputSchema))
|
|
@@ -33,7 +33,7 @@ export class ElicitationSuspended extends Error {
|
|
|
33
33
|
}
|
|
34
34
|
/** 不能展示的 elicitation 被拒绝时附在工具结果后的说明(模型可见,英文)。 */
|
|
35
35
|
export const UNSUPPORTED_ELICITATION_NOTE = "The app asked the user for input that cannot be shown in this client, so the request was declined";
|
|
36
|
-
const unsupportedNote = (why) => `[${UNSUPPORTED_ELICITATION_NOTE} (${why}). If the call needs that input, ask the user for it with
|
|
36
|
+
const unsupportedNote = (why) => `[${UNSUPPORTED_ELICITATION_NOTE} (${why}). If the call needs that input, ask the user for it with AskUserQuestion and pass it as tool arguments, or try another approach.]`;
|
|
37
37
|
/** URL 模式表单里「已完成」开关的字段名。 */
|
|
38
38
|
export const URL_COMPLETED_FIELD = "completed";
|
|
39
39
|
/** 表单标题(远端给的 message)最多字符数。 */
|
package/dist/tools/info.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LocalizedName } from "@capybara-hq/plugin-sdk";
|
|
1
2
|
import { ConversationAccess } from "../conversations/access.js";
|
|
2
3
|
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
3
4
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
@@ -11,16 +12,32 @@ export type ToolKind = "command" | "read" | "edit" | "search" | "connector" | "m
|
|
|
11
12
|
export interface ToolInfo {
|
|
12
13
|
/** The model-visible tool key, as asked for. */
|
|
13
14
|
key: string;
|
|
15
|
+
/**
|
|
16
|
+
* Set only for a release-candidate builtin key (old history, tool naming spec §9): the current
|
|
17
|
+
* tool it stands for. Everything else describes that tool, with `keyArgs` in the old parameter
|
|
18
|
+
* names the logged input carries. Pages map a call's name through it (the `Agent` row, labels).
|
|
19
|
+
*/
|
|
20
|
+
aliasOf?: string;
|
|
14
21
|
/** The declared display title (`ToolSpec.title`), or null. */
|
|
15
22
|
title: string | null;
|
|
16
23
|
kind: ToolKind;
|
|
24
|
+
/** True exactly for the builtin tools that run in the conversation's sandbox. */
|
|
25
|
+
sandbox: boolean;
|
|
17
26
|
/** The declared key arguments (`ToolSpec.keyArgs`), or []. */
|
|
18
27
|
keyArgs: string[];
|
|
28
|
+
/**
|
|
29
|
+
* The local tool name parsed from a connector or extension key (`mcp__crm__delete` -> `delete`),
|
|
30
|
+
* shown in brackets after the source's display name; null for builtins and unparsable keys.
|
|
31
|
+
*/
|
|
32
|
+
tool: string | null;
|
|
19
33
|
source: {
|
|
20
34
|
kind: "builtin";
|
|
21
|
-
}
|
|
35
|
+
}
|
|
36
|
+
/** `displayName`: the plugin's catalog display name, else its name. */
|
|
37
|
+
| {
|
|
22
38
|
kind: "extension";
|
|
23
39
|
name: string;
|
|
40
|
+
displayName: LocalizedName;
|
|
24
41
|
} | {
|
|
25
42
|
kind: "connector";
|
|
26
43
|
id: string;
|
|
@@ -31,8 +48,7 @@ export interface ToolInfo {
|
|
|
31
48
|
kind: "unknown";
|
|
32
49
|
};
|
|
33
50
|
}
|
|
34
|
-
|
|
35
|
-
export declare const BUILTIN_TOOL_KINDS: Readonly<Record<string, ToolKind>>;
|
|
51
|
+
export { BUILTIN_TOOL_KINDS } from "./builtin-names.js";
|
|
36
52
|
/** At most this many keys per request, each at most {@link TOOL_INFO_KEY_MAX} characters. */
|
|
37
53
|
export declare const TOOL_INFO_MAX_KEYS = 100;
|
|
38
54
|
export declare const TOOL_INFO_KEY_MAX = 200;
|
|
@@ -42,7 +58,7 @@ export interface ToolInfoDeps {
|
|
|
42
58
|
}
|
|
43
59
|
/**
|
|
44
60
|
* Presentation metadata for `keys` in this org, in request order with repeats dropped. Builtins
|
|
45
|
-
* by name;
|
|
61
|
+
* by name (a release-candidate builtin key as its current tool, with `aliasOf`); `plugin__<plugin>__<tool>` keys as a loaded extension's registration, and `mcp__<slug>__<tool>` keys as a live
|
|
46
62
|
* connector's current stored tool (its reviewed definition, never a pending one). Anything else
|
|
47
63
|
* comes back as an unknown plain tool.
|
|
48
64
|
*
|
package/dist/tools/info.js
CHANGED
|
@@ -7,28 +7,17 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
+
import { connectorToolKey, extensionToolKey, parseToolKey, } from "@capybara-hq/plugin-sdk";
|
|
10
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
11
12
|
import { ConversationAccess } from "../conversations/access.js";
|
|
12
13
|
import { ApiError } from "../errors.js";
|
|
13
14
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
14
15
|
import { UUID_RE } from "../ui/registry.js";
|
|
15
|
-
import {
|
|
16
|
+
import { BUILTIN_TOOL_KINDS, SANDBOX_TOOL_NAMES } from "./builtin-names.js";
|
|
17
|
+
import { pluginDisplayNames } from "./display-names.js";
|
|
18
|
+
import { isLegacyBuiltinKey, LEGACY_BUILTINS } from "./legacy-keys.js";
|
|
16
19
|
import { ToolRegistry } from "./registry.js";
|
|
17
|
-
|
|
18
|
-
export const BUILTIN_TOOL_KINDS = {
|
|
19
|
-
sandbox_bash: "command",
|
|
20
|
-
sandbox_read: "read",
|
|
21
|
-
sandbox_write: "edit",
|
|
22
|
-
sandbox_edit: "edit",
|
|
23
|
-
sandbox_glob: "search",
|
|
24
|
-
sandbox_grep: "search",
|
|
25
|
-
sandbox_deliver: "tool",
|
|
26
|
-
tool_search: "meta",
|
|
27
|
-
skill_load: "meta",
|
|
28
|
-
skill_write: "meta",
|
|
29
|
-
read_tool_output: "meta",
|
|
30
|
-
ask_user: "meta",
|
|
31
|
-
};
|
|
20
|
+
export { BUILTIN_TOOL_KINDS } from "./builtin-names.js";
|
|
32
21
|
/** At most this many keys per request, each at most {@link TOOL_INFO_KEY_MAX} characters. */
|
|
33
22
|
export const TOOL_INFO_MAX_KEYS = 100;
|
|
34
23
|
export const TOOL_INFO_KEY_MAX = 200;
|
|
@@ -38,7 +27,7 @@ const display = (spec) => ({
|
|
|
38
27
|
});
|
|
39
28
|
/**
|
|
40
29
|
* Presentation metadata for `keys` in this org, in request order with repeats dropped. Builtins
|
|
41
|
-
* by name;
|
|
30
|
+
* by name (a release-candidate builtin key as its current tool, with `aliasOf`); `plugin__<plugin>__<tool>` keys as a loaded extension's registration, and `mcp__<slug>__<tool>` keys as a live
|
|
42
31
|
* connector's current stored tool (its reviewed definition, never a pending one). Anything else
|
|
43
32
|
* comes back as an unknown plain tool.
|
|
44
33
|
*
|
|
@@ -55,17 +44,13 @@ const display = (spec) => ({
|
|
|
55
44
|
export async function toolInfo(deps, orgId, keys) {
|
|
56
45
|
const unique = [...new Set(keys)];
|
|
57
46
|
const builtins = new Map(deps.registry.builtinSpecs().map((s) => [s.name, s]));
|
|
58
|
-
const extensions = new Map(deps.registry.extensionToolSpecs().map((t) => [
|
|
59
|
-
const split = (key) => {
|
|
60
|
-
const i = key.indexOf(KEY_SEPARATOR);
|
|
61
|
-
return i > 0 ? [key.slice(0, i), key.slice(i + KEY_SEPARATOR.length)] : null;
|
|
62
|
-
};
|
|
47
|
+
const extensions = new Map(deps.registry.extensionToolSpecs().map((t) => [extensionToolKey(t.plugin, t.spec.name), t]));
|
|
63
48
|
const slugs = [
|
|
64
49
|
...new Set(unique.flatMap((k) => {
|
|
65
50
|
if (builtins.has(k) || extensions.has(k))
|
|
66
51
|
return [];
|
|
67
|
-
const p =
|
|
68
|
-
return p ? [p
|
|
52
|
+
const p = parseToolKey(k);
|
|
53
|
+
return p?.kind === "connector" ? [p.slug] : [];
|
|
69
54
|
})),
|
|
70
55
|
];
|
|
71
56
|
const connectorRows = slugs.length === 0
|
|
@@ -78,15 +63,37 @@ export async function toolInfo(deps, orgId, keys) {
|
|
|
78
63
|
.where("c.deleted_at", "is", null)
|
|
79
64
|
.where("c.slug", "in", slugs)
|
|
80
65
|
.execute();
|
|
81
|
-
const connectorTools = new Map(connectorRows.map((r) => [
|
|
66
|
+
const connectorTools = new Map(connectorRows.map((r) => [connectorToolKey(r.slug, r.tool), r]));
|
|
67
|
+
const displayNames = await pluginDisplayNames(deps.db, unique.flatMap((k) => {
|
|
68
|
+
const e = extensions.get(k);
|
|
69
|
+
return e ? [e.plugin] : [];
|
|
70
|
+
}));
|
|
71
|
+
const localTool = (key) => {
|
|
72
|
+
const p = parseToolKey(key);
|
|
73
|
+
return p && p.kind !== "builtin" ? p.tool : null;
|
|
74
|
+
};
|
|
75
|
+
const builtinInfo = (key, name, spec) => ({
|
|
76
|
+
key,
|
|
77
|
+
...display(spec),
|
|
78
|
+
kind: BUILTIN_TOOL_KINDS[name] ?? "tool",
|
|
79
|
+
sandbox: SANDBOX_TOOL_NAMES.includes(name),
|
|
80
|
+
tool: null,
|
|
81
|
+
source: { kind: "builtin" },
|
|
82
|
+
});
|
|
82
83
|
return unique.map((key) => {
|
|
83
84
|
const b = builtins.get(key);
|
|
84
|
-
if (b)
|
|
85
|
+
if (b)
|
|
86
|
+
return builtinInfo(key, key, b);
|
|
87
|
+
const legacy = isLegacyBuiltinKey(key) ? LEGACY_BUILTINS[key] : undefined;
|
|
88
|
+
const current = legacy && builtins.get(legacy.name);
|
|
89
|
+
if (legacy && current) {
|
|
90
|
+
// the logged input of an rc call names the old parameters: key arguments in those names
|
|
91
|
+
const oldName = new Map(Object.entries(legacy.params).map(([o, n]) => [n, o]));
|
|
92
|
+
const info = builtinInfo(key, legacy.name, current);
|
|
85
93
|
return {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
source: { kind: "builtin" },
|
|
94
|
+
...info,
|
|
95
|
+
aliasOf: legacy.name,
|
|
96
|
+
keyArgs: info.keyArgs.map((a) => oldName.get(a) ?? a),
|
|
90
97
|
};
|
|
91
98
|
}
|
|
92
99
|
const e = extensions.get(key);
|
|
@@ -95,7 +102,13 @@ export async function toolInfo(deps, orgId, keys) {
|
|
|
95
102
|
key,
|
|
96
103
|
...display(e.spec),
|
|
97
104
|
kind: "tool",
|
|
98
|
-
|
|
105
|
+
sandbox: false,
|
|
106
|
+
tool: e.spec.name,
|
|
107
|
+
source: {
|
|
108
|
+
kind: "extension",
|
|
109
|
+
name: e.plugin,
|
|
110
|
+
displayName: displayNames.get(e.plugin) ?? { zh: e.plugin, en: e.plugin },
|
|
111
|
+
},
|
|
99
112
|
};
|
|
100
113
|
const c = connectorTools.get(key);
|
|
101
114
|
if (c) {
|
|
@@ -104,10 +117,20 @@ export async function toolInfo(deps, orgId, keys) {
|
|
|
104
117
|
key,
|
|
105
118
|
...display(c.spec),
|
|
106
119
|
kind: "connector",
|
|
120
|
+
sandbox: false,
|
|
121
|
+
tool: c.tool,
|
|
107
122
|
source: { kind: "connector", id: c.id, name: c.name },
|
|
108
123
|
};
|
|
109
124
|
}
|
|
110
|
-
return {
|
|
125
|
+
return {
|
|
126
|
+
key,
|
|
127
|
+
title: null,
|
|
128
|
+
kind: "tool",
|
|
129
|
+
sandbox: false,
|
|
130
|
+
keyArgs: [],
|
|
131
|
+
tool: localTool(key),
|
|
132
|
+
source: { kind: "unknown" },
|
|
133
|
+
};
|
|
111
134
|
});
|
|
112
135
|
}
|
|
113
136
|
/** `GET /conversations/:id/tools`: {@link toolInfo} for a conversation the viewer can read. */
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type AgentDefinition } from "../agents/definition.js";
|
|
2
|
+
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
3
|
+
import type { KernelLogger } from "../log.js";
|
|
4
|
+
import { type LegacyScope } from "./legacy-keys.js";
|
|
5
|
+
/**
|
|
6
|
+
* Stored release-candidate tool keys (tool naming spec §8): the scope that reads an rc
|
|
7
|
+
* `<x>__<tool>` key in an org, the stored-definition readers that use it, and the startup notice
|
|
8
|
+
* for the rows migration 0063 could not map. No in-process state: each call reads PostgreSQL.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* What an rc `<x>__<tool>` key can name in `orgId`: the org's live connector slugs and the
|
|
12
|
+
* deployment's extension names (installed ones and config-declared ones, which claim their name
|
|
13
|
+
* in `extension_name_owners`). Migration 0063 reads the same two sources.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadLegacyScope(db: KernelSchemaDb, orgId: string): Promise<LegacyScope>;
|
|
16
|
+
/**
|
|
17
|
+
* A stored agent definition of `orgId` with every rc key it can map read as the current key
|
|
18
|
+
* ({@link normalizeStoredDefinition}); the scope is read only when the definition holds an rc
|
|
19
|
+
* `<x>__<tool>` key.
|
|
20
|
+
*/
|
|
21
|
+
export declare function normalizeStoredDefinitionIn(db: KernelSchemaDb, orgId: string, def: unknown): Promise<AgentDefinition>;
|
|
22
|
+
/** A stored agent template (or any template-shaped value) of `orgId` in current keys. */
|
|
23
|
+
export declare function normalizeTemplateIn<T>(db: KernelSchemaDb, orgId: string, t: T): Promise<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Rows still holding an rc tool key after migration 0063: grants and pending approvals and
|
|
26
|
+
* questions it could not map (an ambiguous or unknown `<x>__<tool>`). Read-only.
|
|
27
|
+
*/
|
|
28
|
+
export declare function countLegacyToolKeys(db: KernelSchemaDb): Promise<number>;
|
|
29
|
+
/**
|
|
30
|
+
* The startup notice (spec §8.2): one WARN `tools.legacy_key_unmapped` with the count of rows
|
|
31
|
+
* still holding an rc key, only when there are any. A grant that no longer matches stops applying
|
|
32
|
+
* (the person is asked again), so this is a notice, not a failure.
|
|
33
|
+
*/
|
|
34
|
+
export declare function warnLegacyToolKeys(db: KernelSchemaDb, log: Pick<KernelLogger, "warn">): Promise<void>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
2
|
+
import { needsLegacyScope, normalizeStoredDefinition, normalizeToolKeys, } from "../agents/definition.js";
|
|
3
|
+
import { LEGACY_BUILTINS } from "./legacy-keys.js";
|
|
4
|
+
/**
|
|
5
|
+
* Stored release-candidate tool keys (tool naming spec §8): the scope that reads an rc
|
|
6
|
+
* `<x>__<tool>` key in an org, the stored-definition readers that use it, and the startup notice
|
|
7
|
+
* for the rows migration 0063 could not map. No in-process state: each call reads PostgreSQL.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* What an rc `<x>__<tool>` key can name in `orgId`: the org's live connector slugs and the
|
|
11
|
+
* deployment's extension names (installed ones and config-declared ones, which claim their name
|
|
12
|
+
* in `extension_name_owners`). Migration 0063 reads the same two sources.
|
|
13
|
+
*/
|
|
14
|
+
export async function loadLegacyScope(db, orgId) {
|
|
15
|
+
const [connectors, installs, owners] = await Promise.all([
|
|
16
|
+
db
|
|
17
|
+
.selectFrom("connectors")
|
|
18
|
+
.select(["id", "slug"])
|
|
19
|
+
.where("org_id", "=", orgId)
|
|
20
|
+
.where("deleted_at", "is", null)
|
|
21
|
+
.execute(),
|
|
22
|
+
db.selectFrom("extension_installs").select("name").execute(),
|
|
23
|
+
db.selectFrom("extension_name_owners").select("name").execute(),
|
|
24
|
+
]);
|
|
25
|
+
return {
|
|
26
|
+
connectorSlugs: new Set(connectors.map((r) => r.slug)),
|
|
27
|
+
connectorSlugById: new Map(connectors.map((r) => [r.id, r.slug])),
|
|
28
|
+
extensionNames: new Set([...installs, ...owners].map((r) => r.name)),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A stored agent definition of `orgId` with every rc key it can map read as the current key
|
|
33
|
+
* ({@link normalizeStoredDefinition}); the scope is read only when the definition holds an rc
|
|
34
|
+
* `<x>__<tool>` key.
|
|
35
|
+
*/
|
|
36
|
+
export async function normalizeStoredDefinitionIn(db, orgId, def) {
|
|
37
|
+
return normalizeStoredDefinition(def, needsLegacyScope(def) ? await loadLegacyScope(db, orgId) : undefined);
|
|
38
|
+
}
|
|
39
|
+
/** A stored agent template (or any template-shaped value) of `orgId` in current keys. */
|
|
40
|
+
export async function normalizeTemplateIn(db, orgId, t) {
|
|
41
|
+
return normalizeToolKeys(t, needsLegacyScope(t) ? await loadLegacyScope(db, orgId) : undefined);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The SQL test for an rc key, the predicate of migration 0063's partial indexes (so the count
|
|
45
|
+
* below reads only those, nearly empty, indexes): an old builtin name, or `<x>__<tool>` not in the
|
|
46
|
+
* current `mcp__` / `plugin__` format.
|
|
47
|
+
*/
|
|
48
|
+
const legacyKeySql = (col) => sql `(${col} = ANY (ARRAY[${sql.join(Object.keys(LEGACY_BUILTINS).map((k) => sql.lit(k)))}]::text[]) OR (${col} ~ '^[a-z0-9][a-z0-9_-]*__.' AND ${col} !~ '^(mcp|plugin)__'))`;
|
|
49
|
+
/**
|
|
50
|
+
* Rows still holding an rc tool key after migration 0063: grants and pending approvals and
|
|
51
|
+
* questions it could not map (an ambiguous or unknown `<x>__<tool>`). Read-only.
|
|
52
|
+
*/
|
|
53
|
+
export async function countLegacyToolKeys(db) {
|
|
54
|
+
const r = await sql `
|
|
55
|
+
SELECT
|
|
56
|
+
(SELECT count(*) FROM capybara.tool_grants WHERE ${legacyKeySql(sql.ref("tool_key"))})
|
|
57
|
+
+ (SELECT count(*) FROM capybara.conversation_tool_grants
|
|
58
|
+
WHERE ${legacyKeySql(sql.ref("tool_key"))})
|
|
59
|
+
+ (SELECT count(*) FROM capybara.approvals
|
|
60
|
+
WHERE status = 'pending' AND ${legacyKeySql(sql.ref("tool_key"))})
|
|
61
|
+
+ (SELECT count(*) FROM capybara.questions
|
|
62
|
+
WHERE status = 'pending' AND ${legacyKeySql(sql.raw("(spec->>'toolKey')"))})
|
|
63
|
+
AS n
|
|
64
|
+
`.execute(db);
|
|
65
|
+
return Number(r.rows[0]?.n ?? 0);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The startup notice (spec §8.2): one WARN `tools.legacy_key_unmapped` with the count of rows
|
|
69
|
+
* still holding an rc key, only when there are any. A grant that no longer matches stops applying
|
|
70
|
+
* (the person is asked again), so this is a notice, not a failure.
|
|
71
|
+
*/
|
|
72
|
+
export async function warnLegacyToolKeys(db, log) {
|
|
73
|
+
const count = await countLegacyToolKeys(db);
|
|
74
|
+
if (count > 0)
|
|
75
|
+
log.warn("tools.legacy_key_unmapped", { count });
|
|
76
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type BuiltinName } from "./builtin-names.js";
|
|
2
|
+
/**
|
|
3
|
+
* The release-candidate builtin tool names (tool naming spec §8.1). FROZEN: it describes the names
|
|
4
|
+
* of the 0.1.0 release candidates and never grows. Old keys stay in logs, recordings and stored
|
|
5
|
+
* configuration; this map is how the kernel reads them. `check:terms` exempts this file.
|
|
6
|
+
*/
|
|
7
|
+
export interface LegacyBuiltin {
|
|
8
|
+
/** The new name. */
|
|
9
|
+
name: BuiltinName;
|
|
10
|
+
/** Old parameter name -> new parameter name (only the renamed ones). */
|
|
11
|
+
params: Readonly<Record<string, string>>;
|
|
12
|
+
/** New parameters whose value is in milliseconds where the old one was in seconds (× 1000). */
|
|
13
|
+
msParams?: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export declare const LEGACY_BUILTINS: Readonly<Record<string, LegacyBuiltin>>;
|
|
16
|
+
/** Is `key` a release-candidate builtin name? */
|
|
17
|
+
export declare const isLegacyBuiltinKey: (key: string) => boolean;
|
|
18
|
+
/** What a connector slug / extension name can be, to read an old `<x>__<tool>` key. */
|
|
19
|
+
export interface LegacyScope {
|
|
20
|
+
connectorSlugs: ReadonlySet<string>;
|
|
21
|
+
extensionNames: ReadonlySet<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Connector id -> slug, for reading a stored definition's connector refs when a key is
|
|
24
|
+
* ambiguous (a slug that is also an extension name); optional.
|
|
25
|
+
*/
|
|
26
|
+
connectorSlugById?: ReadonlyMap<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Is `key` an rc `<x>__<tool>` key, which only a {@link LegacyScope} can map? Keys in the current
|
|
30
|
+
* format (`mcp__…`, `plugin__…`) never are.
|
|
31
|
+
*/
|
|
32
|
+
export declare const isLegacyPrefixedKey: (key: string) => boolean;
|
|
33
|
+
/**
|
|
34
|
+
* The current key of a release-candidate key: a builtin by the frozen map; `<x>__<tool>` becomes
|
|
35
|
+
* `mcp__x__tool` when `x` is only a connector slug in `scope`, `plugin__x__tool` when only an
|
|
36
|
+
* extension name, else null (ambiguous, unknown, or not an old key; a key in the current format
|
|
37
|
+
* is never mapped again).
|
|
38
|
+
*/
|
|
39
|
+
export declare function legacyToolKey(key: string, scope?: LegacyScope): string | null;
|
|
40
|
+
/** The input of an old builtin call in the current parameter names (seconds -> milliseconds). */
|
|
41
|
+
export declare function legacyToolInput(oldKey: string, input: unknown): unknown;
|
|
42
|
+
/** The model-visible hint for a call to an old builtin key; null for any other key. */
|
|
43
|
+
export declare function legacyHint(key: string): string | null;
|
|
44
|
+
/**
|
|
45
|
+
* The current key(s) an rc key stands for, to name in a refusal when a definition is saved with
|
|
46
|
+
* it: a builtin's new name; for `<x>__<tool>`, the candidates among `known` (the agent's tool
|
|
47
|
+
* keys), else both `mcp__<x>__<tool>` and `plugin__<x>__<tool>`. Empty when `key` is no rc key.
|
|
48
|
+
*/
|
|
49
|
+
export declare function legacyKeyCandidates(key: string, known?: ReadonlySet<string>): string[];
|
|
50
|
+
/** The refusal text for saving rc key `key` (null when it is none): names the new key(s). */
|
|
51
|
+
export declare function legacyKeySaveText(key: string, known?: ReadonlySet<string>): string | null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { BUILTIN } from "./builtin-names.js";
|
|
2
|
+
export const LEGACY_BUILTINS = Object.freeze({
|
|
3
|
+
sandbox_bash: {
|
|
4
|
+
name: BUILTIN.Bash,
|
|
5
|
+
params: { timeoutSeconds: "timeout" },
|
|
6
|
+
msParams: ["timeout"],
|
|
7
|
+
},
|
|
8
|
+
sandbox_read: { name: BUILTIN.Read, params: { path: "file_path" } },
|
|
9
|
+
sandbox_write: { name: BUILTIN.Write, params: { path: "file_path" } },
|
|
10
|
+
sandbox_edit: {
|
|
11
|
+
name: BUILTIN.Edit,
|
|
12
|
+
params: {
|
|
13
|
+
path: "file_path",
|
|
14
|
+
oldText: "old_string",
|
|
15
|
+
newText: "new_string",
|
|
16
|
+
replaceAll: "replace_all",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
sandbox_glob: { name: BUILTIN.Glob, params: {} },
|
|
20
|
+
sandbox_grep: { name: BUILTIN.Grep, params: { ignoreCase: "-i" } },
|
|
21
|
+
sandbox_deliver: { name: BUILTIN.Deliver, params: { path: "file_path" } },
|
|
22
|
+
skill_load: { name: BUILTIN.Skill, params: { name: "skill" } },
|
|
23
|
+
skill_write: { name: BUILTIN.SkillWrite, params: {} },
|
|
24
|
+
ask_user: { name: BUILTIN.AskUserQuestion, params: {} },
|
|
25
|
+
read_tool_output: { name: BUILTIN.ReadToolOutput, params: {} },
|
|
26
|
+
tool_search: { name: BUILTIN.ToolSearch, params: { limit: "max_results" } },
|
|
27
|
+
agent: { name: BUILTIN.Agent, params: {} },
|
|
28
|
+
});
|
|
29
|
+
/** Is `key` a release-candidate builtin name? */
|
|
30
|
+
export const isLegacyBuiltinKey = (key) => Object.hasOwn(LEGACY_BUILTINS, key);
|
|
31
|
+
/** A key in the current format for connector and extension tools (`mcp__…`, `plugin__…`). */
|
|
32
|
+
const CURRENT_PREFIXED_RE = /^(mcp|plugin)__/;
|
|
33
|
+
/** An rc connector or extension key `<x>__<tool>` (the SQL of migration 0063 uses the same test). */
|
|
34
|
+
const LEGACY_PREFIXED_RE = /^[a-z0-9][a-z0-9_-]*__./;
|
|
35
|
+
/**
|
|
36
|
+
* Is `key` an rc `<x>__<tool>` key, which only a {@link LegacyScope} can map? Keys in the current
|
|
37
|
+
* format (`mcp__…`, `plugin__…`) never are.
|
|
38
|
+
*/
|
|
39
|
+
export const isLegacyPrefixedKey = (key) => LEGACY_PREFIXED_RE.test(key) && !CURRENT_PREFIXED_RE.test(key);
|
|
40
|
+
/**
|
|
41
|
+
* The current key of a release-candidate key: a builtin by the frozen map; `<x>__<tool>` becomes
|
|
42
|
+
* `mcp__x__tool` when `x` is only a connector slug in `scope`, `plugin__x__tool` when only an
|
|
43
|
+
* extension name, else null (ambiguous, unknown, or not an old key; a key in the current format
|
|
44
|
+
* is never mapped again).
|
|
45
|
+
*/
|
|
46
|
+
export function legacyToolKey(key, scope) {
|
|
47
|
+
if (isLegacyBuiltinKey(key))
|
|
48
|
+
return LEGACY_BUILTINS[key].name;
|
|
49
|
+
if (scope === undefined || !isLegacyPrefixedKey(key))
|
|
50
|
+
return null;
|
|
51
|
+
const i = key.indexOf("__");
|
|
52
|
+
if (i <= 0 || i + 2 >= key.length)
|
|
53
|
+
return null;
|
|
54
|
+
const head = key.slice(0, i);
|
|
55
|
+
const tool = key.slice(i + 2);
|
|
56
|
+
const connector = scope.connectorSlugs.has(head);
|
|
57
|
+
const extension = scope.extensionNames.has(head);
|
|
58
|
+
if (connector === extension)
|
|
59
|
+
return null;
|
|
60
|
+
return connector ? `mcp__${head}__${tool}` : `plugin__${head}__${tool}`;
|
|
61
|
+
}
|
|
62
|
+
/** The input of an old builtin call in the current parameter names (seconds -> milliseconds). */
|
|
63
|
+
export function legacyToolInput(oldKey, input) {
|
|
64
|
+
if (!isLegacyBuiltinKey(oldKey))
|
|
65
|
+
return input;
|
|
66
|
+
if (typeof input !== "object" || input === null || Array.isArray(input))
|
|
67
|
+
return input;
|
|
68
|
+
const { params, msParams } = LEGACY_BUILTINS[oldKey];
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const [k, v] of Object.entries(input)) {
|
|
71
|
+
const next = Object.hasOwn(params, k) ? params[k] : k;
|
|
72
|
+
out[next] = msParams?.includes(next) === true && typeof v === "number" ? v * 1000 : v;
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
/** The new parameters of the builtins whose parameters were renamed (model-visible hint text). */
|
|
77
|
+
const HINT_PARAMS = {
|
|
78
|
+
Bash: "command, description, timeout in milliseconds",
|
|
79
|
+
Read: "file_path, offset, limit",
|
|
80
|
+
Write: "file_path, content",
|
|
81
|
+
Edit: "file_path, old_string, new_string, replace_all",
|
|
82
|
+
Grep: "pattern, path, glob, -i, output_mode, -n, -A, -B, -C, head_limit, offset",
|
|
83
|
+
Deliver: "file_path, name",
|
|
84
|
+
Skill: "skill, args",
|
|
85
|
+
ToolSearch: "query, max_results",
|
|
86
|
+
};
|
|
87
|
+
/** The model-visible hint for a call to an old builtin key; null for any other key. */
|
|
88
|
+
export function legacyHint(key) {
|
|
89
|
+
if (!isLegacyBuiltinKey(key))
|
|
90
|
+
return null;
|
|
91
|
+
const { name } = LEGACY_BUILTINS[key];
|
|
92
|
+
const params = HINT_PARAMS[name];
|
|
93
|
+
return `Tool "${key}" no longer exists; it is now "${name}"${params === undefined ? "" : ` (parameters: ${params})`}.`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The current key(s) an rc key stands for, to name in a refusal when a definition is saved with
|
|
97
|
+
* it: a builtin's new name; for `<x>__<tool>`, the candidates among `known` (the agent's tool
|
|
98
|
+
* keys), else both `mcp__<x>__<tool>` and `plugin__<x>__<tool>`. Empty when `key` is no rc key.
|
|
99
|
+
*/
|
|
100
|
+
export function legacyKeyCandidates(key, known) {
|
|
101
|
+
if (isLegacyBuiltinKey(key))
|
|
102
|
+
return [LEGACY_BUILTINS[key].name];
|
|
103
|
+
if (!isLegacyPrefixedKey(key))
|
|
104
|
+
return [];
|
|
105
|
+
const all = [`mcp__${key}`, `plugin__${key}`];
|
|
106
|
+
const hit = all.filter((k) => known?.has(k) === true);
|
|
107
|
+
return hit.length > 0 ? hit : all;
|
|
108
|
+
}
|
|
109
|
+
/** The refusal text for saving rc key `key` (null when it is none): names the new key(s). */
|
|
110
|
+
export function legacyKeySaveText(key, known) {
|
|
111
|
+
const c = legacyKeyCandidates(key, known);
|
|
112
|
+
if (c.length === 0)
|
|
113
|
+
return null;
|
|
114
|
+
return `"${key}" is a release-candidate tool key; use ${c.map((x) => `"${x}"`).join(" or ")}`;
|
|
115
|
+
}
|
package/dist/tools/naming.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 工具命名(Global Constraints):模型可见的工具 key 必须匹配 `^[a-zA-Z0-9_-]{1,64}$`;
|
|
3
|
-
* 插件工具为
|
|
3
|
+
* 插件工具为 `plugin__<plugin>__<tool>`,连接器工具为 `mcp__<connectorSlug>__<tool>`(拼与拆见 SDK 的 tool-key.ts),内置工具就是其名字本身。
|
|
4
4
|
* 超长或含非法字符的 key 一律报出来,不截断、不猜。
|
|
5
5
|
*/
|
|
6
6
|
export declare const TOOL_KEY_RE: RegExp;
|
|
7
7
|
export declare const TOOL_KEY_MAX = 64;
|
|
8
8
|
/** 连接器 slug:小写字母开头,小写字母 / 数字 / 下划线,最长 24。 */
|
|
9
9
|
export declare const APP_SLUG_RE: RegExp;
|
|
10
|
-
/** 前缀与工具名之间的分隔符。 */
|
|
11
|
-
export declare const KEY_SEPARATOR = "__";
|
|
12
10
|
/**
|
|
13
11
|
* slug 是否合法:除正则外,不得含连续下划线、不得以下划线结尾——否则 `a__b` + `c` 与 `a` + `b__c`
|
|
14
12
|
* 会拼出同一个 key。
|
|
@@ -21,7 +19,5 @@ export declare const SLUG_RULE = "Slug must start with a lowercase letter, conta
|
|
|
21
19
|
* 截到 24 位(再去掉尾部下划线)。推导不出合法 slug(如名称全是中文)时返回 null,由调用方要求显式给出。
|
|
22
20
|
*/
|
|
23
21
|
export declare function deriveSlug(name: string): string | null;
|
|
24
|
-
/** 拼 `<prefix>__<name>`。 */
|
|
25
|
-
export declare function toolKey(prefix: string, name: string): string;
|
|
26
22
|
/** key 不合法时返回英文原因,合法时返回 null。 */
|
|
27
23
|
export declare function toolKeyIssue(key: string): string | null;
|