@kiturone/kapibala 0.0.5 → 0.0.6
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/index.cjs +530 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +224 -8
- package/dist/index.d.ts +224 -8
- package/dist/index.js +529 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -49,6 +49,7 @@ __export(index_exports, {
|
|
|
49
49
|
OpenAIResponsesProvider: () => OpenAIResponsesProvider,
|
|
50
50
|
PathSandbox: () => PathSandbox,
|
|
51
51
|
PermissionPolicy: () => PermissionPolicy,
|
|
52
|
+
PreDispatchError: () => PreDispatchError,
|
|
52
53
|
PromptAssembler: () => PromptAssembler,
|
|
53
54
|
SESSION_SCHEMA_VERSION: () => SESSION_SCHEMA_VERSION,
|
|
54
55
|
SKILL_LIMITS: () => SKILL_LIMITS,
|
|
@@ -257,12 +258,25 @@ var KapibalaError = class extends Error {
|
|
|
257
258
|
code;
|
|
258
259
|
retryPolicy;
|
|
259
260
|
safeMessage;
|
|
261
|
+
dispatchOutcome;
|
|
260
262
|
constructor(message, options) {
|
|
261
263
|
super(message);
|
|
262
264
|
this.name = "KapibalaError";
|
|
263
265
|
this.code = options?.code ?? "INTERNAL_ERROR";
|
|
264
266
|
this.retryPolicy = options?.retryPolicy ?? "never";
|
|
265
267
|
this.safeMessage = options?.safeMessage ?? "Operation failed";
|
|
268
|
+
this.dispatchOutcome = options?.dispatchOutcome;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
var PreDispatchError = class extends KapibalaError {
|
|
272
|
+
constructor(message, options) {
|
|
273
|
+
super(message, {
|
|
274
|
+
code: options?.code ?? "PRE_DISPATCH_FAILED",
|
|
275
|
+
retryPolicy: options?.retryPolicy ?? "never",
|
|
276
|
+
safeMessage: options?.safeMessage ?? "Tool execution was aborted before dispatch",
|
|
277
|
+
dispatchOutcome: "not_sent"
|
|
278
|
+
});
|
|
279
|
+
this.name = "PreDispatchError";
|
|
266
280
|
}
|
|
267
281
|
};
|
|
268
282
|
var ModelError = class extends KapibalaError {
|
|
@@ -447,6 +461,16 @@ function validateProtocolContent(content) {
|
|
|
447
461
|
invalidState();
|
|
448
462
|
}
|
|
449
463
|
}
|
|
464
|
+
if (block.type === "tool_result" && "execution" in block && block.execution !== void 0) {
|
|
465
|
+
const ex = block.execution;
|
|
466
|
+
if (!ex || typeof ex !== "object" || ex.version !== 1 || !["not_sent", "succeeded", "failed", "unknown"].includes(ex.dispatchOutcome) || ex.hostError !== void 0 && (!ex.hostError || typeof ex.hostError !== "object" || !["validation", "projection", "hook", "audit"].includes(ex.hostError.phase) || typeof ex.hostError.code !== "string" || !ex.hostError.code))
|
|
467
|
+
invalidState();
|
|
468
|
+
}
|
|
469
|
+
if (block.type === "text" && "promptSource" in block && block.promptSource !== void 0) {
|
|
470
|
+
const ps = block.promptSource;
|
|
471
|
+
if (!ps || typeof ps !== "object" || ps.version !== 1 || typeof ps.server !== "string" || !ps.server || typeof ps.name !== "string" || !ps.name || ps.scope !== void 0 && typeof ps.scope !== "string" || ps.contentDigest !== void 0 && typeof ps.contentDigest !== "string" || ps.configIdentity !== void 0 && typeof ps.configIdentity !== "string")
|
|
472
|
+
invalidState();
|
|
473
|
+
}
|
|
450
474
|
validatedBlocks.set(block, true);
|
|
451
475
|
}
|
|
452
476
|
validatedContents.set(content, true);
|
|
@@ -564,7 +588,11 @@ function normalizeHistory(input) {
|
|
|
564
588
|
content: "Tool execution was interrupted or crashed in previous session",
|
|
565
589
|
isError: true,
|
|
566
590
|
errorCode: "OUTCOME_UNKNOWN",
|
|
567
|
-
retryPolicy: "after_user_action"
|
|
591
|
+
retryPolicy: "after_user_action",
|
|
592
|
+
execution: {
|
|
593
|
+
version: 1,
|
|
594
|
+
dispatchOutcome: "unknown"
|
|
595
|
+
}
|
|
568
596
|
};
|
|
569
597
|
content.push(repair);
|
|
570
598
|
result.repairs.push({
|
|
@@ -4848,7 +4876,8 @@ var CAPABILITIES = /* @__PURE__ */ new Set([
|
|
|
4848
4876
|
"exec",
|
|
4849
4877
|
"net:outbound",
|
|
4850
4878
|
"env:read",
|
|
4851
|
-
"agent:spawn"
|
|
4879
|
+
"agent:spawn",
|
|
4880
|
+
"external:call"
|
|
4852
4881
|
]);
|
|
4853
4882
|
function validatePermissionRules(rules) {
|
|
4854
4883
|
for (const [index2, rule] of rules.entries()) {
|
|
@@ -4961,7 +4990,8 @@ var SessionApprovalCache = class {
|
|
|
4961
4990
|
[...request.capabilities].sort(),
|
|
4962
4991
|
request.input,
|
|
4963
4992
|
request.rootDir,
|
|
4964
|
-
request.shell ?? null
|
|
4993
|
+
request.shell ?? null,
|
|
4994
|
+
request.executionIdentity ?? null
|
|
4965
4995
|
])
|
|
4966
4996
|
).digest("hex");
|
|
4967
4997
|
}
|
|
@@ -4979,9 +5009,14 @@ var ToolRegistry = class {
|
|
|
4979
5009
|
toolSources = /* @__PURE__ */ new Map();
|
|
4980
5010
|
// toolName -> source
|
|
4981
5011
|
conflictPolicy;
|
|
5012
|
+
version = 0;
|
|
4982
5013
|
constructor(options) {
|
|
4983
5014
|
this.conflictPolicy = options?.conflictPolicy ?? "error";
|
|
4984
5015
|
}
|
|
5016
|
+
/** 获取当前注册表版本号;每次有效变更(注册、替换、注销)单调自增。 */
|
|
5017
|
+
get currentVersion() {
|
|
5018
|
+
return this.version;
|
|
5019
|
+
}
|
|
4985
5020
|
register(tool, opts) {
|
|
4986
5021
|
const source = opts?.source ?? tool.metadata?.source ?? "builtin";
|
|
4987
5022
|
const existing = this.tools.get(tool.name);
|
|
@@ -4997,27 +5032,93 @@ var ToolRegistry = class {
|
|
|
4997
5032
|
}
|
|
4998
5033
|
this.tools.set(tool.name, tool);
|
|
4999
5034
|
this.toolSources.set(tool.name, source);
|
|
5035
|
+
this.version++;
|
|
5000
5036
|
}
|
|
5001
|
-
|
|
5037
|
+
/**
|
|
5038
|
+
* 原子替换指定来源的全部工具:
|
|
5039
|
+
* 1. 预检候选批内自身重名:若候选批次内包含同名工具,整批拒绝;
|
|
5040
|
+
* 2. 预检跨来源冲突:若候选工具被其它来源占用且策略不允许覆盖,整批拒绝;
|
|
5041
|
+
* 3. 同步原子提交:预检全部通过后一次性清理该来源旧工具并写入新工具;失败时完全保持旧状态不变。
|
|
5042
|
+
*/
|
|
5043
|
+
replaceSource(source, tools) {
|
|
5044
|
+
const batchNames = /* @__PURE__ */ new Set();
|
|
5045
|
+
for (const tool of tools) {
|
|
5046
|
+
if (batchNames.has(tool.name)) {
|
|
5047
|
+
throw new Error(
|
|
5048
|
+
`Invalid candidate tools: duplicate tool name '${tool.name}' within source batch '${source}'`
|
|
5049
|
+
);
|
|
5050
|
+
}
|
|
5051
|
+
batchNames.add(tool.name);
|
|
5052
|
+
}
|
|
5002
5053
|
for (const tool of tools) {
|
|
5003
|
-
this.
|
|
5054
|
+
const existing = this.tools.get(tool.name);
|
|
5055
|
+
const existingSource = this.toolSources.get(tool.name);
|
|
5056
|
+
if (existing && existingSource !== source) {
|
|
5057
|
+
if (this.conflictPolicy === "error") {
|
|
5058
|
+
throw new Error(
|
|
5059
|
+
`Tool name conflict: candidate '${tool.name}' from '${source}' conflicts with existing tool registered by '${existingSource}'`
|
|
5060
|
+
);
|
|
5061
|
+
}
|
|
5062
|
+
if (this.conflictPolicy === "prefer-builtin" && existingSource === "builtin") {
|
|
5063
|
+
throw new Error(
|
|
5064
|
+
`Tool name conflict: candidate '${tool.name}' from '${source}' cannot override builtin tool`
|
|
5065
|
+
);
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5004
5068
|
}
|
|
5069
|
+
for (const [name, s] of this.toolSources.entries()) {
|
|
5070
|
+
if (s === source) {
|
|
5071
|
+
this.tools.delete(name);
|
|
5072
|
+
this.toolSources.delete(name);
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
for (const tool of tools) {
|
|
5076
|
+
this.tools.set(tool.name, tool);
|
|
5077
|
+
this.toolSources.set(tool.name, source);
|
|
5078
|
+
}
|
|
5079
|
+
this.version++;
|
|
5080
|
+
}
|
|
5081
|
+
/**
|
|
5082
|
+
* 注册指定来源的工具集。
|
|
5083
|
+
*
|
|
5084
|
+
* 语义与约束:
|
|
5085
|
+
* 1. 委托给 `replaceSource` 实现整源原子替换(新集合未包含的该来源旧工具将被注销移除,非增量累加);
|
|
5086
|
+
* 2. 默认禁止跨来源名称冲突(抛出 KapibalaError TOOL_CONFLICT);若 options 配置了 conflictPolicy: 'prefer-last' 则允许覆盖旧来源同名工具;
|
|
5087
|
+
* 3. 预检阶段发现批内同名重复时整批拒绝,绝不破坏注册表原子状态。
|
|
5088
|
+
*/
|
|
5089
|
+
registerSource(source, tools) {
|
|
5090
|
+
this.replaceSource(source, tools);
|
|
5005
5091
|
}
|
|
5006
5092
|
unregisterSource(source) {
|
|
5093
|
+
let changed = false;
|
|
5007
5094
|
for (const [name, s] of this.toolSources.entries()) {
|
|
5008
5095
|
if (s === source) {
|
|
5009
5096
|
this.tools.delete(name);
|
|
5010
5097
|
this.toolSources.delete(name);
|
|
5098
|
+
changed = true;
|
|
5011
5099
|
}
|
|
5012
5100
|
}
|
|
5101
|
+
if (changed) {
|
|
5102
|
+
this.version++;
|
|
5103
|
+
}
|
|
5013
5104
|
}
|
|
5014
5105
|
get(name) {
|
|
5015
5106
|
return this.tools.get(name);
|
|
5016
5107
|
}
|
|
5108
|
+
has(name) {
|
|
5109
|
+
return this.tools.has(name);
|
|
5110
|
+
}
|
|
5111
|
+
/** 获取工具对应的来源标记。 */
|
|
5112
|
+
sourceOf(name) {
|
|
5113
|
+
return this.toolSources.get(name);
|
|
5114
|
+
}
|
|
5017
5115
|
/** 注销指定名称及来源记录;调用方须核对实例身份,不能移除同名替代工具。 */
|
|
5018
5116
|
unregister(name) {
|
|
5019
|
-
this.tools.
|
|
5020
|
-
|
|
5117
|
+
if (this.tools.has(name)) {
|
|
5118
|
+
this.tools.delete(name);
|
|
5119
|
+
this.toolSources.delete(name);
|
|
5120
|
+
this.version++;
|
|
5121
|
+
}
|
|
5021
5122
|
}
|
|
5022
5123
|
/** 未命中时抛 ToolNotFound,并携带当前全部可用名称辅助模型自纠。 */
|
|
5023
5124
|
resolve(name) {
|
|
@@ -5033,6 +5134,30 @@ var ToolRegistry = class {
|
|
|
5033
5134
|
definitions() {
|
|
5034
5135
|
return this.list().map(toToolDefinition);
|
|
5035
5136
|
}
|
|
5137
|
+
/**
|
|
5138
|
+
* 捕获当前注册表的不可变只读快照。
|
|
5139
|
+
* 注册表后续的增删改绝不改变该快照的定义列表、工具执行实例与来源版本。
|
|
5140
|
+
*/
|
|
5141
|
+
snapshot() {
|
|
5142
|
+
const snapshotTools = new Map(this.tools);
|
|
5143
|
+
const snapshotSources = new Map(this.toolSources);
|
|
5144
|
+
const snapshotVersion = this.version;
|
|
5145
|
+
return {
|
|
5146
|
+
version: snapshotVersion,
|
|
5147
|
+
get: (name) => snapshotTools.get(name),
|
|
5148
|
+
has: (name) => snapshotTools.has(name),
|
|
5149
|
+
resolve: (name) => {
|
|
5150
|
+
const tool = snapshotTools.get(name);
|
|
5151
|
+
if (!tool) {
|
|
5152
|
+
throw new ToolNotFound(name, Array.from(snapshotTools.keys()));
|
|
5153
|
+
}
|
|
5154
|
+
return tool;
|
|
5155
|
+
},
|
|
5156
|
+
list: () => Array.from(snapshotTools.values()),
|
|
5157
|
+
definitions: () => Array.from(snapshotTools.values()).map(toToolDefinition),
|
|
5158
|
+
sourceOf: (name) => snapshotSources.get(name)
|
|
5159
|
+
};
|
|
5160
|
+
}
|
|
5036
5161
|
};
|
|
5037
5162
|
|
|
5038
5163
|
// src/capabilities/tools/builtin/fs.ts
|
|
@@ -5508,14 +5633,18 @@ var ToolExecutor = class {
|
|
|
5508
5633
|
call.executionRoot = import_node_path15.default.resolve(this.rootDir);
|
|
5509
5634
|
const startedAt = Date.now();
|
|
5510
5635
|
const operationId = createLogId();
|
|
5511
|
-
const failure = (message, errorCode, retryPolicy = "never") => ({
|
|
5636
|
+
const failure = (message, errorCode, retryPolicy = "never", execution) => ({
|
|
5512
5637
|
type: "tool_result",
|
|
5513
5638
|
toolUseId: call.id,
|
|
5514
5639
|
content: JSON.stringify({ code: errorCode, retryPolicy, message }),
|
|
5515
5640
|
isError: true,
|
|
5516
5641
|
errorCode,
|
|
5517
5642
|
retryPolicy,
|
|
5518
|
-
durationMs: Math.max(0, Date.now() - startedAt)
|
|
5643
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
5644
|
+
execution: execution ?? {
|
|
5645
|
+
version: 1,
|
|
5646
|
+
dispatchOutcome: "not_sent"
|
|
5647
|
+
}
|
|
5519
5648
|
});
|
|
5520
5649
|
const audit = async (event, fields = {}) => {
|
|
5521
5650
|
if (!this.eventLogger || !this.sessionId) return;
|
|
@@ -5535,7 +5664,12 @@ var ToolExecutor = class {
|
|
|
5535
5664
|
return failure(
|
|
5536
5665
|
"Audit unavailable; tool was not started",
|
|
5537
5666
|
"AUDIT_UNAVAILABLE",
|
|
5538
|
-
"after_user_action"
|
|
5667
|
+
"after_user_action",
|
|
5668
|
+
{
|
|
5669
|
+
version: 1,
|
|
5670
|
+
dispatchOutcome: "not_sent",
|
|
5671
|
+
hostError: { phase: "audit", code: "AUDIT_UNAVAILABLE" }
|
|
5672
|
+
}
|
|
5539
5673
|
);
|
|
5540
5674
|
}
|
|
5541
5675
|
if (forcedDeny) {
|
|
@@ -5546,7 +5680,12 @@ var ToolExecutor = class {
|
|
|
5546
5680
|
return failure(
|
|
5547
5681
|
"Audit unavailable; tool was not started",
|
|
5548
5682
|
"AUDIT_UNAVAILABLE",
|
|
5549
|
-
"after_user_action"
|
|
5683
|
+
"after_user_action",
|
|
5684
|
+
{
|
|
5685
|
+
version: 1,
|
|
5686
|
+
dispatchOutcome: "not_sent",
|
|
5687
|
+
hostError: { phase: "audit", code: "AUDIT_UNAVAILABLE" }
|
|
5688
|
+
}
|
|
5550
5689
|
);
|
|
5551
5690
|
}
|
|
5552
5691
|
return failure(
|
|
@@ -5569,7 +5708,11 @@ var ToolExecutor = class {
|
|
|
5569
5708
|
toolUseId: call.id,
|
|
5570
5709
|
content: decision.result,
|
|
5571
5710
|
isError: decision.isError ?? false,
|
|
5572
|
-
durationMs: Math.max(0, Date.now() - startedAt)
|
|
5711
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
5712
|
+
execution: {
|
|
5713
|
+
version: 1,
|
|
5714
|
+
dispatchOutcome: "not_sent"
|
|
5715
|
+
}
|
|
5573
5716
|
};
|
|
5574
5717
|
}
|
|
5575
5718
|
}
|
|
@@ -5579,8 +5722,23 @@ var ToolExecutor = class {
|
|
|
5579
5722
|
await audit("tool.finished", { status: "failed" });
|
|
5580
5723
|
} catch {
|
|
5581
5724
|
}
|
|
5582
|
-
return failure("Tool preparation failed", "HOOK_ERROR"
|
|
5725
|
+
return failure("Tool preparation failed", "HOOK_ERROR", "never", {
|
|
5726
|
+
version: 1,
|
|
5727
|
+
dispatchOutcome: "not_sent",
|
|
5728
|
+
hostError: { phase: "hook", code: "HOOK_ERROR" }
|
|
5729
|
+
});
|
|
5583
5730
|
}
|
|
5731
|
+
function deepFreeze(obj) {
|
|
5732
|
+
if (obj === null || typeof obj !== "object" || Object.isFrozen(obj)) {
|
|
5733
|
+
return obj;
|
|
5734
|
+
}
|
|
5735
|
+
Object.freeze(obj);
|
|
5736
|
+
for (const key of Object.keys(obj)) {
|
|
5737
|
+
deepFreeze(obj[key]);
|
|
5738
|
+
}
|
|
5739
|
+
return obj;
|
|
5740
|
+
}
|
|
5741
|
+
const finalInput = deepFreeze(structuredClone(currentInput));
|
|
5584
5742
|
let tool;
|
|
5585
5743
|
try {
|
|
5586
5744
|
tool = this.tools.resolve(call.name);
|
|
@@ -5593,13 +5751,50 @@ var ToolExecutor = class {
|
|
|
5593
5751
|
}
|
|
5594
5752
|
return failure(
|
|
5595
5753
|
known?.safeMessage ?? "Requested tool is unavailable",
|
|
5596
|
-
known?.code ?? "TOOL_NOT_FOUND"
|
|
5754
|
+
known?.code ?? "TOOL_NOT_FOUND",
|
|
5755
|
+
"never",
|
|
5756
|
+
{
|
|
5757
|
+
version: 1,
|
|
5758
|
+
dispatchOutcome: "not_sent"
|
|
5759
|
+
}
|
|
5597
5760
|
);
|
|
5598
5761
|
}
|
|
5762
|
+
try {
|
|
5763
|
+
const validation = tool.validateInput?.(finalInput);
|
|
5764
|
+
if (validation && !validation.valid) {
|
|
5765
|
+
try {
|
|
5766
|
+
await audit("tool.decided", { decision: "deny", decisionSource: "validation" });
|
|
5767
|
+
await audit("tool.finished", { status: "failed", errorCode: "INVALID_INPUT" });
|
|
5768
|
+
} catch {
|
|
5769
|
+
}
|
|
5770
|
+
return failure(
|
|
5771
|
+
validation.message || "Tool input validation failed",
|
|
5772
|
+
"INVALID_INPUT",
|
|
5773
|
+
"never",
|
|
5774
|
+
{
|
|
5775
|
+
version: 1,
|
|
5776
|
+
dispatchOutcome: "not_sent",
|
|
5777
|
+
hostError: { phase: "validation", code: "INVALID_INPUT" }
|
|
5778
|
+
}
|
|
5779
|
+
);
|
|
5780
|
+
}
|
|
5781
|
+
} catch (err) {
|
|
5782
|
+
try {
|
|
5783
|
+
await audit("tool.decided", { decision: "deny", decisionSource: "validation" });
|
|
5784
|
+
await audit("tool.finished", { status: "failed", errorCode: "INVALID_INPUT" });
|
|
5785
|
+
} catch {
|
|
5786
|
+
}
|
|
5787
|
+
const msg = err instanceof Error ? err.message : "Tool input validation error";
|
|
5788
|
+
return failure(msg || "Tool input validation error", "INVALID_INPUT", "never", {
|
|
5789
|
+
version: 1,
|
|
5790
|
+
dispatchOutcome: "not_sent",
|
|
5791
|
+
hostError: { phase: "validation", code: "INVALID_INPUT" }
|
|
5792
|
+
});
|
|
5793
|
+
}
|
|
5599
5794
|
try {
|
|
5600
5795
|
return await this.executeResolved(
|
|
5601
5796
|
call,
|
|
5602
|
-
|
|
5797
|
+
finalInput,
|
|
5603
5798
|
tool,
|
|
5604
5799
|
startedAt,
|
|
5605
5800
|
audit,
|
|
@@ -5615,14 +5810,18 @@ var ToolExecutor = class {
|
|
|
5615
5810
|
return failure(
|
|
5616
5811
|
"Tool outcome is unknown after an executor failure",
|
|
5617
5812
|
"OUTCOME_UNKNOWN",
|
|
5618
|
-
"after_user_action"
|
|
5813
|
+
"after_user_action",
|
|
5814
|
+
{
|
|
5815
|
+
version: 1,
|
|
5816
|
+
dispatchOutcome: "unknown"
|
|
5817
|
+
}
|
|
5619
5818
|
);
|
|
5620
5819
|
}
|
|
5621
5820
|
}
|
|
5622
|
-
async executeResolved(call,
|
|
5821
|
+
async executeResolved(call, finalInput, tool, startedAt, audit, failure, hookCtx, onProgress) {
|
|
5623
5822
|
let shell;
|
|
5624
5823
|
try {
|
|
5625
|
-
shell = tool.approvalScope?.(
|
|
5824
|
+
shell = tool.approvalScope?.(finalInput, this.rootDir);
|
|
5626
5825
|
} catch {
|
|
5627
5826
|
try {
|
|
5628
5827
|
await audit("tool.decided", { decision: "deny", decisionSource: "invalid_target" });
|
|
@@ -5631,19 +5830,32 @@ var ToolExecutor = class {
|
|
|
5631
5830
|
}
|
|
5632
5831
|
return failure("Invalid tool execution target", "INVALID_TARGET");
|
|
5633
5832
|
}
|
|
5833
|
+
let executionIdentity;
|
|
5834
|
+
try {
|
|
5835
|
+
executionIdentity = tool.executionIdentity?.(finalInput, this.rootDir);
|
|
5836
|
+
} catch {
|
|
5837
|
+
try {
|
|
5838
|
+
await audit("tool.decided", { decision: "deny", decisionSource: "invalid_identity" });
|
|
5839
|
+
await audit("tool.finished", { status: "failed", errorCode: "INVALID_TARGET" });
|
|
5840
|
+
} catch {
|
|
5841
|
+
}
|
|
5842
|
+
return failure("Invalid tool execution identity", "INVALID_TARGET");
|
|
5843
|
+
}
|
|
5634
5844
|
let auditFields = {};
|
|
5635
5845
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
5636
5846
|
const request = {
|
|
5637
5847
|
toolName: call.name,
|
|
5638
5848
|
capabilities: tool.metadata?.permissions ?? [],
|
|
5639
|
-
input:
|
|
5849
|
+
input: finalInput,
|
|
5640
5850
|
rootDir: this.rootDir,
|
|
5641
5851
|
...shell ? { shell } : {},
|
|
5852
|
+
...executionIdentity ? { executionIdentity } : {},
|
|
5642
5853
|
// 解释器内建查询与内容已绑定摘要的工作区直引脚本可确认不受外部脚本变更影响,
|
|
5643
5854
|
// 允许会话级批准;其余 shell 命令仅允许单次批准。
|
|
5855
|
+
// 非 shell 工具(含深度绑定 executionIdentity 的外部工具)允许会话级批准。
|
|
5644
5856
|
sessionAllowed: !shell || /^(?:pwd|get-location)$/i.test(shell.command.trim()) || shell.scriptDigest !== void 0
|
|
5645
5857
|
};
|
|
5646
|
-
const repeatKey = (0, import_node_crypto14.createHash)("sha256").update(stableJsonStringify([call.name,
|
|
5858
|
+
const repeatKey = (0, import_node_crypto14.createHash)("sha256").update(stableJsonStringify([call.name, finalInput])).digest("hex");
|
|
5647
5859
|
if (this.neverFailures.has(repeatKey)) {
|
|
5648
5860
|
try {
|
|
5649
5861
|
await audit("tool.decided", { decision: "deny", decisionSource: "repeat_block" });
|
|
@@ -5652,7 +5864,12 @@ var ToolExecutor = class {
|
|
|
5652
5864
|
return failure(
|
|
5653
5865
|
"Audit unavailable; tool was not started",
|
|
5654
5866
|
"AUDIT_UNAVAILABLE",
|
|
5655
|
-
"after_user_action"
|
|
5867
|
+
"after_user_action",
|
|
5868
|
+
{
|
|
5869
|
+
version: 1,
|
|
5870
|
+
dispatchOutcome: "not_sent",
|
|
5871
|
+
hostError: { phase: "audit", code: "AUDIT_UNAVAILABLE" }
|
|
5872
|
+
}
|
|
5656
5873
|
);
|
|
5657
5874
|
}
|
|
5658
5875
|
return failure(
|
|
@@ -5660,7 +5877,7 @@ var ToolExecutor = class {
|
|
|
5660
5877
|
"REPEAT_BLOCKED"
|
|
5661
5878
|
);
|
|
5662
5879
|
}
|
|
5663
|
-
const hardDenied = this.hardDenial?.(tool,
|
|
5880
|
+
const hardDenied = this.hardDenial?.(tool, finalInput);
|
|
5664
5881
|
const cached = this.approvalCache.get(request);
|
|
5665
5882
|
const decision = this.policy.decide({
|
|
5666
5883
|
hardDenied: hardDenied !== void 0,
|
|
@@ -5696,6 +5913,11 @@ var ToolExecutor = class {
|
|
|
5696
5913
|
executableDigest: shell.executableDigest,
|
|
5697
5914
|
executionFingerprint: shell.executionFingerprint,
|
|
5698
5915
|
...commandAuditSummary(shell.command)
|
|
5916
|
+
} : executionIdentity ? {
|
|
5917
|
+
serverIdentity: executionIdentity.serverIdentity,
|
|
5918
|
+
connectionGeneration: executionIdentity.connectionGeneration,
|
|
5919
|
+
catalogEpoch: executionIdentity.catalogEpoch,
|
|
5920
|
+
toolDigest: executionIdentity.toolDigest
|
|
5699
5921
|
} : {};
|
|
5700
5922
|
try {
|
|
5701
5923
|
await audit("tool.decided", {
|
|
@@ -5709,7 +5931,12 @@ var ToolExecutor = class {
|
|
|
5709
5931
|
return failure(
|
|
5710
5932
|
"Audit unavailable; tool was not started",
|
|
5711
5933
|
"AUDIT_UNAVAILABLE",
|
|
5712
|
-
"after_user_action"
|
|
5934
|
+
"after_user_action",
|
|
5935
|
+
{
|
|
5936
|
+
version: 1,
|
|
5937
|
+
dispatchOutcome: "not_sent",
|
|
5938
|
+
hostError: { phase: "audit", code: "AUDIT_UNAVAILABLE" }
|
|
5939
|
+
}
|
|
5713
5940
|
);
|
|
5714
5941
|
}
|
|
5715
5942
|
if (!permitted || this.signal?.aborted) {
|
|
@@ -5717,7 +5944,11 @@ var ToolExecutor = class {
|
|
|
5717
5944
|
try {
|
|
5718
5945
|
await audit("tool.finished", { status: "denied", errorCode: code });
|
|
5719
5946
|
} catch {
|
|
5720
|
-
return failure("Audit result unavailable", "AUDIT_UNAVAILABLE", "after_user_action"
|
|
5947
|
+
return failure("Audit result unavailable", "AUDIT_UNAVAILABLE", "after_user_action", {
|
|
5948
|
+
version: 1,
|
|
5949
|
+
dispatchOutcome: "not_sent",
|
|
5950
|
+
hostError: { phase: "audit", code: "AUDIT_UNAVAILABLE" }
|
|
5951
|
+
});
|
|
5721
5952
|
}
|
|
5722
5953
|
return failure(
|
|
5723
5954
|
this.signal?.aborted ? "Operation cancelled" : hardDenied?.message ?? "Tool permission denied",
|
|
@@ -5727,7 +5958,7 @@ var ToolExecutor = class {
|
|
|
5727
5958
|
}
|
|
5728
5959
|
if (shell) {
|
|
5729
5960
|
try {
|
|
5730
|
-
const next = tool.approvalScope?.(
|
|
5961
|
+
const next = tool.approvalScope?.(finalInput, this.rootDir);
|
|
5731
5962
|
if (next?.executionFingerprint !== shell.executionFingerprint) {
|
|
5732
5963
|
if (attempt === 0 && next) {
|
|
5733
5964
|
try {
|
|
@@ -5736,7 +5967,12 @@ var ToolExecutor = class {
|
|
|
5736
5967
|
return failure(
|
|
5737
5968
|
"Audit unavailable; tool was not started",
|
|
5738
5969
|
"AUDIT_UNAVAILABLE",
|
|
5739
|
-
"after_user_action"
|
|
5970
|
+
"after_user_action",
|
|
5971
|
+
{
|
|
5972
|
+
version: 1,
|
|
5973
|
+
dispatchOutcome: "not_sent",
|
|
5974
|
+
hostError: { phase: "audit", code: "AUDIT_UNAVAILABLE" }
|
|
5975
|
+
}
|
|
5740
5976
|
);
|
|
5741
5977
|
}
|
|
5742
5978
|
shell = next;
|
|
@@ -5757,6 +5993,37 @@ var ToolExecutor = class {
|
|
|
5757
5993
|
);
|
|
5758
5994
|
}
|
|
5759
5995
|
}
|
|
5996
|
+
if (executionIdentity) {
|
|
5997
|
+
try {
|
|
5998
|
+
const nextIdentity = tool.executionIdentity?.(finalInput, this.rootDir);
|
|
5999
|
+
if (!nextIdentity || nextIdentity.serverIdentity !== executionIdentity.serverIdentity || nextIdentity.connectionGeneration !== executionIdentity.connectionGeneration || nextIdentity.catalogEpoch !== executionIdentity.catalogEpoch || nextIdentity.toolDigest !== executionIdentity.toolDigest) {
|
|
6000
|
+
await audit("tool.finished", {
|
|
6001
|
+
status: "identity_changed",
|
|
6002
|
+
errorCode: "MCP_TOOL_CHANGED"
|
|
6003
|
+
});
|
|
6004
|
+
return failure(
|
|
6005
|
+
"Tool execution identity changed after approval",
|
|
6006
|
+
"MCP_TOOL_CHANGED",
|
|
6007
|
+
"after_user_action",
|
|
6008
|
+
{
|
|
6009
|
+
version: 1,
|
|
6010
|
+
dispatchOutcome: "not_sent",
|
|
6011
|
+
hostError: { phase: "validation", code: "MCP_TOOL_CHANGED" }
|
|
6012
|
+
}
|
|
6013
|
+
);
|
|
6014
|
+
}
|
|
6015
|
+
} catch {
|
|
6016
|
+
return failure(
|
|
6017
|
+
"Tool execution identity check failed after approval",
|
|
6018
|
+
"MCP_TOOL_CHANGED",
|
|
6019
|
+
"after_user_action",
|
|
6020
|
+
{
|
|
6021
|
+
version: 1,
|
|
6022
|
+
dispatchOutcome: "not_sent"
|
|
6023
|
+
}
|
|
6024
|
+
);
|
|
6025
|
+
}
|
|
6026
|
+
}
|
|
5760
6027
|
break;
|
|
5761
6028
|
}
|
|
5762
6029
|
try {
|
|
@@ -5765,13 +6032,19 @@ var ToolExecutor = class {
|
|
|
5765
6032
|
return failure(
|
|
5766
6033
|
"Audit unavailable; tool was not started",
|
|
5767
6034
|
"AUDIT_UNAVAILABLE",
|
|
5768
|
-
"after_user_action"
|
|
6035
|
+
"after_user_action",
|
|
6036
|
+
{
|
|
6037
|
+
version: 1,
|
|
6038
|
+
dispatchOutcome: "not_sent",
|
|
6039
|
+
hostError: { phase: "audit", code: "AUDIT_UNAVAILABLE" }
|
|
6040
|
+
}
|
|
5769
6041
|
);
|
|
5770
6042
|
}
|
|
5771
6043
|
let outputStr;
|
|
5772
6044
|
let isError = false;
|
|
5773
6045
|
let errorCode;
|
|
5774
6046
|
let retryPolicy;
|
|
6047
|
+
let execution;
|
|
5775
6048
|
try {
|
|
5776
6049
|
const executionController = new AbortController();
|
|
5777
6050
|
const abortFromSession = () => executionController.abort();
|
|
@@ -5790,16 +6063,52 @@ var ToolExecutor = class {
|
|
|
5790
6063
|
} : {}
|
|
5791
6064
|
};
|
|
5792
6065
|
try {
|
|
5793
|
-
const rawResult = tool.metadata?.managesTimeout ? await tool.execute(
|
|
5794
|
-
tool.execute(
|
|
6066
|
+
const rawResult = tool.metadata?.managesTimeout ? await tool.execute(finalInput, toolCtx) : await this.withTimeout(
|
|
6067
|
+
tool.execute(finalInput, toolCtx),
|
|
5795
6068
|
call.name,
|
|
5796
6069
|
executionController
|
|
5797
6070
|
);
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
6071
|
+
if (typeof tool.toExecutionResult === "function") {
|
|
6072
|
+
try {
|
|
6073
|
+
const converted = tool.toExecutionResult(rawResult);
|
|
6074
|
+
outputStr = converted.content;
|
|
6075
|
+
isError = converted.isError ?? false;
|
|
6076
|
+
errorCode = converted.errorCode;
|
|
6077
|
+
retryPolicy = converted.retryPolicy;
|
|
6078
|
+
execution = converted.execution ?? {
|
|
6079
|
+
version: 1,
|
|
6080
|
+
dispatchOutcome: isError ? "failed" : "succeeded"
|
|
6081
|
+
};
|
|
6082
|
+
} catch (convErr) {
|
|
6083
|
+
isError = true;
|
|
6084
|
+
errorCode = "MCP_RESULT_INVALID";
|
|
6085
|
+
retryPolicy = "after_user_action";
|
|
6086
|
+
outputStr = convErr instanceof Error ? convErr.message : "Tool result projection failed";
|
|
6087
|
+
execution = {
|
|
6088
|
+
version: 1,
|
|
6089
|
+
dispatchOutcome: "succeeded",
|
|
6090
|
+
hostError: {
|
|
6091
|
+
phase: "projection",
|
|
6092
|
+
code: "MCP_RESULT_INVALID"
|
|
6093
|
+
}
|
|
6094
|
+
};
|
|
6095
|
+
}
|
|
6096
|
+
} else {
|
|
6097
|
+
outputStr = typeof rawResult === "string" ? rawResult : JSON.stringify(rawResult, null, 2);
|
|
6098
|
+
if (call.name === "run_command" && typeof rawResult === "object" && rawResult !== null && "exitCode" in rawResult && rawResult.exitCode !== 0) {
|
|
6099
|
+
isError = true;
|
|
6100
|
+
errorCode = "COMMAND_EXIT_NONZERO";
|
|
6101
|
+
retryPolicy = "never";
|
|
6102
|
+
execution = {
|
|
6103
|
+
version: 1,
|
|
6104
|
+
dispatchOutcome: "failed"
|
|
6105
|
+
};
|
|
6106
|
+
} else {
|
|
6107
|
+
execution = {
|
|
6108
|
+
version: 1,
|
|
6109
|
+
dispatchOutcome: "succeeded"
|
|
6110
|
+
};
|
|
6111
|
+
}
|
|
5803
6112
|
}
|
|
5804
6113
|
} finally {
|
|
5805
6114
|
this.signal?.removeEventListener("abort", abortFromSession);
|
|
@@ -5810,13 +6119,19 @@ var ToolExecutor = class {
|
|
|
5810
6119
|
outputStr = known?.safeMessage ?? "Tool execution failed";
|
|
5811
6120
|
errorCode = known?.code ?? "TOOL_FAILED";
|
|
5812
6121
|
retryPolicy = known?.retryPolicy ?? "never";
|
|
6122
|
+
const outcome = known?.dispatchOutcome ?? (errorCode === "OUTCOME_UNKNOWN" || known && known.retryPolicy === "after_user_action" ? "unknown" : "failed");
|
|
6123
|
+
execution = {
|
|
6124
|
+
version: 1,
|
|
6125
|
+
dispatchOutcome: outcome
|
|
6126
|
+
};
|
|
5813
6127
|
}
|
|
5814
6128
|
const resultBlock = {
|
|
5815
6129
|
type: "tool_result",
|
|
5816
6130
|
toolUseId: call.id,
|
|
5817
6131
|
content: outputStr,
|
|
5818
6132
|
isError,
|
|
5819
|
-
...errorCode ? { errorCode, retryPolicy } : {}
|
|
6133
|
+
...errorCode ? { errorCode, retryPolicy } : {},
|
|
6134
|
+
execution
|
|
5820
6135
|
};
|
|
5821
6136
|
if (resultBlock.isError && resultBlock.errorCode) {
|
|
5822
6137
|
resultBlock.content = JSON.stringify({
|
|
@@ -5829,7 +6144,7 @@ var ToolExecutor = class {
|
|
|
5829
6144
|
for (const hook of this.hooks.get("tool:after")) {
|
|
5830
6145
|
await hook(
|
|
5831
6146
|
hookCtx,
|
|
5832
|
-
{ id: call.id, name: call.name, input:
|
|
6147
|
+
{ id: call.id, name: call.name, input: finalInput },
|
|
5833
6148
|
{ output: outputStr, isError }
|
|
5834
6149
|
);
|
|
5835
6150
|
}
|
|
@@ -5842,11 +6157,19 @@ var ToolExecutor = class {
|
|
|
5842
6157
|
resultBlock.isError = true;
|
|
5843
6158
|
resultBlock.errorCode = "HOOK_ERROR";
|
|
5844
6159
|
resultBlock.retryPolicy = "after_user_action";
|
|
6160
|
+
resultBlock.execution = {
|
|
6161
|
+
version: 1,
|
|
6162
|
+
dispatchOutcome: resultBlock.execution?.dispatchOutcome ?? "failed",
|
|
6163
|
+
hostError: {
|
|
6164
|
+
phase: "hook",
|
|
6165
|
+
code: "HOOK_ERROR"
|
|
6166
|
+
}
|
|
6167
|
+
};
|
|
5845
6168
|
}
|
|
5846
6169
|
resultBlock.durationMs = Math.max(0, Date.now() - startedAt);
|
|
5847
6170
|
if (resultBlock.isError && resultBlock.retryPolicy === "never") {
|
|
5848
6171
|
this.neverFailures.add(
|
|
5849
|
-
(0, import_node_crypto14.createHash)("sha256").update(stableJsonStringify([call.name,
|
|
6172
|
+
(0, import_node_crypto14.createHash)("sha256").update(stableJsonStringify([call.name, finalInput])).digest("hex")
|
|
5850
6173
|
);
|
|
5851
6174
|
}
|
|
5852
6175
|
try {
|
|
@@ -5856,13 +6179,25 @@ var ToolExecutor = class {
|
|
|
5856
6179
|
durationMs: resultBlock.durationMs
|
|
5857
6180
|
});
|
|
5858
6181
|
} catch {
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
6182
|
+
resultBlock.isError = true;
|
|
6183
|
+
resultBlock.errorCode = "OUTCOME_UNKNOWN";
|
|
6184
|
+
resultBlock.retryPolicy = "after_user_action";
|
|
6185
|
+
resultBlock.content = JSON.stringify({
|
|
6186
|
+
code: "OUTCOME_UNKNOWN",
|
|
6187
|
+
retryPolicy: "after_user_action",
|
|
6188
|
+
message: "Tool outcome is unknown because the audit result could not be written"
|
|
6189
|
+
});
|
|
6190
|
+
resultBlock.execution = {
|
|
6191
|
+
version: 1,
|
|
6192
|
+
dispatchOutcome: resultBlock.execution?.dispatchOutcome ?? "succeeded",
|
|
6193
|
+
hostError: {
|
|
6194
|
+
phase: "audit",
|
|
6195
|
+
code: "AUDIT_UNAVAILABLE"
|
|
6196
|
+
}
|
|
6197
|
+
};
|
|
6198
|
+
return resultBlock;
|
|
5864
6199
|
}
|
|
5865
|
-
await this.onToolResult?.(tool,
|
|
6200
|
+
await this.onToolResult?.(tool, finalInput, resultBlock);
|
|
5866
6201
|
return resultBlock;
|
|
5867
6202
|
}
|
|
5868
6203
|
/**
|
|
@@ -7637,6 +7972,7 @@ var AgentLoop = class {
|
|
|
7637
7972
|
let consecutiveErrors = 0;
|
|
7638
7973
|
let completed = false;
|
|
7639
7974
|
let overflowRetried = false;
|
|
7975
|
+
const toolSnapshot = "snapshot" in this.tools ? this.tools.snapshot() : this.tools;
|
|
7640
7976
|
while (step < this.maxSteps) {
|
|
7641
7977
|
step++;
|
|
7642
7978
|
const turnStartTime = Date.now();
|
|
@@ -7647,7 +7983,7 @@ var AgentLoop = class {
|
|
|
7647
7983
|
let request = {
|
|
7648
7984
|
systemPrompt: this.systemPrompt,
|
|
7649
7985
|
messages: structuredClone(this.projectHistory?.(history) ?? history),
|
|
7650
|
-
tools: visibleTools(
|
|
7986
|
+
tools: visibleTools(toolSnapshot.list(), this.getMode(), this.permissionRules).map(
|
|
7651
7987
|
toToolDefinition
|
|
7652
7988
|
),
|
|
7653
7989
|
signal: this.signal,
|
|
@@ -7656,7 +7992,30 @@ var AgentLoop = class {
|
|
|
7656
7992
|
const beforeModelHooks = this.hooks.get("model:before");
|
|
7657
7993
|
const hookCtx = { signal: this.signal };
|
|
7658
7994
|
for (const hook of beforeModelHooks) {
|
|
7659
|
-
|
|
7995
|
+
const modified = await hook(hookCtx, request);
|
|
7996
|
+
if (modified) {
|
|
7997
|
+
request = modified;
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
8000
|
+
if (request.tools) {
|
|
8001
|
+
for (const t of request.tools) {
|
|
8002
|
+
const original = toolSnapshot.get(t.name);
|
|
8003
|
+
if (!original) {
|
|
8004
|
+
throw new ModelError(`Hook injected tool '${t.name}' not present in snapshot`, {
|
|
8005
|
+
stage: "request",
|
|
8006
|
+
retryable: false
|
|
8007
|
+
});
|
|
8008
|
+
}
|
|
8009
|
+
if (t.description !== original.description || JSON.stringify(t.parameters) !== JSON.stringify(original.parameters)) {
|
|
8010
|
+
throw new ModelError(
|
|
8011
|
+
`Hook mutated tool definition for '${t.name}' (description or parameters drifted from snapshot)`,
|
|
8012
|
+
{
|
|
8013
|
+
stage: "request",
|
|
8014
|
+
retryable: false
|
|
8015
|
+
}
|
|
8016
|
+
);
|
|
8017
|
+
}
|
|
8018
|
+
}
|
|
7660
8019
|
}
|
|
7661
8020
|
if (this.prepareRequest) request = yield* this.prepareRequest(request, history);
|
|
7662
8021
|
yield {
|
|
@@ -8396,6 +8755,14 @@ var AgentSession = class {
|
|
|
8396
8755
|
});
|
|
8397
8756
|
this.plugins.push(plugin);
|
|
8398
8757
|
}
|
|
8758
|
+
/**
|
|
8759
|
+
* 获取当前会话已成功挂载的扩展插件只读列表。
|
|
8760
|
+
*
|
|
8761
|
+
* 保证返回当前状态的独立切片,外部修改不污染内部挂载表。
|
|
8762
|
+
*/
|
|
8763
|
+
getPlugins() {
|
|
8764
|
+
return [...this.plugins];
|
|
8765
|
+
}
|
|
8399
8766
|
/**
|
|
8400
8767
|
* 空闲时原子替换角色的模型绑定;本方法不改变当前角色或工具权限。
|
|
8401
8768
|
* @param profile 模型协议、请求目标和输出预算。
|
|
@@ -8462,6 +8829,10 @@ var AgentSession = class {
|
|
|
8462
8829
|
getMode() {
|
|
8463
8830
|
return this.mode;
|
|
8464
8831
|
}
|
|
8832
|
+
/** 获取当前会话生效的权限规则列表。 */
|
|
8833
|
+
getPermissionRules() {
|
|
8834
|
+
return this.permissionRules;
|
|
8835
|
+
}
|
|
8465
8836
|
/** 返回最近一次成功装载的来源路径,不暴露指令正文。 */
|
|
8466
8837
|
getInstructionSources() {
|
|
8467
8838
|
return this.instructionSnapshot?.sources.map((source) => source.path) ?? [];
|
|
@@ -8590,8 +8961,23 @@ var AgentSession = class {
|
|
|
8590
8961
|
options?.signal
|
|
8591
8962
|
);
|
|
8592
8963
|
}
|
|
8964
|
+
/**
|
|
8965
|
+
* 受控任务准备入口:在同一运行锁内异步准备文本块/来源,预算和存储成功后才进入模型。
|
|
8966
|
+
*
|
|
8967
|
+
* @param task 用户显式任务正文(或命令描述)。
|
|
8968
|
+
* @param prepare 异步任务准备函数;在锁内执行,若准备失败或取消则释放锁且零 append。
|
|
8969
|
+
* @param options 可选中止信号与显式主任务角色。
|
|
8970
|
+
* @returns 结构化事件的异步流。
|
|
8971
|
+
* @throws {SessionBusyError} 当前会话忙碌时抛出。
|
|
8972
|
+
*/
|
|
8973
|
+
runPreparedTask(task, prepare, options) {
|
|
8974
|
+
return cancellableGenerator(
|
|
8975
|
+
(signal) => this.runOperation(task, { signal, role: options?.role }, void 0, prepare),
|
|
8976
|
+
options?.signal
|
|
8977
|
+
);
|
|
8978
|
+
}
|
|
8593
8979
|
/** 单次任务执行主体;调用方 return 的中止与 finally 清理由外层统一协调。 */
|
|
8594
|
-
async *runOperation(userInput, options, skillName) {
|
|
8980
|
+
async *runOperation(userInput, options, skillName, prepare) {
|
|
8595
8981
|
this.assertIdle("start another run");
|
|
8596
8982
|
const role = options?.role ?? this.primaryRole;
|
|
8597
8983
|
const { profile, provider } = this.resolvePrimaryBinding(role);
|
|
@@ -8675,12 +9061,22 @@ var AgentSession = class {
|
|
|
8675
9061
|
profile.contextWindow,
|
|
8676
9062
|
profile.maxOutputTokens
|
|
8677
9063
|
).inputBudget;
|
|
9064
|
+
const toolSnapshot = this.tools.snapshot();
|
|
9065
|
+
let prepared;
|
|
9066
|
+
if (prepare) {
|
|
9067
|
+
prepared = await prepare({ signal: options?.signal, runId, tools: toolSnapshot });
|
|
9068
|
+
if (options?.signal?.aborted) throw new AbortError();
|
|
9069
|
+
if (prepared.skillName) skillName = prepared.skillName;
|
|
9070
|
+
}
|
|
8678
9071
|
await this.skillRegistry?.refresh(options?.signal);
|
|
8679
9072
|
this.skillRuntime?.syncTools();
|
|
9073
|
+
const extraPreparedTokens = prepared?.content ? typeof prepared.content === "string" ? estimateSkillTokens(prepared.content) : prepared.content.filter(isTextBlock).reduce((sum, b) => sum + estimateSkillTokens(b.text), 0) : 0;
|
|
8680
9074
|
const promptAssembler = this.createPromptAssembler(
|
|
8681
9075
|
profile,
|
|
8682
9076
|
this.context.project(this.history),
|
|
8683
|
-
32 + estimateSkillTokens(userInput)
|
|
9077
|
+
32 + estimateSkillTokens(userInput) + extraPreparedTokens,
|
|
9078
|
+
toolSnapshot.list(),
|
|
9079
|
+
toolSnapshot
|
|
8684
9080
|
);
|
|
8685
9081
|
this.skillRuntime?.begin(
|
|
8686
9082
|
runId,
|
|
@@ -8689,7 +9085,7 @@ var AgentSession = class {
|
|
|
8689
9085
|
const current = {
|
|
8690
9086
|
...previousRequest ?? { systemPrompt: promptAssembler.assemble() },
|
|
8691
9087
|
messages: [...this.context.project(this.history), ...budgetToolMessages],
|
|
8692
|
-
tools: previousRequest?.tools ?? visibleTools(
|
|
9088
|
+
tools: previousRequest?.tools ?? visibleTools(toolSnapshot.list(), this.mode, this.permissionRules).map((tool) => ({
|
|
8693
9089
|
name: tool.name,
|
|
8694
9090
|
description: tool.description,
|
|
8695
9091
|
parameters: tool.parameters
|
|
@@ -8697,7 +9093,7 @@ var AgentSession = class {
|
|
|
8697
9093
|
};
|
|
8698
9094
|
return Math.max(
|
|
8699
9095
|
0,
|
|
8700
|
-
inputBudget - (this.estimator ?? new UnicodeTokenEstimator()).estimate(current).total - (previousRequest ? 0 : estimateSkillTokens(userInput) + 32)
|
|
9096
|
+
inputBudget - (this.estimator ?? new UnicodeTokenEstimator()).estimate(current).total - (previousRequest ? 0 : estimateSkillTokens(userInput) + extraPreparedTokens + 32)
|
|
8701
9097
|
);
|
|
8702
9098
|
},
|
|
8703
9099
|
() => previousRequest?.messages ?? this.context.project(this.history)
|
|
@@ -8709,6 +9105,51 @@ var AgentSession = class {
|
|
|
8709
9105
|
await this.authorizeSkill(skillName, runId, options?.signal);
|
|
8710
9106
|
preparedSkill = await this.skillRuntime.prepare(skillName, "user", options?.signal);
|
|
8711
9107
|
}
|
|
9108
|
+
if (prepared?.verifyIdentity) {
|
|
9109
|
+
const valid = await prepared.verifyIdentity();
|
|
9110
|
+
if (!valid) {
|
|
9111
|
+
throw new KapibalaError("Task preparation identity changed before commit", {
|
|
9112
|
+
code: "MCP_PROMPT_CHANGED",
|
|
9113
|
+
retryPolicy: "never"
|
|
9114
|
+
});
|
|
9115
|
+
}
|
|
9116
|
+
}
|
|
9117
|
+
const userContent = [{ type: "text", text: userInput }];
|
|
9118
|
+
if (preparedSkill)
|
|
9119
|
+
userContent.push({
|
|
9120
|
+
type: "text",
|
|
9121
|
+
text: preparedSkill.text,
|
|
9122
|
+
skillSource: preparedSkill.source
|
|
9123
|
+
});
|
|
9124
|
+
if (prepared?.content) {
|
|
9125
|
+
if (typeof prepared.content === "string") {
|
|
9126
|
+
userContent.push({
|
|
9127
|
+
type: "text",
|
|
9128
|
+
text: prepared.content,
|
|
9129
|
+
...prepared.promptSource ? { promptSource: prepared.promptSource } : {}
|
|
9130
|
+
});
|
|
9131
|
+
} else {
|
|
9132
|
+
userContent.push(...prepared.content);
|
|
9133
|
+
}
|
|
9134
|
+
}
|
|
9135
|
+
const userMessage = {
|
|
9136
|
+
id: createMessageId(),
|
|
9137
|
+
interactionId: runId,
|
|
9138
|
+
state: "completed",
|
|
9139
|
+
role: "user",
|
|
9140
|
+
content: userContent,
|
|
9141
|
+
timestamp: Date.now()
|
|
9142
|
+
};
|
|
9143
|
+
const taskBudgetCheck = (this.estimator ?? new UnicodeTokenEstimator()).estimate({
|
|
9144
|
+
systemPrompt: promptAssembler.assemble(),
|
|
9145
|
+
messages: [userMessage]
|
|
9146
|
+
}).total;
|
|
9147
|
+
if (taskBudgetCheck > inputBudget) {
|
|
9148
|
+
throw new ContextOverflowError(400, {
|
|
9149
|
+
message: "Prepared task content exceeds context input budget",
|
|
9150
|
+
stage: "request"
|
|
9151
|
+
});
|
|
9152
|
+
}
|
|
8712
9153
|
this.totalTurns++;
|
|
8713
9154
|
if (this.store && supportsSessionState(this.store)) {
|
|
8714
9155
|
const currentProject = resolveSessionProject(this.cwd);
|
|
@@ -8722,24 +9163,39 @@ var AgentSession = class {
|
|
|
8722
9163
|
});
|
|
8723
9164
|
}
|
|
8724
9165
|
runStarted = true;
|
|
8725
|
-
const userMessage = {
|
|
8726
|
-
id: createMessageId(),
|
|
8727
|
-
interactionId: runId,
|
|
8728
|
-
state: "completed",
|
|
8729
|
-
role: "user",
|
|
8730
|
-
content: [{ type: "text", text: userInput }],
|
|
8731
|
-
timestamp: Date.now()
|
|
8732
|
-
};
|
|
8733
|
-
if (preparedSkill)
|
|
8734
|
-
userMessage.content.push({
|
|
8735
|
-
type: "text",
|
|
8736
|
-
text: preparedSkill.text,
|
|
8737
|
-
skillSource: preparedSkill.source
|
|
8738
|
-
});
|
|
8739
9166
|
if (this.store) {
|
|
8740
9167
|
await this.store.append(userMessage);
|
|
8741
9168
|
}
|
|
8742
9169
|
this.history.push(userMessage);
|
|
9170
|
+
if (prepared?.promptSource) {
|
|
9171
|
+
const persistence = this.store ? "disk" : "memory";
|
|
9172
|
+
yield {
|
|
9173
|
+
type: "mcp_prompt_loaded",
|
|
9174
|
+
server: prepared.promptSource.server,
|
|
9175
|
+
name: prepared.promptSource.name,
|
|
9176
|
+
persistence,
|
|
9177
|
+
promptSource: prepared.promptSource,
|
|
9178
|
+
runId
|
|
9179
|
+
};
|
|
9180
|
+
try {
|
|
9181
|
+
await this.eventLogger.recordAudit({
|
|
9182
|
+
level: "info",
|
|
9183
|
+
event: "mcp.prompt_loaded",
|
|
9184
|
+
sessionId: this.sessionId,
|
|
9185
|
+
runId,
|
|
9186
|
+
fields: {
|
|
9187
|
+
server: prepared.promptSource.server,
|
|
9188
|
+
promptName: prepared.promptSource.name,
|
|
9189
|
+
contentDigest: prepared.promptSource.contentDigest,
|
|
9190
|
+
persistence
|
|
9191
|
+
}
|
|
9192
|
+
});
|
|
9193
|
+
} catch (auditErr) {
|
|
9194
|
+
const msg = `MCP prompt audit recording failed: ${auditErr instanceof Error ? auditErr.message : String(auditErr)}`;
|
|
9195
|
+
this.logger?.(msg);
|
|
9196
|
+
this.onDiagnostic?.(msg);
|
|
9197
|
+
}
|
|
9198
|
+
}
|
|
8743
9199
|
if (preparedSkill && this.skillRuntime) {
|
|
8744
9200
|
this.skillRuntime.acceptUser(preparedSkill);
|
|
8745
9201
|
for (const event of await this.commitSkills(runId)) yield event;
|
|
@@ -8750,7 +9206,7 @@ var AgentSession = class {
|
|
|
8750
9206
|
const executor = new ToolExecutor({
|
|
8751
9207
|
hardDenial: (tool) => this.skillRuntime?.denial(tool),
|
|
8752
9208
|
onToolResult: (tool, _input, result) => this.skillRuntime?.acceptResult(tool, result),
|
|
8753
|
-
tools:
|
|
9209
|
+
tools: toolSnapshot,
|
|
8754
9210
|
hooks: this.hooks,
|
|
8755
9211
|
rootDir: this.rootDir,
|
|
8756
9212
|
signal: options?.signal,
|
|
@@ -8872,12 +9328,12 @@ var AgentSession = class {
|
|
|
8872
9328
|
const skills = this.skillRuntime;
|
|
8873
9329
|
const preparation = context.prepare(history, candidate, profile, runId, reason, !frozen);
|
|
8874
9330
|
return (async function* () {
|
|
8875
|
-
const
|
|
8876
|
-
skills?.assertProjection(
|
|
8877
|
-
previousRequest = structuredClone({ ...
|
|
9331
|
+
const prepared2 = yield* preparation;
|
|
9332
|
+
skills?.assertProjection(prepared2.messages);
|
|
9333
|
+
previousRequest = structuredClone({ ...prepared2, signal: void 0 });
|
|
8878
9334
|
lastPromptTokens = void 0;
|
|
8879
9335
|
lastEstimatedPromptTokens = context.getSnapshot()?.estimate.total;
|
|
8880
|
-
return
|
|
9336
|
+
return prepared2;
|
|
8881
9337
|
})();
|
|
8882
9338
|
},
|
|
8883
9339
|
onUsage: async (request, usage, attemptId) => {
|
|
@@ -8896,7 +9352,7 @@ var AgentSession = class {
|
|
|
8896
9352
|
},
|
|
8897
9353
|
provider,
|
|
8898
9354
|
executor,
|
|
8899
|
-
tools:
|
|
9355
|
+
tools: toolSnapshot,
|
|
8900
9356
|
hooks: this.hooks,
|
|
8901
9357
|
systemPrompt: assembledSystemPrompt,
|
|
8902
9358
|
maxSteps: this.maxSteps,
|
|
@@ -9102,11 +9558,12 @@ var AgentSession = class {
|
|
|
9102
9558
|
if (this.activeRun) throw new SessionBusyError(operation);
|
|
9103
9559
|
}
|
|
9104
9560
|
/** 实际请求、静态统计和手动压缩共用目录空间推导;先计正文、工具和非目录提示,再预算整段目录。 */
|
|
9105
|
-
createPromptAssembler(profile, messages, extraTokens = 0) {
|
|
9106
|
-
const
|
|
9561
|
+
createPromptAssembler(profile, messages, extraTokens = 0, toolList, toolRegistry) {
|
|
9562
|
+
const registry = toolRegistry ?? this.tools;
|
|
9563
|
+
const tools = visibleTools(toolList ?? registry.list(), this.mode, this.permissionRules);
|
|
9107
9564
|
const options = {
|
|
9108
9565
|
skills: this.skillRegistry,
|
|
9109
|
-
tools:
|
|
9566
|
+
tools: registry,
|
|
9110
9567
|
rootDir: this.rootDir,
|
|
9111
9568
|
customInstructions: this.customSystemPrompt,
|
|
9112
9569
|
instructionSources: this.instructionSnapshot?.sources,
|
|
@@ -9407,6 +9864,7 @@ var AgentSession = class {
|
|
|
9407
9864
|
OpenAIResponsesProvider,
|
|
9408
9865
|
PathSandbox,
|
|
9409
9866
|
PermissionPolicy,
|
|
9867
|
+
PreDispatchError,
|
|
9410
9868
|
PromptAssembler,
|
|
9411
9869
|
SESSION_SCHEMA_VERSION,
|
|
9412
9870
|
SKILL_LIMITS,
|