linkque-cli-v2 1.0.7 → 1.1.0

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/linkquejs.js CHANGED
@@ -25449,6 +25449,20 @@ function getJsonSchemaIssues(value, path22 = "schema", options = {}) {
25449
25449
  validateSchemaNode(value, path22, issues, options);
25450
25450
  return issues;
25451
25451
  }
25452
+ var OPAQUE_OBJECT_SCHEMA = {
25453
+ type: "object",
25454
+ additionalProperties: true
25455
+ };
25456
+ function normalizeMcpOutputSchema(value) {
25457
+ if (!isPlainRecord(value) || getJsonSchemaIssues(value).length > 0) {
25458
+ return { schema: { ...OPAQUE_OBJECT_SCHEMA }, mode: "opaque" };
25459
+ }
25460
+ const schema = cloneJsonValue(value);
25461
+ if (schema.type === "object" && (!isPlainRecord(schema.properties) || Object.keys(schema.properties).length === 0) && !isPlainRecord(schema.additionalProperties)) {
25462
+ return { schema: { ...OPAQUE_OBJECT_SCHEMA }, mode: "opaque" };
25463
+ }
25464
+ return { schema, mode: "declared" };
25465
+ }
25452
25466
  function normalizeTargetJsonSchema(schema) {
25453
25467
  const cloned = JSON.parse(JSON.stringify(schema));
25454
25468
  normalizeObjectDefaults(cloned);
@@ -25901,7 +25915,7 @@ function parseA2UICardBindingContext(value) {
25901
25915
  const issues = getA2UICardBindingContextIssues(value);
25902
25916
  return issues.length > 0 ? { success: false, issues } : {
25903
25917
  success: true,
25904
- data: JSON.parse(JSON.stringify(value)),
25918
+ data: normalizeBindingContext(value),
25905
25919
  issues: []
25906
25920
  };
25907
25921
  }
@@ -26048,11 +26062,7 @@ function validateA2UICardBindings(input, options = {}) {
26048
26062
  issues.push(issue2(`fields.${field.fieldId}`, "MCP_TOOL_UNAVAILABLE", `MCP tool ${binding.toolName} is absent from binding context`));
26049
26063
  continue;
26050
26064
  }
26051
- const transform = validateA2UICardTransform(binding.transform, {
26052
- fieldId: field.fieldId,
26053
- sourceSchema: tool.outputSchema,
26054
- targetSchema: field.valueSchema
26055
- });
26065
+ const transform = validateA2UICardTransform(binding.transform);
26056
26066
  issues.push(...transform.issues.map((entry) => ({
26057
26067
  ...entry,
26058
26068
  path: `fields.${field.fieldId}.${entry.path}`
@@ -26123,6 +26133,7 @@ function validateTool(value, path22, resourceId, issues) {
26123
26133
  "description",
26124
26134
  "inputSchema",
26125
26135
  "outputSchema",
26136
+ "outputSchemaMode",
26126
26137
  "annotations"
26127
26138
  ], path22, issues);
26128
26139
  if (value.resourceId !== resourceId) {
@@ -26132,11 +26143,28 @@ function validateTool(value, path22, resourceId, issues) {
26132
26143
  issues.push(issue2(`${path22}.toolName`, "CARD_BINDING_CONTEXT_INVALID", "toolName is required"));
26133
26144
  }
26134
26145
  issues.push(...getJsonSchemaIssues(value.inputSchema, `${path22}.inputSchema`));
26135
- issues.push(...getJsonSchemaIssues(value.outputSchema, `${path22}.outputSchema`));
26136
- if (isPlainRecord(value.annotations) && value.annotations.readOnlyHint === false) {
26137
- issues.push(issue2(`${path22}.annotations`, "CARD_BINDING_CONTEXT_INVALID", "tool explicitly declares readOnlyHint=false"));
26146
+ if (value.outputSchemaMode !== void 0 && value.outputSchemaMode !== "declared" && value.outputSchemaMode !== "opaque") {
26147
+ issues.push(issue2(`${path22}.outputSchemaMode`, "CARD_BINDING_CONTEXT_INVALID", "outputSchemaMode must be declared or opaque"));
26138
26148
  }
26139
26149
  }
26150
+ function normalizeBindingContext(context) {
26151
+ const cloned = JSON.parse(JSON.stringify(context));
26152
+ cloned.candidates = cloned.candidates.map((candidate) => {
26153
+ if (candidate.status !== "ready")
26154
+ return candidate;
26155
+ const normalized = normalizeMcpOutputSchema(candidate.tool.outputSchema);
26156
+ const mode2 = normalized.mode === "opaque" || candidate.tool.outputSchemaMode === "opaque" ? "opaque" : "declared";
26157
+ return {
26158
+ ...candidate,
26159
+ tool: {
26160
+ ...candidate.tool,
26161
+ outputSchema: normalized.schema,
26162
+ outputSchemaMode: mode2
26163
+ }
26164
+ };
26165
+ });
26166
+ return cloned;
26167
+ }
26140
26168
  function validatePreferences(value, path22, issues) {
26141
26169
  if (value === void 0)
26142
26170
  return;
@@ -27150,7 +27178,7 @@ function compileV3Resource(draft, revision, bindings, tools) {
27150
27178
  purpose: binding.purpose,
27151
27179
  required: binding.required,
27152
27180
  ...binding.dependsOn?.length ? { dependsOn: [...binding.dependsOn] } : {},
27153
- outputSchema: tool?.outputSchema ?? {}
27181
+ outputSchema: normalizeMcpOutputSchema(tool?.outputSchema).schema
27154
27182
  };
27155
27183
  const previous = sourceById.get(source.sourceId);
27156
27184
  if (previous && stableJsonStringify(previous) !== stableJsonStringify(source)) {
@@ -27600,7 +27628,10 @@ async function validateCardBindingsFromDisk(cwd, draftId, expectedContextChecksu
27600
27628
  }
27601
27629
  const context = contextResult.data;
27602
27630
  const actualContextChecksum = createA2UICardBindingContextChecksum(context);
27603
- if (actualContextChecksum !== expectedContextChecksum) {
27631
+ const legacyContextChecksum = createA2UICardBindingContextChecksum(
27632
+ contextValue
27633
+ );
27634
+ if (actualContextChecksum !== expectedContextChecksum && legacyContextChecksum !== expectedContextChecksum) {
27604
27635
  throw new LinkqueCliError(
27605
27636
  "CARD_BINDING_CONTEXT_STALE",
27606
27637
  "\u7ED1\u5B9A\u4E0A\u4E0B\u6587\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77 AI \u8F85\u52A9\u7ED1\u5B9A"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linkque-cli-v2",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
4
4
  "description": "Public Linkque CLI bundle for sandbox debugging",
5
5
  "bin": {
6
6
  "linkquejs": "linkquejs.js"
@@ -1385,7 +1385,7 @@ var require_errors = __commonJS({
1385
1385
  message: ({ keyword }) => (0, codegen_1.str)`must pass "${keyword}" keyword validation`
1386
1386
  };
1387
1387
  exports.keyword$DataError = {
1388
- message: ({ keyword, schemaType }) => schemaType ? (0, codegen_1.str)`"${keyword}" keyword must be ${schemaType} ($data)` : (0, codegen_1.str)`"${keyword}" keyword is invalid ($data)`
1388
+ message: ({ keyword, schemaType: schemaType2 }) => schemaType2 ? (0, codegen_1.str)`"${keyword}" keyword must be ${schemaType2} ($data)` : (0, codegen_1.str)`"${keyword}" keyword is invalid ($data)`
1389
1389
  };
1390
1390
  function reportError(cxt, error = exports.keywordError, errorPaths, overrideAllErrors) {
1391
1391
  const { it } = cxt;
@@ -2044,8 +2044,8 @@ var require_keyword = __commonJS({
2044
2044
  throw new Error(`keyword "${keyword}" failed to compile`);
2045
2045
  return gen.scopeValue("keyword", typeof result == "function" ? { ref: result } : { ref: result, code: (0, codegen_1.stringify)(result) });
2046
2046
  }
2047
- function validSchemaType(schema, schemaType, allowUndefined = false) {
2048
- return !schemaType.length || schemaType.some((st) => st === "array" ? Array.isArray(schema) : st === "object" ? schema && typeof schema == "object" && !Array.isArray(schema) : typeof schema == st || allowUndefined && typeof schema == "undefined");
2047
+ function validSchemaType(schema, schemaType2, allowUndefined = false) {
2048
+ return !schemaType2.length || schemaType2.some((st) => st === "array" ? Array.isArray(schema) : st === "object" ? schema && typeof schema == "object" && !Array.isArray(schema) : typeof schema == st || allowUndefined && typeof schema == "undefined");
2049
2049
  }
2050
2050
  exports.validSchemaType = validSchemaType;
2051
2051
  function validateKeywordUsage({ schema, opts, self, errSchemaPath }, def, keyword) {
@@ -2823,11 +2823,11 @@ var require_validate = __commonJS({
2823
2823
  check$data(valid = codegen_1.nil, $dataValid = codegen_1.nil) {
2824
2824
  if (!this.$data)
2825
2825
  return;
2826
- const { gen, schemaCode, schemaType, def } = this;
2826
+ const { gen, schemaCode, schemaType: schemaType2, def } = this;
2827
2827
  gen.if((0, codegen_1.or)((0, codegen_1._)`${schemaCode} === undefined`, $dataValid));
2828
2828
  if (valid !== codegen_1.nil)
2829
2829
  gen.assign(valid, true);
2830
- if (schemaType.length || def.validateSchema) {
2830
+ if (schemaType2.length || def.validateSchema) {
2831
2831
  gen.elseIf(this.invalid$data());
2832
2832
  this.$dataError();
2833
2833
  if (valid !== codegen_1.nil)
@@ -2836,13 +2836,13 @@ var require_validate = __commonJS({
2836
2836
  gen.else();
2837
2837
  }
2838
2838
  invalid$data() {
2839
- const { gen, schemaCode, schemaType, def, it } = this;
2839
+ const { gen, schemaCode, schemaType: schemaType2, def, it } = this;
2840
2840
  return (0, codegen_1.or)(wrong$DataType(), invalid$DataSchema());
2841
2841
  function wrong$DataType() {
2842
- if (schemaType.length) {
2842
+ if (schemaType2.length) {
2843
2843
  if (!(schemaCode instanceof codegen_1.Name))
2844
2844
  throw new Error("ajv implementation error");
2845
- const st = Array.isArray(schemaType) ? schemaType : [schemaType];
2845
+ const st = Array.isArray(schemaType2) ? schemaType2 : [schemaType2];
2846
2846
  return (0, codegen_1._)`${(0, dataType_2.checkDataTypes)(st, schemaCode, it.opts.strictNumbers, dataType_2.DataType.Wrong)}`;
2847
2847
  }
2848
2848
  return codegen_1.nil;
@@ -16666,6 +16666,9 @@ function isJsonValue(value) {
16666
16666
  return value.every(isJsonValue);
16667
16667
  return isPlainRecord(value) && Object.values(value).every(isJsonValue);
16668
16668
  }
16669
+ function cloneJsonValue(value) {
16670
+ return isJsonValue(value) ? JSON.parse(JSON.stringify(value)) : void 0;
16671
+ }
16669
16672
  function cloneJsonObject(value) {
16670
16673
  if (!isPlainRecord(value) || !isJsonValue(value))
16671
16674
  return void 0;
@@ -17978,7 +17981,7 @@ function optionalString(value, path2, issues) {
17978
17981
 
17979
17982
  // ../agent-a2ui-card-contract/dist/esm/resolver.js
17980
17983
  var A2UICardResolverError = class extends Error {
17981
- constructor(code, message, fieldId) {
17984
+ constructor(code, message, fieldId, expectedType, actualType) {
17982
17985
  super(message);
17983
17986
  Object.defineProperty(this, "code", {
17984
17987
  enumerable: true,
@@ -17992,9 +17995,23 @@ var A2UICardResolverError = class extends Error {
17992
17995
  writable: true,
17993
17996
  value: void 0
17994
17997
  });
17998
+ Object.defineProperty(this, "expectedType", {
17999
+ enumerable: true,
18000
+ configurable: true,
18001
+ writable: true,
18002
+ value: void 0
18003
+ });
18004
+ Object.defineProperty(this, "actualType", {
18005
+ enumerable: true,
18006
+ configurable: true,
18007
+ writable: true,
18008
+ value: void 0
18009
+ });
17995
18010
  this.name = "A2UICardResolverError";
17996
18011
  this.code = code;
17997
18012
  this.fieldId = fieldId;
18013
+ this.expectedType = expectedType;
18014
+ this.actualType = actualType;
17998
18015
  }
17999
18016
  };
18000
18017
  async function executeA2UICardResolver(input) {
@@ -18014,8 +18031,8 @@ async function executeA2UICardResolver(input) {
18014
18031
  if (!tool) {
18015
18032
  throw new A2UICardResolverError("MCP_TOOL_UNAVAILABLE", `MCP tool ${call.mcpResourceId}/${call.toolName} is unavailable`, fieldId);
18016
18033
  }
18017
- if (!tool.readOnly || !tool.outputSchema) {
18018
- throw new A2UICardResolverError("MCP_SCHEMA_UNSUPPORTED", `MCP tool ${call.mcpResourceId}/${call.toolName} is not a read-only structured tool`, fieldId);
18034
+ if (!tool.outputSchema) {
18035
+ throw new A2UICardResolverError("MCP_SCHEMA_UNSUPPORTED", `MCP tool ${call.mcpResourceId}/${call.toolName} has no output schema`, fieldId);
18019
18036
  }
18020
18037
  const args = {};
18021
18038
  for (const binding of call.inputBindings) {
@@ -18042,7 +18059,7 @@ async function executeA2UICardResolver(input) {
18042
18059
  for (const binding of input.resolver.outputBindings) {
18043
18060
  const field = fieldById.get(binding.fieldId);
18044
18061
  const result = results.get(binding.callId);
18045
- if (!field || !result) {
18062
+ if (!field || result === void 0) {
18046
18063
  throw new A2UICardResolverError("CARD_BINDING_INVALID", `Output binding ${binding.fieldId} is invalid`, binding.fieldId);
18047
18064
  }
18048
18065
  let value;
@@ -18078,7 +18095,7 @@ async function executeA2UICardResolver(input) {
18078
18095
  }
18079
18096
  const value = readJsonPointer(viewModel, field.path);
18080
18097
  if (field.required && value === void 0) {
18081
- throw new A2UICardResolverError("CARD_REQUIRED_FIELD_MISSING", `Required card field ${field.fieldId} is missing`, field.fieldId);
18098
+ throw new A2UICardResolverError("CARD_REQUIRED_FIELD_MISSING", `Required card field ${field.fieldId} is missing`, field.fieldId, field.type, "missing");
18082
18099
  }
18083
18100
  if (value !== void 0) {
18084
18101
  assertSchemaValue(field.valueSchema, value, "CARD_MAPPING_SCHEMA_MISMATCH", `card field ${field.fieldId}`, field.fieldId);
@@ -18118,8 +18135,8 @@ function normalizeOptionalNulls(value, schema, required) {
18118
18135
  }
18119
18136
  async function executeTool(tool, args, signal, fieldId) {
18120
18137
  const response = await tool.call(args, signal);
18121
- const structured = cloneJsonObject(response.structuredContent);
18122
- if (!structured) {
18138
+ const structured = cloneJsonValue(response.structuredContent);
18139
+ if (structured === void 0) {
18123
18140
  throw new A2UICardResolverError("MCP_STRUCTURED_CONTENT_REQUIRED", `MCP tool ${tool.resourceId}/${tool.toolName} did not return structuredContent`, fieldId);
18124
18141
  }
18125
18142
  return structured;
@@ -18133,9 +18150,19 @@ function toolKey(resourceId, toolName) {
18133
18150
  function assertSchemaValue(schema, value, code, label, fieldId) {
18134
18151
  const issues = validateJsonSchemaValue2(schema, value);
18135
18152
  if (issues.length > 0) {
18136
- throw new A2UICardResolverError(code, `${label} does not match schema at ${issues[0].path}: ${issues[0].message}`, fieldId);
18153
+ throw new A2UICardResolverError(code, `${label} does not match schema at ${issues[0].path}: ${issues[0].message}`, fieldId, schemaType(schema), valueType2(value));
18137
18154
  }
18138
18155
  }
18156
+ function schemaType(schema) {
18157
+ return typeof schema.type === "string" ? schema.type : "unknown";
18158
+ }
18159
+ function valueType2(value) {
18160
+ if (value === null)
18161
+ return "null";
18162
+ if (Array.isArray(value))
18163
+ return "array";
18164
+ return typeof value === "object" ? "object" : typeof value;
18165
+ }
18139
18166
 
18140
18167
  // ../linkque-agent-runtime/dist/esm/core/mcpNaming.js
18141
18168
  import { createHash } from "node:crypto";
@@ -18172,7 +18199,7 @@ async function executeCardPresentation(resource, registry, runtimeContext, args,
18172
18199
  if (!handle) {
18173
18200
  throw new Error(`MCP tool ${call.mcpResourceId}/${call.toolName} is unavailable`);
18174
18201
  }
18175
- const inputSchema = isJsonObject(handle.inputSchema) ? handle.inputSchema : FALLBACK_OBJECT_SCHEMA;
18202
+ const inputSchema = normalizeMcpInputSchema(handle.inputSchema);
18176
18203
  return {
18177
18204
  resourceId: call.mcpResourceId,
18178
18205
  toolName: call.toolName,
@@ -18182,9 +18209,6 @@ async function executeCardPresentation(resource, registry, runtimeContext, args,
18182
18209
  // ViewModel Schema 校验。
18183
18210
  inputSchema,
18184
18211
  outputSchema: isJsonObject(handle.outputSchema) ? handle.outputSchema : FALLBACK_OBJECT_SCHEMA,
18185
- // 未声明 readOnlyHint 是旧 MCP 的常见情况;只拒绝明确声明
18186
- // readOnlyHint=false 的工具。
18187
- readOnly: handle.readOnly !== false,
18188
18212
  call: async (input, callSignal) => {
18189
18213
  const result = await handle.execute(supplementMcpInput(input, invocation, config.presentationInputSchema, inputSchema), callSignal);
18190
18214
  return { structuredContent: readStructuredContent(result) };
@@ -18233,29 +18257,46 @@ async function executeCardPresentationFromSources(resource, runtimeContext, sour
18233
18257
  transform: binding.transform
18234
18258
  }))
18235
18259
  };
18236
- const viewModel = await executeA2UICardResolver({
18237
- resolver,
18238
- fields: config.fields,
18239
- presentationInputSchema: {
18240
- type: "object",
18241
- properties: {},
18242
- additionalProperties: false
18243
- },
18244
- argsSchema: config.a2uiTemplate.argsSchema,
18245
- invocation: {},
18246
- runtimeContext,
18247
- tools: config.dataSources.filter((source) => sourceResults.has(source.sourceId)).map((source) => ({
18248
- resourceId: source.mcpResourceId,
18249
- toolName: source.toolName,
18250
- inputSchema: FALLBACK_OBJECT_SCHEMA,
18251
- outputSchema: source.outputSchema ?? FALLBACK_OBJECT_SCHEMA,
18252
- readOnly: true,
18253
- call: async () => ({
18254
- structuredContent: sourceResults.get(source.sourceId)
18255
- })
18256
- })),
18257
- signal
18258
- });
18260
+ let viewModel;
18261
+ try {
18262
+ viewModel = await executeA2UICardResolver({
18263
+ resolver,
18264
+ fields: config.fields,
18265
+ presentationInputSchema: {
18266
+ type: "object",
18267
+ properties: {},
18268
+ additionalProperties: false
18269
+ },
18270
+ argsSchema: config.a2uiTemplate.argsSchema,
18271
+ invocation: {},
18272
+ runtimeContext,
18273
+ tools: config.dataSources.filter((source) => sourceResults.has(source.sourceId)).map((source) => ({
18274
+ resourceId: source.mcpResourceId,
18275
+ toolName: source.toolName,
18276
+ inputSchema: FALLBACK_OBJECT_SCHEMA,
18277
+ outputSchema: source.outputSchema ?? FALLBACK_OBJECT_SCHEMA,
18278
+ call: async () => ({
18279
+ structuredContent: sourceResults.get(source.sourceId)
18280
+ })
18281
+ })),
18282
+ signal
18283
+ });
18284
+ } catch (error) {
18285
+ if (error instanceof A2UICardResolverError) {
18286
+ const binding = config.bindings.find((candidate) => candidate.fieldId === error.fieldId);
18287
+ const field = config.fields.find((candidate) => candidate.fieldId === error.fieldId);
18288
+ throw new CardMappingError({
18289
+ code: error.code,
18290
+ cardId: resource.resourceId,
18291
+ fieldId: error.fieldId ?? "unknown",
18292
+ sourceId: binding?.sourceId ?? "unknown",
18293
+ expectedType: error.expectedType ?? field?.type ?? "unknown",
18294
+ actualType: error.actualType ?? "unknown",
18295
+ retryable: false
18296
+ });
18297
+ }
18298
+ throw error;
18299
+ }
18259
18300
  return createPresentationResult(resource, config, viewModel);
18260
18301
  }
18261
18302
  function createPresentationResult(resource, config, viewModel) {
@@ -18349,23 +18390,44 @@ function compatibleSchemaTypes(source, target) {
18349
18390
  function readStructuredContent(result) {
18350
18391
  if (!isJsonObject(result))
18351
18392
  return void 0;
18352
- if (isJsonObject(result.structuredContent)) {
18353
- return JSON.parse(JSON.stringify(result.structuredContent));
18393
+ if (Object.hasOwn(result, "structuredContent") && isJsonValue(result.structuredContent)) {
18394
+ return cloneJsonValue(result.structuredContent);
18354
18395
  }
18355
- if (!Array.isArray(result.content) || result.content.length !== 1) {
18396
+ if (!Array.isArray(result.content) || result.content.length === 0) {
18356
18397
  return void 0;
18357
18398
  }
18358
- const [content] = result.content;
18359
- if (!isJsonObject(content) || content.type !== "text" || typeof content.text !== "string") {
18360
- return void 0;
18399
+ const texts = [];
18400
+ for (const content of result.content) {
18401
+ if (!isJsonObject(content) || content.type !== "text" || typeof content.text !== "string")
18402
+ return void 0;
18403
+ texts.push(content.text);
18361
18404
  }
18405
+ if (texts.length > 1)
18406
+ return texts.join("\n");
18362
18407
  try {
18363
- const parsed = JSON.parse(content.text);
18364
- return isJsonObject(parsed) ? JSON.parse(JSON.stringify(parsed)) : void 0;
18408
+ const parsed = JSON.parse(texts[0]);
18409
+ return isJsonValue(parsed) ? cloneJsonValue(parsed) : texts[0];
18365
18410
  } catch {
18366
- return void 0;
18411
+ return texts[0];
18367
18412
  }
18368
18413
  }
18414
+ function normalizeMcpInputSchema(value) {
18415
+ if (!isJsonObject(value) || getJsonSchemaIssues2(value).length > 0) {
18416
+ return { ...FALLBACK_OBJECT_SCHEMA };
18417
+ }
18418
+ if (value.type === "object" && (!isJsonObject(value.properties) || Object.keys(value.properties).length === 0))
18419
+ return { ...FALLBACK_OBJECT_SCHEMA };
18420
+ return JSON.parse(JSON.stringify(value));
18421
+ }
18422
+ var CardMappingError = class extends Error {
18423
+ details;
18424
+ retryable = false;
18425
+ constructor(details) {
18426
+ super(JSON.stringify(details));
18427
+ this.name = "CardMappingError";
18428
+ this.details = details;
18429
+ }
18430
+ };
18369
18431
  function isJsonObject(value) {
18370
18432
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
18371
18433
  }
@@ -18473,11 +18535,9 @@ var McpProxyTool = class {
18473
18535
  const tool = entry.tools.find((t) => t.name === toolName);
18474
18536
  if (!tool)
18475
18537
  return void 0;
18476
- const annotations = tool.annotations;
18477
18538
  return {
18478
18539
  inputSchema: tool.inputSchema,
18479
18540
  outputSchema: tool.outputSchema,
18480
- readOnly: annotations?.readOnlyHint,
18481
18541
  execute: (args, sig) => entry.server.callTool(tool.name, args, sig)
18482
18542
  };
18483
18543
  }
@@ -19690,6 +19750,7 @@ function wrapToolWithHooks(tool, dispatcher, commonParams, now = Date.now) {
19690
19750
 
19691
19751
  // ../linkque-agent-runtime/dist/esm/core/hook/noopHookExecutor.js
19692
19752
  var NoopHookExecutor = class {
19753
+ name = "NoopHookExecutor";
19693
19754
  execute(_resource, _context, _options) {
19694
19755
  return Promise.resolve({
19695
19756
  ok: true,
@@ -19808,6 +19869,7 @@ function buildLinkquegwEventBody(context) {
19808
19869
  };
19809
19870
  }
19810
19871
  var LinkquegwEventHookExecutor = class {
19872
+ name = EHookExecutorName.LinkquegwEventHookExecutor;
19811
19873
  baseUrl;
19812
19874
  getAuthHeaders;
19813
19875
  fetchImpl;
@@ -30194,10 +30256,10 @@ function coerceWithJsonSchema(value, schema) {
30194
30256
  nextValue = coerceWithUnionSchema(nextValue, schema.oneOf);
30195
30257
  }
30196
30258
  const schemaTypes = getSchemaTypes(schema);
30197
- const matchesUnionMember = schemaTypes.length > 1 && schemaTypes.some((schemaType) => matchesJsonType(nextValue, schemaType));
30259
+ const matchesUnionMember = schemaTypes.length > 1 && schemaTypes.some((schemaType2) => matchesJsonType(nextValue, schemaType2));
30198
30260
  if (schemaTypes.length > 0 && !matchesUnionMember) {
30199
- for (const schemaType of schemaTypes) {
30200
- const candidate = coercePrimitiveByType(nextValue, schemaType);
30261
+ for (const schemaType2 of schemaTypes) {
30262
+ const candidate = coercePrimitiveByType(nextValue, schemaType2);
30201
30263
  if (candidate !== nextValue) {
30202
30264
  nextValue = candidate;
30203
30265
  break;
@@ -52217,10 +52279,6 @@ ${currentTurn}`;
52217
52279
  }
52218
52280
 
52219
52281
  // ../linkque-agent-runtime/dist/esm/core/cardRenderMcpServer.js
52220
- var FALLBACK_SOURCE_INPUT_SCHEMA = {
52221
- type: "object",
52222
- additionalProperties: true
52223
- };
52224
52282
  var CardRenderMcpServer = class {
52225
52283
  resources;
52226
52284
  registry;
@@ -52322,7 +52380,7 @@ Select this card first, then call its card_source tools and card_finish tool.`,
52322
52380
  resourceId: resource.resourceId,
52323
52381
  label: source.purpose,
52324
52382
  description: `Use after selecting ${name}. ${source.purpose}. Choose all MCP arguments from the conversation and prior source results.`,
52325
- inputSchema: discoveredHandle?.inputSchema ?? FALLBACK_SOURCE_INPUT_SCHEMA,
52383
+ inputSchema: normalizeMcpInputSchema(discoveredHandle?.inputSchema),
52326
52384
  outputSchema: discoveredHandle?.outputSchema ?? source.outputSchema,
52327
52385
  execute: async (args, signal) => {
52328
52386
  if (!state2.selected || this.activeCardId !== resource.resourceId)
@@ -52333,12 +52391,21 @@ Select this card first, then call its card_source tools and card_finish tool.`,
52333
52391
  const handle = discoveredHandle ?? await this.registry.resolve(source.mcpResourceId, source.toolName, signal);
52334
52392
  if (!handle)
52335
52393
  throw new Error(`MCP tool ${source.mcpResourceId}/${source.toolName} is unavailable`);
52336
- if (handle.readOnly === false)
52337
- throw new Error(`Card data source ${source.mcpResourceId}/${source.toolName} must be read-only`);
52338
52394
  const result = await handle.execute(args, signal);
52339
52395
  const structured = readStructuredContent(result);
52340
- if (!structured)
52341
- throw new Error(`MCP tool ${source.mcpResourceId}/${source.toolName} did not return structured content`);
52396
+ if (structured === void 0) {
52397
+ const binding = config.bindings.find((candidate) => candidate.sourceId === source.sourceId);
52398
+ const field = config.fields.find((candidate) => candidate.fieldId === binding?.fieldId);
52399
+ throw new CardMappingError({
52400
+ code: "MCP_RESULT_UNSUPPORTED",
52401
+ cardId: resource.resourceId,
52402
+ fieldId: binding?.fieldId ?? "unknown",
52403
+ sourceId: source.sourceId,
52404
+ expectedType: field?.type ?? "json",
52405
+ actualType: "mixed-or-empty-content",
52406
+ retryable: false
52407
+ });
52408
+ }
52342
52409
  state2.results.set(source.sourceId, structured);
52343
52410
  return structured;
52344
52411
  }
@@ -52563,6 +52630,92 @@ var NoopKnowledgeSearchPort = class {
52563
52630
  }
52564
52631
  };
52565
52632
 
52633
+ // ../linkque-agent-runtime/dist/esm/core/agentRuntimePlugin.js
52634
+ var validatePlugins = (plugins) => {
52635
+ const names = /* @__PURE__ */ new Set();
52636
+ for (const plugin of plugins) {
52637
+ const name = plugin.name.trim();
52638
+ if (!name) {
52639
+ throw new Error("Agent runtime \u63D2\u4EF6 name \u4E0D\u80FD\u4E3A\u7A7A");
52640
+ }
52641
+ if (names.has(name)) {
52642
+ throw new Error(`Agent runtime \u63D2\u4EF6 name \u91CD\u590D: ${name}`);
52643
+ }
52644
+ names.add(name);
52645
+ }
52646
+ return plugins;
52647
+ };
52648
+ function assertAgentConfigRoot(value, pluginName) {
52649
+ if (typeof value !== "object" || value === null || !("agent" in value)) {
52650
+ throw new Error(`Agent runtime \u63D2\u4EF6 ${pluginName} \u7684 modifyAgentConfig \u8FD4\u56DE\u4E86\u65E0\u6548\u914D\u7F6E`);
52651
+ }
52652
+ }
52653
+ var applyAgentRuntimePluginConfig = (agentConfig, plugins = []) => {
52654
+ const validatedPlugins = validatePlugins(plugins);
52655
+ const modifiers = validatedPlugins.filter((plugin) => plugin.modifyAgentConfig !== void 0);
52656
+ if (modifiers.length === 0)
52657
+ return agentConfig;
52658
+ let current = structuredClone(agentConfig);
52659
+ for (const plugin of modifiers) {
52660
+ try {
52661
+ const modified = plugin.modifyAgentConfig?.(current);
52662
+ if (modified !== void 0) {
52663
+ assertAgentConfigRoot(modified, plugin.name);
52664
+ current = modified;
52665
+ }
52666
+ current = structuredClone(current);
52667
+ } catch (error) {
52668
+ throw new Error(`Agent runtime \u63D2\u4EF6 ${plugin.name} \u4FEE\u6539 Agent Config \u5931\u8D25`, { cause: error });
52669
+ }
52670
+ }
52671
+ return current;
52672
+ };
52673
+ var MergedHookExecutorHandler = class {
52674
+ executorByName;
52675
+ constructor(executorByName) {
52676
+ this.executorByName = executorByName;
52677
+ }
52678
+ async execute(resource, context, options) {
52679
+ const executor = this.executorByName.get(resource.config.executorName);
52680
+ if (executor)
52681
+ return executor.execute(resource, context, options);
52682
+ return {
52683
+ ok: true,
52684
+ skipped: true,
52685
+ code: "PLUGIN_EXECUTOR_NOT_FOUND",
52686
+ message: `\u672A\u627E\u5230 name=${resource.config.executorName} \u7684 hook executor`
52687
+ };
52688
+ }
52689
+ };
52690
+ var resolveAgentRuntimeHookExecutor = (plugins = [], portHookExecutorList = [], debugLog) => {
52691
+ const entries = validatePlugins(plugins).flatMap((plugin) => (plugin.hookExecutorList ?? []).map((executor, index2) => ({
52692
+ executor,
52693
+ source: `plugin:${plugin.name}[${index2}]`
52694
+ })));
52695
+ entries.push(...portHookExecutorList.map((executor, index2) => ({
52696
+ executor,
52697
+ source: `ports.hookExecutorList[${index2}]`
52698
+ })));
52699
+ if (entries.length === 0)
52700
+ return void 0;
52701
+ const merged = /* @__PURE__ */ new Map();
52702
+ for (const entry of entries) {
52703
+ const name = entry.executor.name;
52704
+ if (!name.trim())
52705
+ throw new Error(`Hook executor name \u4E0D\u80FD\u4E3A\u7A7A: ${entry.source}`);
52706
+ const previous = merged.get(name);
52707
+ if (previous) {
52708
+ try {
52709
+ debugLog?.(`[hook-executor-merge] name=${name} \u51B2\u7A81: ${previous.source} \u88AB ${entry.source} \u8986\u76D6`, EDebugLogScope.HOOK);
52710
+ } catch {
52711
+ }
52712
+ }
52713
+ merged.set(name, entry);
52714
+ }
52715
+ const executorByName = new Map([...merged].map(([name, entry]) => [name, entry.executor]));
52716
+ return new MergedHookExecutorHandler(executorByName);
52717
+ };
52718
+
52566
52719
  // ../linkque-agent-runtime/dist/esm/core/agentRuntime.js
52567
52720
  var DEFAULT_HARNESS_PROMPT = `# Harness \u8FD0\u884C\u89C4\u7EA6
52568
52721
 
@@ -52625,13 +52778,14 @@ var AgentRuntime = class _AgentRuntime {
52625
52778
  * 本 constructor 供单测/noop 调试或需要完全自定义默认的场景。
52626
52779
  */
52627
52780
  constructor(ports = {}) {
52781
+ const mergedHookExecutor = resolveAgentRuntimeHookExecutor(ports.plugins, ports.hookExecutorList, ports.debugLog);
52628
52782
  this.agentLoop = ports.agentLoop ?? new PiAgentLoop(void 0, ports.linkqueBaseUrl);
52629
52783
  this.followUpQuestionGenerator = ports.followUpQuestionGenerator ?? new PiFollowUpQuestionGenerator(ports.linkqueBaseUrl);
52630
52784
  this.sessionManager = ports.sessionManager ?? new InMemorySessionManager();
52631
52785
  this.skillLoader = ports.skillLoader ?? new NoopSkillLoader();
52632
52786
  this.mcpServerProvider = ports.mcpServerProvider ?? new NoopMcpServerProvider();
52633
52787
  this.knowledgeSearchPort = ports.knowledgeSearchPort ?? new NoopKnowledgeSearchPort();
52634
- this.hookExecutor = ports.hookExecutor ?? new NoopHookExecutor();
52788
+ this.hookExecutor = mergedHookExecutor ?? new NoopHookExecutor();
52635
52789
  this.hookMetricsSink = ports.hookMetricsSink;
52636
52790
  this.portsDebugLog = ports.debugLog;
52637
52791
  }
@@ -52647,31 +52801,32 @@ var AgentRuntime = class _AgentRuntime {
52647
52801
  * @param ports 可选端口覆盖(任一缺省走默认适配器)
52648
52802
  */
52649
52803
  static create(protocol, ports) {
52650
- const model = protocol.agent.model;
52651
- const skillResources = protocol.agent.resources.filter((r) => r.resourceType === EResourceType.SKILL);
52652
- const mcpResources = protocol.agent.resources.filter((r) => r.resourceType === EResourceType.MCP);
52653
- const mcpToolResources = protocol.agent.resources.filter((r) => r.resourceType === EResourceType.MCP_TOOL);
52654
- const cardResources = protocol.agent.resources.filter((r) => r.resourceType === EResourceType.CARD);
52655
- const knowledgeResources = protocol.agent.resources.filter((r) => r.resourceType === EResourceType.KNOWLEDGE);
52656
- const followUpQuestionGenerationEnabled = protocol.agent.resources.filter((resource) => resource.resourceType === EResourceType.FOLLOW_UP_QUESTIONS).some((resource) => resource.config.enable === true);
52657
- const mcpServerSpecs = aggregateMcpServers([...mcpResources, ...mcpToolResources], protocol.agentId);
52658
52804
  const portsArg = ports ?? {};
52805
+ const agentConfig = applyAgentRuntimePluginConfig(protocol, portsArg.plugins);
52806
+ const model = agentConfig.agent.model;
52807
+ const skillResources = agentConfig.agent.resources.filter((r) => r.resourceType === EResourceType.SKILL);
52808
+ const mcpResources = agentConfig.agent.resources.filter((r) => r.resourceType === EResourceType.MCP);
52809
+ const mcpToolResources = agentConfig.agent.resources.filter((r) => r.resourceType === EResourceType.MCP_TOOL);
52810
+ const cardResources = agentConfig.agent.resources.filter((r) => r.resourceType === EResourceType.CARD);
52811
+ const knowledgeResources = agentConfig.agent.resources.filter((r) => r.resourceType === EResourceType.KNOWLEDGE);
52812
+ const followUpQuestionGenerationEnabled = agentConfig.agent.resources.filter((resource) => resource.resourceType === EResourceType.FOLLOW_UP_QUESTIONS).some((resource) => resource.config.enable === true);
52813
+ const mcpServerSpecs = aggregateMcpServers([...mcpResources, ...mcpToolResources], agentConfig.agentId);
52659
52814
  const innerProvider = portsArg.mcpServerProvider ?? new NoopMcpServerProvider();
52660
52815
  const rt = new _AgentRuntime({
52661
52816
  ...portsArg,
52662
52817
  mcpServerProvider: new CardRenderMcpServerProvider(innerProvider)
52663
52818
  });
52664
- rt.defaultSystemPrompt = protocol.agent.systemPrompt.content;
52665
- rt.defaultHarnessPrompt = protocol.agent.harness ? protocol.agent.harness.systemPrompt : DEFAULT_HARNESS_PROMPT;
52819
+ rt.defaultSystemPrompt = agentConfig.agent.systemPrompt.content;
52820
+ rt.defaultHarnessPrompt = agentConfig.agent.harness ? agentConfig.agent.harness.systemPrompt : DEFAULT_HARNESS_PROMPT;
52666
52821
  rt.defaultModel = model.defaultModel || void 0;
52667
52822
  rt.defaultThinkingLevel = model.callParams?.thinkingLevel;
52668
52823
  rt.skillResources = skillResources;
52669
52824
  rt.mcpServerSpecs = mcpServerSpecs;
52670
52825
  rt.cardResources = cardResources;
52671
52826
  rt.knowledgeResources = knowledgeResources;
52672
- rt.agentId = protocol.agentId;
52827
+ rt.agentId = agentConfig.agentId;
52673
52828
  rt.followUpQuestionGenerationEnabled = followUpQuestionGenerationEnabled;
52674
- rt.hookResources = normalizeHookResources(protocol.agent.resources.filter((r) => r.resourceType === EResourceType.HOOK));
52829
+ rt.hookResources = normalizeHookResources(agentConfig.agent.resources.filter((r) => r.resourceType === EResourceType.HOOK));
52675
52830
  return rt;
52676
52831
  }
52677
52832
  async prepareFollowUpQuestionContext(sessionId, history = []) {
@@ -60279,6 +60434,9 @@ function setBakedProtocol(protocol) {
60279
60434
 
60280
60435
  export {
60281
60436
  EResourceType,
60437
+ EResourceProviderType,
60438
+ EHookStage,
60439
+ EHookExecutorName,
60282
60440
  executeHomeData,
60283
60441
  FOLLOW_UP_CONTEXT_MAX_HISTORY_RUNS,
60284
60442
  LinkquegwEventHookExecutor,