@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
package/dist/tools/naming.js
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 const TOOL_KEY_RE = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
7
7
|
export const TOOL_KEY_MAX = 64;
|
|
8
8
|
/** 连接器 slug:小写字母开头,小写字母 / 数字 / 下划线,最长 24。 */
|
|
9
9
|
export const APP_SLUG_RE = /^[a-z][a-z0-9_]{0,23}$/;
|
|
10
|
-
/** 前缀与工具名之间的分隔符。 */
|
|
11
|
-
export const KEY_SEPARATOR = "__";
|
|
12
10
|
/**
|
|
13
11
|
* slug 是否合法:除正则外,不得含连续下划线、不得以下划线结尾——否则 `a__b` + `c` 与 `a` + `b__c`
|
|
14
12
|
* 会拼出同一个 key。
|
|
@@ -31,10 +29,6 @@ export function deriveSlug(name) {
|
|
|
31
29
|
.replace(/_+$/, "");
|
|
32
30
|
return isValidSlug(s) ? s : null;
|
|
33
31
|
}
|
|
34
|
-
/** 拼 `<prefix>__<name>`。 */
|
|
35
|
-
export function toolKey(prefix, name) {
|
|
36
|
-
return `${prefix}${KEY_SEPARATOR}${name}`;
|
|
37
|
-
}
|
|
38
32
|
/** key 不合法时返回英文原因,合法时返回 null。 */
|
|
39
33
|
export function toolKeyIssue(key) {
|
|
40
34
|
if (key.length > TOOL_KEY_MAX) {
|
package/dist/tools/outputs.js
CHANGED
|
@@ -194,7 +194,7 @@ export async function spillIfLarge(text, opts, save) {
|
|
|
194
194
|
return {
|
|
195
195
|
content: `${text.slice(0, opts.previewChars)}\n…\n` +
|
|
196
196
|
`[Output truncated: ${saved.sizeChars} chars, ${saved.lineCount} lines. Full output id: ${id}. ` +
|
|
197
|
-
`Use
|
|
197
|
+
`Use ReadToolOutput to page through it, or pass {"$output":"${id}"} as a tool argument.]`,
|
|
198
198
|
outputId: id,
|
|
199
199
|
};
|
|
200
200
|
}
|
package/dist/tools/pipeline.d.ts
CHANGED
|
@@ -142,8 +142,8 @@ interface PipelineDeps {
|
|
|
142
142
|
*/
|
|
143
143
|
onToolsChanged(orgId: string, connectorId: string): void;
|
|
144
144
|
/**
|
|
145
|
-
* Dispatches the sub-agent of an `
|
|
146
|
-
* it, `
|
|
145
|
+
* Dispatches the sub-agent of an `Agent` call (sub-agents spec §7.1; `dispatchSubagent`). Without
|
|
146
|
+
* it, `Agent` calls reach the builtin's guard, which refuses them.
|
|
147
147
|
*/
|
|
148
148
|
readonly subagents: {
|
|
149
149
|
dispatch(a: {
|
|
@@ -229,7 +229,7 @@ export declare class ToolPipeline {
|
|
|
229
229
|
private readonly decide;
|
|
230
230
|
private readonly deps;
|
|
231
231
|
constructor(kdb: KernelDb, eventLog: EventLog,
|
|
232
|
-
/** The `
|
|
232
|
+
/** The `Agent` tool's sub-agent dispatch; absent (tests): `Agent` calls are refused. */
|
|
233
233
|
subagents: Pick<SubagentsService, "dispatch"> | undefined, registry: Pick<ToolRegistry, "forAgent" | "findConnector" | "handler">, credentials: Pick<Credentials, "injection" | "onUnauthorized">, breaker: Pick<Breaker, "check" | "failure" | "success">, outputs: Pick<ToolOutputs, "save" | "load">, approvals: Pick<ApprovalsService, "request" | "forCall">, questions: Pick<QuestionsService, "ask" | "forCall">, config: Pick<KernelConfigService, "get">, log: Logger,
|
|
234
234
|
/** Plugin hooks and inline ui contributions; absent (tests): no hooks, cards dropped. */
|
|
235
235
|
host: Pick<ExtensionHost, "hooks" | "current"> | undefined, drift: Pick<ToolDrift, "onToolsChanged"> | undefined, txHost: TransactionHost, hooks?: ToolPipelineHooks);
|
|
@@ -242,7 +242,7 @@ export declare class ToolPipeline {
|
|
|
242
242
|
*/
|
|
243
243
|
runStep(ctx: ToolStepContext): Promise<ToolStepOutcome>;
|
|
244
244
|
/**
|
|
245
|
-
* An `
|
|
245
|
+
* An `Agent` call that was dispatched (spec §7.1 "already dispatched"): when its child run is
|
|
246
246
|
* terminal, writes the parent's `subagent_finished` (if the child's finisher did not) and the
|
|
247
247
|
* call's `tool_result` from the child's log, and returns true; else false (still open).
|
|
248
248
|
*
|
|
@@ -255,14 +255,14 @@ export declare class ToolPipeline {
|
|
|
255
255
|
/** {@link stopOpenAgentCalls} in a transaction of its own (a stop seen by the tools node). */
|
|
256
256
|
private stopOpenAgentCallsNow;
|
|
257
257
|
/**
|
|
258
|
-
* Writes, in the caller's transaction (`mandatory`), the `tool_result` of every open `
|
|
258
|
+
* Writes, in the caller's transaction (`mandatory`), the `tool_result` of every open `Agent` call
|
|
259
259
|
* of a run that is being stopped
|
|
260
260
|
* (sub-agents spec §12: a person stopped it while it waited on sub-agents, or its deadline or a
|
|
261
261
|
* stop hit its tools node after a dispatch). A call whose child already ended gets the child's
|
|
262
262
|
* result (and the missing `subagent_finished`); one whose child is still active gets
|
|
263
263
|
* {@link AGENT_STOPPED_WITH_RUN_TEXT}. The caller stops those children after its commit.
|
|
264
264
|
*
|
|
265
|
-
* Like every `
|
|
265
|
+
* Like every `Agent` result (see {@link finishAgentCall}), each is audited `tool.called` in it
|
|
266
266
|
* (duration: the child's, or the time since the dispatch) and fires `tool.postExecute`, through
|
|
267
267
|
* `fire()`, which the caller runs after its commit. Returns the steps and calls written (the
|
|
268
268
|
* caller must not write a "not executed" result for them) and the highest seq written.
|
|
@@ -280,9 +280,15 @@ export declare class ToolPipeline {
|
|
|
280
280
|
*/
|
|
281
281
|
private processCall;
|
|
282
282
|
private runCall;
|
|
283
|
+
/**
|
|
284
|
+
* Every tool key the request that issued this step's calls offered (`request.tools` plus the
|
|
285
|
+
* `catalog` keys), in any mode; null when its model_call is not found. Used to tell a call to an
|
|
286
|
+
* old key that was offered (in flight across the rename) from one that never was.
|
|
287
|
+
*/
|
|
288
|
+
private requestToolKeys;
|
|
283
289
|
/**
|
|
284
290
|
* 发出本步调用的请求里带了完整定义的工具 key:本步最后一次尝试的 `model_call.request.tools`
|
|
285
|
-
* (`assistant_message` 来自最后一次尝试)。该请求不在
|
|
291
|
+
* (`assistant_message` 来自最后一次尝试)。该请求不在 ToolSearch 模式(没有 `catalog`,工具全部发送)
|
|
286
292
|
* 或找不到 model_call 时返回 null(不做加载检查)。已加载集合由此从日志得出,不放进程内存。
|
|
287
293
|
*/
|
|
288
294
|
private sentTools;
|
package/dist/tools/pipeline.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 { ToolError, validateInputForm, validateToolDisplay, } from "@capybara-hq/plugin-sdk";
|
|
10
|
+
import { parseToolKey, ToolError, validateInputForm, validateToolDisplay, } from "@capybara-hq/plugin-sdk";
|
|
11
11
|
import { Inject, Injectable, Optional } from "@nestjs/common";
|
|
12
12
|
import { normalizeStoredDefinition } from "../agents/definition.js";
|
|
13
13
|
import { EventLog } from "../conversations/event-log.js";
|
|
@@ -32,11 +32,13 @@ import { SubagentsService } from "../runtime/subagents/subagents.service.js";
|
|
|
32
32
|
import { skillLoadOfferedIn } from "../skills/catalog.js";
|
|
33
33
|
import { ApprovalsService } from "./approvals.service.js";
|
|
34
34
|
import { Breaker, breakerOpenMessage } from "./breaker.js";
|
|
35
|
+
import { TOOL_SEARCH } from "./builtin-names.js";
|
|
35
36
|
import { AGENT_STOPPED_WITH_RUN_TEXT, AGENT_TOOL, AgentToolInput, } from "./builtins/agent.js";
|
|
36
37
|
import { isAlwaysLoaded, notLoadedText } from "./builtins/tool-search.js";
|
|
37
38
|
import { CredentialMissing, Credentials } from "./credentials.js";
|
|
38
39
|
import { ToolDrift } from "./drift.js";
|
|
39
40
|
import { ElicitationSession } from "./elicitation.js";
|
|
41
|
+
import { legacyHint, legacyToolInput, legacyToolKey } from "./legacy-keys.js";
|
|
40
42
|
import { renderResult, resolveOutputRefs, spillIfLarge, ToolOutputs } from "./outputs.js";
|
|
41
43
|
import { createDecidePolicy } from "./policy.js";
|
|
42
44
|
import { QuestionsService } from "./questions.service.js";
|
|
@@ -67,8 +69,27 @@ export const RUN_STOPPED_TEXT = "The run was stopped before this tool call was e
|
|
|
67
69
|
export const RUN_STOPPED_DURING_TEXT = "The run was stopped while this tool call was running; its outcome is unknown. Verify before retrying.";
|
|
68
70
|
const timeoutText = (key, ms) => `Tool ${key} timed out after ${ms / 1000}s. The operation may or may not have completed; verify before retrying.`;
|
|
69
71
|
const failedText = (key, errName) => `Tool ${key} failed (${errName}). The operation may not have completed; check before retrying or try another approach.`;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Is a logged call key the `Agent` tool: the current key, or the rc `agent` of a call dispatched
|
|
74
|
+
* before the rename (spec §9; a builtin maps without a scope)?
|
|
75
|
+
*/
|
|
76
|
+
const isAgentCallKey = (key) => key === AGENT_TOOL || legacyToolKey(key) === AGENT_TOOL;
|
|
77
|
+
/** The connector slugs and extension names among the run's tools (to read old `<x>__<tool>` keys). */
|
|
78
|
+
const legacyScopeOf = (tools) => {
|
|
79
|
+
const connectorSlugs = new Set();
|
|
80
|
+
const extensionNames = new Set();
|
|
81
|
+
for (const t of tools) {
|
|
82
|
+
const p = parseToolKey(t.key);
|
|
83
|
+
if (p?.kind === "connector")
|
|
84
|
+
connectorSlugs.add(p.slug);
|
|
85
|
+
else if (p?.kind === "extension")
|
|
86
|
+
extensionNames.add(p.plugin);
|
|
87
|
+
}
|
|
88
|
+
return { connectorSlugs, extensionNames };
|
|
89
|
+
};
|
|
90
|
+
const unknownToolText = (name, keys, toolSearch, hint = null) => `Unknown tool "${name}". Available tools: ${keys.length === 0 ? "(none)" : keys.join(", ")}.` +
|
|
91
|
+
(toolSearch ? " Use ToolSearch to load other tools." : "") +
|
|
92
|
+
(hint === null ? "" : ` ${hint}`);
|
|
72
93
|
const unavailableText = (name, reason) => `Tool "${name}" is unavailable: ${reason}.`;
|
|
73
94
|
const deniedText = (key) => `Tool ${key} is denied by policy.`;
|
|
74
95
|
/** Longest hook reason quoted in {@link hookDeniedText}. */
|
|
@@ -159,7 +180,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
159
180
|
decide;
|
|
160
181
|
deps;
|
|
161
182
|
constructor(kdb, eventLog,
|
|
162
|
-
/** The `
|
|
183
|
+
/** The `Agent` tool's sub-agent dispatch; absent (tests): `Agent` calls are refused. */
|
|
163
184
|
subagents, registry, credentials, breaker, outputs, approvals, questions, config, log,
|
|
164
185
|
/** Plugin hooks and inline ui contributions; absent (tests): no hooks, cards dropped. */
|
|
165
186
|
host, drift, txHost, hooks) {
|
|
@@ -213,7 +234,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
213
234
|
// 每步实时解析(连接器 / 插件状态可能在模型调用之后变化)
|
|
214
235
|
if (!resolved) {
|
|
215
236
|
// the request builder's rule: a skills catalog snapshot (even an empty one) or a person's
|
|
216
|
-
// skill invocation in the log →
|
|
237
|
+
// skill invocation in the log → Skill is offered (see skillLoadOffered)
|
|
217
238
|
const skillLoad = await skillLoadOfferedIn(this.deps.db, this.deps.eventLog, ctx.conversationId);
|
|
218
239
|
resolved = await this.deps.registry.forAgent(ctx.orgId, {
|
|
219
240
|
tools: ctx.agent.tools ?? [],
|
|
@@ -223,13 +244,13 @@ let ToolPipeline = class ToolPipeline {
|
|
|
223
244
|
}
|
|
224
245
|
return resolved;
|
|
225
246
|
};
|
|
226
|
-
// `
|
|
247
|
+
// `Agent` calls whose sub-agent still runs (spec §7.1): a dispatch never blocks later calls;
|
|
227
248
|
// once every call was handled, the step waits for them
|
|
228
249
|
let open = 0;
|
|
229
250
|
for (const call of calls) {
|
|
230
251
|
if (await eventLog.findByIdem(ctx.conversationId, idem.result(ctx.runId, call.id)))
|
|
231
252
|
continue;
|
|
232
|
-
if (call.name
|
|
253
|
+
if (isAgentCallKey(call.name)) {
|
|
233
254
|
const row = await dispatchOfCall(this.deps.db, ctx.runId, call.id);
|
|
234
255
|
if (row) {
|
|
235
256
|
if (!(await this.finishAgentCall(ctx, row, call, resolve)))
|
|
@@ -263,7 +284,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
263
284
|
return { kind: "done" };
|
|
264
285
|
}
|
|
265
286
|
/**
|
|
266
|
-
* An `
|
|
287
|
+
* An `Agent` call that was dispatched (spec §7.1 "already dispatched"): when its child run is
|
|
267
288
|
* terminal, writes the parent's `subagent_finished` (if the child's finisher did not) and the
|
|
268
289
|
* call's `tool_result` from the child's log, and returns true; else false (still open).
|
|
269
290
|
*
|
|
@@ -279,12 +300,13 @@ let ToolPipeline = class ToolPipeline {
|
|
|
279
300
|
const r = await childResult(this.deps.eventLog, row, { modelGiven });
|
|
280
301
|
if (!r)
|
|
281
302
|
return false;
|
|
282
|
-
// No spec-hash check as in processCall: an `
|
|
303
|
+
// No spec-hash check as in processCall: an `Agent` call writes no tool_call_started holding
|
|
283
304
|
// the executed definition's hash, and its description changes with the roster and limits
|
|
284
305
|
// while the child runs. The call's approval (forCall: its latest) is by construction the one
|
|
285
306
|
// that let this dispatch start, since a dispatch happens only after an approved decision.
|
|
286
307
|
const approval = await this.deps.approvals.forCall(ctx.runId, call.id);
|
|
287
|
-
|
|
308
|
+
// the result keeps the logged key (`agent` for a call dispatched before the rename, spec §9)
|
|
309
|
+
await this.writeResult(ctx, row.parentCallId, call.name, { content: r.content, isError: r.isError }, [finishedEvent(row, r.finished)], {
|
|
288
310
|
durationMs: r.finished.durationMs,
|
|
289
311
|
connectorId: null,
|
|
290
312
|
...(approval?.status === "approved" && {
|
|
@@ -316,14 +338,14 @@ let ToolPipeline = class ToolPipeline {
|
|
|
316
338
|
return this.stopOpenAgentCalls(runId, agent);
|
|
317
339
|
}
|
|
318
340
|
/**
|
|
319
|
-
* Writes, in the caller's transaction (`mandatory`), the `tool_result` of every open `
|
|
341
|
+
* Writes, in the caller's transaction (`mandatory`), the `tool_result` of every open `Agent` call
|
|
320
342
|
* of a run that is being stopped
|
|
321
343
|
* (sub-agents spec §12: a person stopped it while it waited on sub-agents, or its deadline or a
|
|
322
344
|
* stop hit its tools node after a dispatch). A call whose child already ended gets the child's
|
|
323
345
|
* result (and the missing `subagent_finished`); one whose child is still active gets
|
|
324
346
|
* {@link AGENT_STOPPED_WITH_RUN_TEXT}. The caller stops those children after its commit.
|
|
325
347
|
*
|
|
326
|
-
* Like every `
|
|
348
|
+
* Like every `Agent` result (see {@link finishAgentCall}), each is audited `tool.called` in it
|
|
327
349
|
* (duration: the child's, or the time since the dispatch) and fires `tool.postExecute`, through
|
|
328
350
|
* `fire()`, which the caller runs after its commit. Returns the steps and calls written (the
|
|
329
351
|
* caller must not write a "not executed" result for them) and the highest seq written.
|
|
@@ -373,6 +395,8 @@ let ToolPipeline = class ToolPipeline {
|
|
|
373
395
|
return none;
|
|
374
396
|
const conversationId = run.conversation_id;
|
|
375
397
|
const inputs = new Map();
|
|
398
|
+
// the logged key of each call (`agent` for a call dispatched before the rename, spec §9)
|
|
399
|
+
const names = new Map();
|
|
376
400
|
const events = [];
|
|
377
401
|
const calls = [];
|
|
378
402
|
for (const d of dispatches) {
|
|
@@ -394,8 +418,11 @@ let ToolPipeline = class ToolPipeline {
|
|
|
394
418
|
.executeTakeFirst();
|
|
395
419
|
const content = msg?.payload?.content ?? [];
|
|
396
420
|
for (const b of content) {
|
|
397
|
-
if (b.type === "tool_call" && b.id !== undefined)
|
|
421
|
+
if (b.type === "tool_call" && b.id !== undefined) {
|
|
398
422
|
inputs.set(b.id, b.input);
|
|
423
|
+
if (b.name !== undefined)
|
|
424
|
+
names.set(b.id, b.name);
|
|
425
|
+
}
|
|
399
426
|
}
|
|
400
427
|
}
|
|
401
428
|
const input = inputs.get(d.parent_call_id);
|
|
@@ -435,7 +462,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
435
462
|
payload: {
|
|
436
463
|
step: d.step,
|
|
437
464
|
callId: d.parent_call_id,
|
|
438
|
-
toolKey: AGENT_TOOL,
|
|
465
|
+
toolKey: names.get(d.parent_call_id) ?? AGENT_TOOL,
|
|
439
466
|
content: r.content,
|
|
440
467
|
isError: r.isError,
|
|
441
468
|
},
|
|
@@ -578,36 +605,48 @@ let ToolPipeline = class ToolPipeline {
|
|
|
578
605
|
await this.writeResult(ctx, call.id, key, { content, isError: true });
|
|
579
606
|
return undefined;
|
|
580
607
|
};
|
|
581
|
-
|
|
608
|
+
let tool = resolved.tools.find((t) => t.key === call.name);
|
|
609
|
+
// A call logged before the tool keys were renamed (spec 8.1): the request that issued it offered
|
|
610
|
+
// the old key. It runs as the current tool with translated input; the result keeps the logged key.
|
|
611
|
+
let legacy = false;
|
|
612
|
+
if (!tool) {
|
|
613
|
+
const next = legacyToolKey(call.name, legacyScopeOf(resolved.tools));
|
|
614
|
+
const current = next === null ? undefined : resolved.tools.find((t) => t.key === next);
|
|
615
|
+
if (current && (await this.requestToolKeys(ctx))?.has(call.name) === true) {
|
|
616
|
+
tool = current;
|
|
617
|
+
legacy = true;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
582
620
|
if (!tool) {
|
|
583
621
|
const why = resolved.unavailable.find((u) => u.ref === call.name) ??
|
|
584
622
|
resolved.hidden?.find((u) => u.ref === call.name);
|
|
585
623
|
if (why)
|
|
586
624
|
return fail(unavailableText(call.name, why.reason));
|
|
587
625
|
let keys = resolved.tools.map((t) => t.key);
|
|
588
|
-
//
|
|
626
|
+
// ToolSearch 模式:目录可能很大,只列内置工具与发出本次调用的请求里带了定义的工具
|
|
589
627
|
if (resolved.toolSearch === true) {
|
|
590
628
|
const sent = await this.sentTools(ctx);
|
|
591
629
|
if (sent !== null)
|
|
592
630
|
keys = keys.filter((k) => isAlwaysLoaded(k) || sent.has(k));
|
|
593
631
|
}
|
|
594
632
|
keys.sort();
|
|
595
|
-
return fail(unknownToolText(call.name, keys, keys.includes(
|
|
633
|
+
return fail(unknownToolText(call.name, keys, keys.includes(TOOL_SEARCH), legacyHint(call.name)));
|
|
596
634
|
}
|
|
597
|
-
|
|
598
|
-
//
|
|
599
|
-
|
|
635
|
+
const outKey = legacy ? call.name : tool.key;
|
|
636
|
+
// ToolSearch 模式下,模型只能调用请求里带了完整定义的工具:目录里有、但发出本次调用的请求没加载 →
|
|
637
|
+
// 明确错误,不自动加载(spec 5.5 / 6.3)。同一步里先 ToolSearch 再调用刚加载的工具同样报错。
|
|
638
|
+
if (!legacy && resolved.toolSearch === true && !isAlwaysLoaded(tool.key)) {
|
|
600
639
|
const sent = await this.sentTools(ctx);
|
|
601
640
|
if (sent !== null && !sent.has(tool.key))
|
|
602
|
-
return fail(notLoadedText(tool.key),
|
|
641
|
+
return fail(notLoadedText(tool.key), outKey);
|
|
603
642
|
}
|
|
604
643
|
let input;
|
|
605
644
|
try {
|
|
606
|
-
input = await resolveOutputRefs(call.input, (id) => this.deps.outputs.load({ orgId: ctx.orgId, conversationId: ctx.conversationId }, id));
|
|
645
|
+
input = await resolveOutputRefs(legacy ? legacyToolInput(call.name, call.input) : call.input, (id) => this.deps.outputs.load({ orgId: ctx.orgId, conversationId: ctx.conversationId }, id));
|
|
607
646
|
}
|
|
608
647
|
catch (e) {
|
|
609
648
|
if (e instanceof ToolError)
|
|
610
|
-
return fail(e.message,
|
|
649
|
+
return fail(e.message, outKey);
|
|
611
650
|
throw e;
|
|
612
651
|
}
|
|
613
652
|
// 连接器工具的 schema 来自连接器(不受信任):去掉有风险的关键字并限制大小 / 层数
|
|
@@ -616,7 +655,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
616
655
|
log: this.deps.log,
|
|
617
656
|
});
|
|
618
657
|
if (invalid !== null)
|
|
619
|
-
return fail(invalid,
|
|
658
|
+
return fail(invalid, outKey);
|
|
620
659
|
const decision = await this.decide(tool, ctx.agent, {
|
|
621
660
|
orgId: ctx.orgId,
|
|
622
661
|
accountId: ctx.accountId,
|
|
@@ -629,15 +668,19 @@ let ToolPipeline = class ToolPipeline {
|
|
|
629
668
|
}),
|
|
630
669
|
});
|
|
631
670
|
if (decision === "deny")
|
|
632
|
-
return fail(deniedText(tool.key),
|
|
671
|
+
return fail(deniedText(tool.key), outKey);
|
|
633
672
|
if (typeof decision === "object" && "unavailable" in decision) {
|
|
634
|
-
return fail(unavailableText(tool.key, decision.unavailable),
|
|
673
|
+
return fail(unavailableText(tool.key, decision.unavailable), outKey);
|
|
635
674
|
}
|
|
636
675
|
// 该调用已有审批(续跑)→ 按审批结果;没有且策略为 ask → 建审批并等待
|
|
637
676
|
// (不写 tool_call_started:此时尚未开始执行)。
|
|
638
677
|
let approval = await this.deps.approvals.forCall(ctx.runId, call.id);
|
|
639
678
|
// 批准的是旧定义(等待期间工具定义变了):原批准不适用于当前定义,策略仍需审批时按新定义重新审批。
|
|
640
|
-
|
|
679
|
+
// An in-flight rc call (spec §9) is the exception: its approval was asked under the rc definition
|
|
680
|
+
// of the same call, and the rename changed every builtin's definition; the person approved this
|
|
681
|
+
// very call, so the approval stands.
|
|
682
|
+
const approvedHere = approval?.status === "approved" && (legacy || approval.specHash === tool.specHash);
|
|
683
|
+
const stale = approval?.status === "approved" && !approvedHere;
|
|
641
684
|
if ((!approval || stale) && typeof decision === "object" && "ask" in decision) {
|
|
642
685
|
approval = await this.deps.approvals.request({
|
|
643
686
|
attempt: (approval?.attempt ?? 0) + 1,
|
|
@@ -657,10 +700,10 @@ let ToolPipeline = class ToolPipeline {
|
|
|
657
700
|
}
|
|
658
701
|
if (approval && approval.status !== "approved") {
|
|
659
702
|
const refused = this.refusalText(approval);
|
|
660
|
-
return refused === null ? approvalWait(approval) : fail(refused,
|
|
703
|
+
return refused === null ? approvalWait(approval) : fail(refused, outKey);
|
|
661
704
|
}
|
|
662
705
|
// 走到这里即放行:在(与当前定义一致的)审批下执行,否则是授权或策略放行
|
|
663
|
-
const authorization = approval?.status === "approved" &&
|
|
706
|
+
const authorization = approval?.status === "approved" && approvedHere
|
|
664
707
|
? approvalAuthorization(approval)
|
|
665
708
|
: typeof decision === "object" && "grant" in decision
|
|
666
709
|
? { grant: decision.grant }
|
|
@@ -675,7 +718,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
675
718
|
// written with the call's tool_result, so a call with a logged denial never runs again anyway.
|
|
676
719
|
const prior = await this.deps.eventLog.findByIdem(ctx.conversationId, idem.hookDenied(ctx.runId, call.id));
|
|
677
720
|
if (prior?.type === "hook_denied") {
|
|
678
|
-
return fail(hookDeniedText(prior.payload.plugin, prior.payload.reason),
|
|
721
|
+
return fail(hookDeniedText(prior.payload.plugin, prior.payload.reason), outKey);
|
|
679
722
|
}
|
|
680
723
|
}
|
|
681
724
|
// payload only built when a tool hook is registered (zero overhead otherwise)
|
|
@@ -697,7 +740,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
697
740
|
catch (e) {
|
|
698
741
|
// run() rejects only when the run's signal aborted: an abort, never a deny
|
|
699
742
|
if (ctx.signal.aborted)
|
|
700
|
-
return fail(RUN_STOPPED_TEXT,
|
|
743
|
+
return fail(RUN_STOPPED_TEXT, outKey);
|
|
701
744
|
throw e;
|
|
702
745
|
}
|
|
703
746
|
if (!verdict.allowed) {
|
|
@@ -715,20 +758,20 @@ let ToolPipeline = class ToolPipeline {
|
|
|
715
758
|
step: ctx.step,
|
|
716
759
|
callId: call.id,
|
|
717
760
|
});
|
|
718
|
-
await this.writeResult(ctx, call.id,
|
|
761
|
+
await this.writeResult(ctx, call.id, outKey, { content: hookDeniedText(verdict.plugin, verdict.reason), isError: true }, [event], undefined, [hookDeniedAudit(site, event.payload, verdict)]);
|
|
719
762
|
return undefined;
|
|
720
763
|
}
|
|
721
764
|
}
|
|
722
|
-
// `
|
|
765
|
+
// `Agent` (sub-agents spec §7.1): no tool_call_started; the dispatch is made durable in one
|
|
723
766
|
// transaction (its marker is the subagent_dispatches row) and the call stays open
|
|
724
767
|
const dispatcher = this.deps.subagents;
|
|
725
768
|
if (tool.source.kind === "builtin" && tool.key === AGENT_TOOL && dispatcher) {
|
|
726
769
|
const parsed = AgentToolInput.safeParse(input);
|
|
727
770
|
if (!parsed.success) {
|
|
728
|
-
return fail("Invalid agent call: pass description and prompt, and optionally subagent_type, model or resume.",
|
|
771
|
+
return fail("Invalid agent call: pass description and prompt, and optionally subagent_type, model or resume.", outKey);
|
|
729
772
|
}
|
|
730
773
|
const r = await dispatcher.dispatch({ ctx, callId: call.id, input: parsed.data });
|
|
731
|
-
return r.kind === "refused" ? fail(r.content,
|
|
774
|
+
return r.kind === "refused" ? fail(r.content, outKey) : OPEN;
|
|
732
775
|
}
|
|
733
776
|
await this.deps.eventLog.append(ctx.conversationId, [
|
|
734
777
|
{
|
|
@@ -751,7 +794,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
751
794
|
const rendered = outcome.kind === "result"
|
|
752
795
|
? renderResult(outcome.result)
|
|
753
796
|
: { text: outcome.message, contentTypes: [], isError: true };
|
|
754
|
-
// 内置工具(
|
|
797
|
+
// 内置工具(ToolSearch)报告的已加载工具:只认本步可用、非内置的 key,写进 tool_result(日志即事实源)
|
|
755
798
|
const loadedTools = outcome.kind === "result" && tool.source.kind === "builtin"
|
|
756
799
|
? loadedFrom(outcome.result, resolved.tools)
|
|
757
800
|
: [];
|
|
@@ -795,7 +838,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
795
838
|
},
|
|
796
839
|
]
|
|
797
840
|
: [];
|
|
798
|
-
await this.writeResult(ctx, call.id,
|
|
841
|
+
await this.writeResult(ctx, call.id, outKey, {
|
|
799
842
|
content: spilled.content,
|
|
800
843
|
isError: rendered.isError,
|
|
801
844
|
...(spilled.outputId !== undefined && { outputId: spilled.outputId }),
|
|
@@ -836,9 +879,24 @@ let ToolPipeline = class ToolPipeline {
|
|
|
836
879
|
}
|
|
837
880
|
return undefined;
|
|
838
881
|
}
|
|
882
|
+
/**
|
|
883
|
+
* Every tool key the request that issued this step's calls offered (`request.tools` plus the
|
|
884
|
+
* `catalog` keys), in any mode; null when its model_call is not found. Used to tell a call to an
|
|
885
|
+
* old key that was offered (in flight across the rename) from one that never was.
|
|
886
|
+
*/
|
|
887
|
+
async requestToolKeys(ctx) {
|
|
888
|
+
for (let attempt = MAX_MODEL_ATTEMPTS; attempt >= 1; attempt--) {
|
|
889
|
+
const e = await this.deps.eventLog.findByIdem(ctx.conversationId, modelCallIdemKey(ctx.runId, ctx.step, attempt));
|
|
890
|
+
if (e?.type !== "model_call")
|
|
891
|
+
continue;
|
|
892
|
+
const { tools, catalog } = e.payload.request;
|
|
893
|
+
return new Set([...(tools ?? []), ...(catalog ?? [])].map((t) => t.key));
|
|
894
|
+
}
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
839
897
|
/**
|
|
840
898
|
* 发出本步调用的请求里带了完整定义的工具 key:本步最后一次尝试的 `model_call.request.tools`
|
|
841
|
-
* (`assistant_message` 来自最后一次尝试)。该请求不在
|
|
899
|
+
* (`assistant_message` 来自最后一次尝试)。该请求不在 ToolSearch 模式(没有 `catalog`,工具全部发送)
|
|
842
900
|
* 或找不到 model_call 时返回 null(不做加载检查)。已加载集合由此从日志得出,不放进程内存。
|
|
843
901
|
*/
|
|
844
902
|
async sentTools(ctx) {
|
|
@@ -912,7 +970,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
912
970
|
const handler = this.deps.registry.handler(tool.source);
|
|
913
971
|
if (!handler)
|
|
914
972
|
return { kind: "error", message: `Tool ${tool.key} is no longer available.` };
|
|
915
|
-
// 插件处理函数只拿到 SDK 的 ToolCallContext;内置工具另外拿到本步解析出的工具(
|
|
973
|
+
// 插件处理函数只拿到 SDK 的 ToolCallContext;内置工具另外拿到本步解析出的工具(ToolSearch 用)
|
|
916
974
|
const base = {
|
|
917
975
|
orgId: ctx.orgId,
|
|
918
976
|
accountId: ctx.accountId,
|
package/dist/tools/policy.d.ts
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
1
|
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
2
2
|
import type { DecidePolicy } from "./pipeline.js";
|
|
3
3
|
import type { ResolvedTool, ToolPolicy } from "./registry.js";
|
|
4
|
-
|
|
5
|
-
* spec 3.6 R3「有权 ≠ 已确认」固定清单上的工具 key:策略至少为 `ask`,记住的授权也不放行。
|
|
6
|
-
*
|
|
7
|
-
* R3 清单:发布到公共市场、安装连接器、提插件申请、改角色与成员、删除、改共享技能、改自己的指令。
|
|
8
|
-
* M3 的内置工具(`ask_user`、`read_tool_output`、`tool_search`)与连接器工具都不在清单上;M5 T15 加入
|
|
9
|
-
* `skill_write`(改共享技能)。后续实现上述动作的内置工具时把 key 登记在这里。连接器 / 插件工具由
|
|
10
|
-
* `annotations.requiresUserInteraction` 表达同一约束。
|
|
11
|
-
*/
|
|
12
|
-
export declare const R3_FIXED_ASK_TOOL_KEYS: ReadonlySet<string>;
|
|
13
|
-
/**
|
|
14
|
-
* R3 清单里只对共享 agent 生效的 key(窄口子):R3 的条目是「改共享技能」,私有 agent(`visibility = private`,
|
|
15
|
-
* 只有负责人本人能用)的技能不是共享的,`skill_write` 直接生效、不询问(R4)。其余 R3 条目不论可见性一律固定 ask。
|
|
16
|
-
*/
|
|
17
|
-
export declare const R3_SHARED_AGENT_ONLY_TOOL_KEYS: ReadonlySet<string>;
|
|
4
|
+
export { R3_FIXED_ASK_TOOL_KEYS, R3_SHARED_AGENT_ONLY_TOOL_KEYS } from "./builtin-names.js";
|
|
18
5
|
/** 取更严的策略(`allow < ask < deny`)。 */
|
|
19
6
|
export declare function stricter(a: ToolPolicy, b: ToolPolicy | undefined): ToolPolicy;
|
|
20
7
|
export interface PolicyInput {
|
package/dist/tools/policy.js
CHANGED
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
import { sql } from "kysely";
|
|
2
2
|
import { roleAtLeast } from "../orgs/roles.js";
|
|
3
3
|
import { readOrgSettings } from "../orgs/settings.service.js";
|
|
4
|
-
import { SANDBOX_ASK_TOOL_NAMES } from "./
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* R3 清单:发布到公共市场、安装连接器、提插件申请、改角色与成员、删除、改共享技能、改自己的指令。
|
|
9
|
-
* M3 的内置工具(`ask_user`、`read_tool_output`、`tool_search`)与连接器工具都不在清单上;M5 T15 加入
|
|
10
|
-
* `skill_write`(改共享技能)。后续实现上述动作的内置工具时把 key 登记在这里。连接器 / 插件工具由
|
|
11
|
-
* `annotations.requiresUserInteraction` 表达同一约束。
|
|
12
|
-
*/
|
|
13
|
-
export const R3_FIXED_ASK_TOOL_KEYS = new Set(["skill_write"]);
|
|
14
|
-
/**
|
|
15
|
-
* R3 清单里只对共享 agent 生效的 key(窄口子):R3 的条目是「改共享技能」,私有 agent(`visibility = private`,
|
|
16
|
-
* 只有负责人本人能用)的技能不是共享的,`skill_write` 直接生效、不询问(R4)。其余 R3 条目不论可见性一律固定 ask。
|
|
17
|
-
*/
|
|
18
|
-
export const R3_SHARED_AGENT_ONLY_TOOL_KEYS = new Set(["skill_write"]);
|
|
4
|
+
import { R3_FIXED_ASK_TOOL_KEYS, R3_SHARED_AGENT_ONLY_TOOL_KEYS, SANDBOX_ASK_TOOL_NAMES, } from "./builtin-names.js";
|
|
5
|
+
import { normalizeStoredDefinitionIn } from "./legacy-data.js";
|
|
6
|
+
export { R3_FIXED_ASK_TOOL_KEYS, R3_SHARED_AGENT_ONLY_TOOL_KEYS } from "./builtin-names.js";
|
|
19
7
|
const RANK = { allow: 0, ask: 1, deny: 2 };
|
|
20
8
|
/** 取更严的策略(`allow < ask < deny`)。 */
|
|
21
9
|
export function stricter(a, b) {
|
|
@@ -122,7 +110,11 @@ export function createDecidePolicy(db) {
|
|
|
122
110
|
.select(["agent_versions.definition", "agents.visibility"])
|
|
123
111
|
.where("agents.id", "=", ctx.agentId)
|
|
124
112
|
.executeTakeFirst();
|
|
125
|
-
|
|
113
|
+
// stored raw: an rc version's keys read as current keys, so its deny keeps applying (§8.3)
|
|
114
|
+
const currentDef = current === undefined
|
|
115
|
+
? undefined
|
|
116
|
+
: await normalizeStoredDefinitionIn(db, ctx.orgId, current.definition);
|
|
117
|
+
const currentPolicies = currentDef?.toolPolicies;
|
|
126
118
|
const currentOverride = asOverride(currentPolicies?.[tool.key]);
|
|
127
119
|
let strictest = stricter(runOverride ?? "allow", currentOverride);
|
|
128
120
|
if (ctx.parentConversationId !== undefined && ctx.parentAgentDiffers !== false) {
|
|
@@ -142,9 +134,9 @@ export function createDecidePolicy(db) {
|
|
|
142
134
|
if (!roleAtLeast(m.role, tool.requiredRole))
|
|
143
135
|
escalateTo = tool.requiredRole;
|
|
144
136
|
}
|
|
145
|
-
// 可见性实时读(R5):共享 agent 的
|
|
137
|
+
// 可见性实时读(R5):共享 agent 的 SkillWrite 固定 ask,私有 agent 不问(见 R3_SHARED_AGENT_ONLY_TOOL_KEYS)
|
|
146
138
|
// 当前版本的 skills.writePolicy 同样实时读(R5):运行中改为 ask,下一次调用即生效;固定 ask 时授权不放行
|
|
147
|
-
const writePolicy =
|
|
139
|
+
const writePolicy = currentDef?.skills?.writePolicy;
|
|
148
140
|
const fixedAsk = isFixedAsk(tool, current?.visibility, writePolicy);
|
|
149
141
|
const input = {
|
|
150
142
|
connectorDefault,
|
|
@@ -203,7 +195,9 @@ async function parentAgentOverride(db, toolKey, ctx) {
|
|
|
203
195
|
.executeTakeFirst();
|
|
204
196
|
if (!row || row.id === ctx.agentId)
|
|
205
197
|
return undefined;
|
|
206
|
-
|
|
198
|
+
// stored raw: an rc version's keys read as current keys, so its deny keeps applying (§8.3)
|
|
199
|
+
const policies = (await normalizeStoredDefinitionIn(db, ctx.orgId, row.definition))
|
|
200
|
+
.toolPolicies;
|
|
207
201
|
return asOverride(policies?.[toolKey]);
|
|
208
202
|
}
|
|
209
203
|
/**
|
|
@@ -7,7 +7,7 @@ declare const AnswerBody: z.ZodObject<{
|
|
|
7
7
|
answer: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
8
8
|
}, z.core.$strict>;
|
|
9
9
|
/**
|
|
10
|
-
* 输入请求(`
|
|
10
|
+
* 输入请求(`AskUserQuestion`、插件工具的 `input_required`):`/api/v1/orgs/:orgId/questions`。
|
|
11
11
|
* 动作用 `approvals.decide`(访客起、仅限用户主体):回答必须由人做出,agent / 服务令牌不能代答;
|
|
12
12
|
* 具体是否允许由服务判定——只有该运行的发起人能读取与回答,其他成员 403。
|
|
13
13
|
*/
|
|
@@ -19,7 +19,7 @@ const AnswerBody = z.object({ answer: z.record(z.string().max(200), z.unknown())
|
|
|
19
19
|
class QuestionAnswerBody extends createZodDto(AnswerBody, "QuestionAnswerBody") {
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* 输入请求(`
|
|
22
|
+
* 输入请求(`AskUserQuestion`、插件工具的 `input_required`):`/api/v1/orgs/:orgId/questions`。
|
|
23
23
|
* 动作用 `approvals.decide`(访客起、仅限用户主体):回答必须由人做出,agent / 服务令牌不能代答;
|
|
24
24
|
* 具体是否允许由服务判定——只有该运行的发起人能读取与回答,其他成员 403。
|
|
25
25
|
*/
|
|
@@ -5,7 +5,7 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
5
5
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
6
6
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
7
7
|
export type QuestionStatus = "pending" | "answered" | "expired" | "cancelled";
|
|
8
|
-
/** 一条输入请求(`
|
|
8
|
+
/** 一条输入请求(`AskUserQuestion`、插件工具的 `input_required`、MCP elicitation 共用)。 */
|
|
9
9
|
export interface QuestionRecord {
|
|
10
10
|
id: string;
|
|
11
11
|
orgId: string;
|