@nextclaw/ncp-agent-runtime 0.3.8 → 0.3.9
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.d.ts +3 -0
- package/dist/index.js +91 -71
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -166,6 +166,9 @@ declare class DefaultNcpAgentRuntime implements NcpAgentRuntime {
|
|
|
166
166
|
* The stream encoder does not emit RunFinished; it only converts chunks to NCP events.
|
|
167
167
|
*/
|
|
168
168
|
private runLoop;
|
|
169
|
+
private executeToolCall;
|
|
170
|
+
private resolveValidationIssues;
|
|
171
|
+
private executeValidatedToolCall;
|
|
169
172
|
private tapStream;
|
|
170
173
|
}
|
|
171
174
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
3
3
|
import { copyFile, mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, join, resolve } from "node:path";
|
|
5
5
|
import { NcpAssistantTextStreamNormalizer, NcpEventType, isHiddenNcpMessage, normalizeAssistantText } from "@nextclaw/ncp";
|
|
6
|
+
import AjvPkg from "ajv";
|
|
6
7
|
//#region src/user-content.ts
|
|
7
8
|
function readOptionalString$1(value) {
|
|
8
9
|
if (typeof value !== "string") return null;
|
|
@@ -859,6 +860,12 @@ var EchoNcpLLMApi = class {
|
|
|
859
860
|
};
|
|
860
861
|
//#endregion
|
|
861
862
|
//#region src/utils.ts
|
|
863
|
+
const toolSchemaValidator = new AjvPkg({
|
|
864
|
+
allErrors: true,
|
|
865
|
+
strict: false,
|
|
866
|
+
removeAdditional: false
|
|
867
|
+
});
|
|
868
|
+
const validatorCache = /* @__PURE__ */ new WeakMap();
|
|
862
869
|
function genId() {
|
|
863
870
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
|
|
864
871
|
}
|
|
@@ -911,47 +918,25 @@ function parseToolArgs(args) {
|
|
|
911
918
|
}
|
|
912
919
|
function validateToolArgs(args, schema) {
|
|
913
920
|
if (!schema) return [];
|
|
914
|
-
|
|
921
|
+
const validate = getOrCreateValidator(schema);
|
|
922
|
+
if (validate(args)) return [];
|
|
923
|
+
return formatSchemaIssues(validate.errors);
|
|
915
924
|
}
|
|
916
|
-
function
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
if (typeof value === "number") {
|
|
923
|
-
if (schema.minimum !== void 0 && value < schema.minimum) errors.push(`${label} must be >= ${schema.minimum}`);
|
|
924
|
-
if (schema.maximum !== void 0 && value > schema.maximum) errors.push(`${label} must be <= ${schema.maximum}`);
|
|
925
|
-
}
|
|
926
|
-
if (typeof value === "string") {
|
|
927
|
-
if (schema.minLength !== void 0 && value.length < schema.minLength) errors.push(`${label} must be at least ${schema.minLength} chars`);
|
|
928
|
-
if (schema.maxLength !== void 0 && value.length > schema.maxLength) errors.push(`${label} must be at most ${schema.maxLength} chars`);
|
|
929
|
-
}
|
|
930
|
-
if (type === "object") {
|
|
931
|
-
const objectValue = value;
|
|
932
|
-
for (const key of schema.required ?? []) if (!(key in objectValue)) errors.push(`missing required ${path ? `${path}.${key}` : key}`);
|
|
933
|
-
const properties = schema.properties ?? {};
|
|
934
|
-
for (const [key, childValue] of Object.entries(objectValue)) {
|
|
935
|
-
const childSchema = properties[key];
|
|
936
|
-
if (!childSchema) continue;
|
|
937
|
-
errors.push(...validateToolValue(childValue, childSchema, path ? `${path}.${key}` : key));
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
if (type === "array" && schema.items && Array.isArray(value)) value.forEach((item, index) => {
|
|
941
|
-
errors.push(...validateToolValue(item, schema.items, `${label}[${index}]`));
|
|
942
|
-
});
|
|
943
|
-
return errors;
|
|
925
|
+
function getOrCreateValidator(schema) {
|
|
926
|
+
const cached = validatorCache.get(schema);
|
|
927
|
+
if (cached) return cached;
|
|
928
|
+
const validate = toolSchemaValidator.compile(schema);
|
|
929
|
+
validatorCache.set(schema, validate);
|
|
930
|
+
return validate;
|
|
944
931
|
}
|
|
945
|
-
function
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
default: return true;
|
|
954
|
-
}
|
|
932
|
+
function formatSchemaIssues(errors) {
|
|
933
|
+
if (!errors || errors.length === 0) return ["Tool arguments do not match the declared schema."];
|
|
934
|
+
return errors.map((error) => {
|
|
935
|
+
const instancePath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
|
|
936
|
+
if (error.keyword === "required" && "missingProperty" in error.params && typeof error.params.missingProperty === "string") return `${instancePath ? `${instancePath}.${error.params.missingProperty}` : error.params.missingProperty} is required`;
|
|
937
|
+
if (error.keyword === "additionalProperties" && "additionalProperty" in error.params && typeof error.params.additionalProperty === "string") return `${instancePath ? `${instancePath}.${error.params.additionalProperty}` : error.params.additionalProperty} is not allowed`;
|
|
938
|
+
return `${instancePath || "parameter"}: ${error.message ?? "invalid"}`;
|
|
939
|
+
});
|
|
955
940
|
}
|
|
956
941
|
function createInvalidToolArgumentsResult(params) {
|
|
957
942
|
return {
|
|
@@ -966,6 +951,18 @@ function createInvalidToolArgumentsResult(params) {
|
|
|
966
951
|
}
|
|
967
952
|
};
|
|
968
953
|
}
|
|
954
|
+
function createToolExecutionFailedResult(params) {
|
|
955
|
+
const { toolCallId, toolName, error } = params;
|
|
956
|
+
return {
|
|
957
|
+
ok: false,
|
|
958
|
+
error: {
|
|
959
|
+
code: "tool_execution_failed",
|
|
960
|
+
message: error instanceof Error ? error.message : String(error),
|
|
961
|
+
toolCallId,
|
|
962
|
+
toolName
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
}
|
|
969
966
|
function appendToolRoundToInput(input, reasoning, text, toolResults) {
|
|
970
967
|
const assistantMsg = {
|
|
971
968
|
role: "assistant",
|
|
@@ -1115,43 +1112,14 @@ var DefaultNcpAgentRuntime = class {
|
|
|
1115
1112
|
for await (const event of this.streamEncoder.encode(tappedStream, ctx)) yield event;
|
|
1116
1113
|
const toolResults = [];
|
|
1117
1114
|
for (const toolCall of roundCollector.getToolCalls()) {
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
let result;
|
|
1121
|
-
let args = null;
|
|
1122
|
-
if (!parsedArgs.ok) result = createInvalidToolArgumentsResult({
|
|
1123
|
-
toolCallId: toolCall.toolCallId,
|
|
1124
|
-
toolName: toolCall.toolName,
|
|
1125
|
-
rawArgumentsText: parsedArgs.rawText,
|
|
1126
|
-
issues: parsedArgs.issues
|
|
1127
|
-
});
|
|
1128
|
-
else {
|
|
1129
|
-
const schemaIssues = validateToolArgs(parsedArgs.value, tool?.parameters);
|
|
1130
|
-
const validationIssues = schemaIssues.length > 0 ? schemaIssues : typeof tool?.validateArgs === "function" ? tool.validateArgs(parsedArgs.value) : [];
|
|
1131
|
-
if (validationIssues.length > 0) result = createInvalidToolArgumentsResult({
|
|
1132
|
-
toolCallId: toolCall.toolCallId,
|
|
1133
|
-
toolName: toolCall.toolName,
|
|
1134
|
-
rawArgumentsText: parsedArgs.rawText,
|
|
1135
|
-
issues: validationIssues
|
|
1136
|
-
});
|
|
1137
|
-
else {
|
|
1138
|
-
args = parsedArgs.value;
|
|
1139
|
-
result = await this.toolRegistry.execute(toolCall.toolCallId, toolCall.toolName, parsedArgs.value);
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
toolResults.push({
|
|
1143
|
-
toolCallId: toolCall.toolCallId,
|
|
1144
|
-
toolName: toolCall.toolName,
|
|
1145
|
-
args,
|
|
1146
|
-
rawArgsText: parsedArgs.rawText,
|
|
1147
|
-
result
|
|
1148
|
-
});
|
|
1115
|
+
const toolResult = await this.executeToolCall(toolCall);
|
|
1116
|
+
toolResults.push(toolResult);
|
|
1149
1117
|
yield {
|
|
1150
1118
|
type: NcpEventType.MessageToolCallResult,
|
|
1151
1119
|
payload: {
|
|
1152
1120
|
sessionId: ctx.sessionId,
|
|
1153
1121
|
toolCallId: toolCall.toolCallId,
|
|
1154
|
-
content: result
|
|
1122
|
+
content: toolResult.result
|
|
1155
1123
|
}
|
|
1156
1124
|
};
|
|
1157
1125
|
}
|
|
@@ -1170,6 +1138,58 @@ var DefaultNcpAgentRuntime = class {
|
|
|
1170
1138
|
currentInput = appendToolRoundToInput(currentInput, roundCollector.getReasoning(), roundCollector.getText(), toolResults);
|
|
1171
1139
|
}
|
|
1172
1140
|
};
|
|
1141
|
+
executeToolCall = async function(toolCall) {
|
|
1142
|
+
const tool = this.toolRegistry.getTool(toolCall.toolName);
|
|
1143
|
+
const parsedArgs = parseToolArgs(toolCall.args);
|
|
1144
|
+
if (!parsedArgs.ok) return {
|
|
1145
|
+
toolCallId: toolCall.toolCallId,
|
|
1146
|
+
toolName: toolCall.toolName,
|
|
1147
|
+
args: null,
|
|
1148
|
+
rawArgsText: parsedArgs.rawText,
|
|
1149
|
+
result: createInvalidToolArgumentsResult({
|
|
1150
|
+
toolCallId: toolCall.toolCallId,
|
|
1151
|
+
toolName: toolCall.toolName,
|
|
1152
|
+
rawArgumentsText: parsedArgs.rawText,
|
|
1153
|
+
issues: parsedArgs.issues
|
|
1154
|
+
})
|
|
1155
|
+
};
|
|
1156
|
+
const validationIssues = this.resolveValidationIssues(parsedArgs.value, tool);
|
|
1157
|
+
if (validationIssues.length > 0) return {
|
|
1158
|
+
toolCallId: toolCall.toolCallId,
|
|
1159
|
+
toolName: toolCall.toolName,
|
|
1160
|
+
args: null,
|
|
1161
|
+
rawArgsText: parsedArgs.rawText,
|
|
1162
|
+
result: createInvalidToolArgumentsResult({
|
|
1163
|
+
toolCallId: toolCall.toolCallId,
|
|
1164
|
+
toolName: toolCall.toolName,
|
|
1165
|
+
rawArgumentsText: parsedArgs.rawText,
|
|
1166
|
+
issues: validationIssues
|
|
1167
|
+
})
|
|
1168
|
+
};
|
|
1169
|
+
return {
|
|
1170
|
+
toolCallId: toolCall.toolCallId,
|
|
1171
|
+
toolName: toolCall.toolName,
|
|
1172
|
+
args: parsedArgs.value,
|
|
1173
|
+
rawArgsText: parsedArgs.rawText,
|
|
1174
|
+
result: await this.executeValidatedToolCall(toolCall, parsedArgs.value)
|
|
1175
|
+
};
|
|
1176
|
+
};
|
|
1177
|
+
resolveValidationIssues = function(args, tool) {
|
|
1178
|
+
const schemaIssues = validateToolArgs(args, tool?.parameters);
|
|
1179
|
+
if (schemaIssues.length > 0) return schemaIssues;
|
|
1180
|
+
return typeof tool?.validateArgs === "function" ? tool.validateArgs(args) : [];
|
|
1181
|
+
};
|
|
1182
|
+
executeValidatedToolCall = async function(toolCall, args) {
|
|
1183
|
+
try {
|
|
1184
|
+
return await this.toolRegistry.execute(toolCall.toolCallId, toolCall.toolName, args);
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
return createToolExecutionFailedResult({
|
|
1187
|
+
toolCallId: toolCall.toolCallId,
|
|
1188
|
+
toolName: toolCall.toolName,
|
|
1189
|
+
error
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1173
1193
|
tapStream = async function* (stream, onChunk) {
|
|
1174
1194
|
for await (const chunk of stream) {
|
|
1175
1195
|
onChunk(chunk);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ncp-agent-runtime",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Default agent runtime implementation built on NCP interfaces.",
|
|
6
6
|
"type": "module",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
+
"ajv": "^8.17.1",
|
|
18
19
|
"@nextclaw/ncp": "0.5.0"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|