linkque-cli-v2 1.1.4 → 1.2.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 +744 -60
- package/package.json +1 -1
- package/vendor/linkque-agent-appwrite-integrate/dist/esm/{chunk-YAKPGLYU.js → chunk-HPOFEAMP.js} +498 -34
- package/vendor/linkque-agent-appwrite-integrate/dist/esm/{chunk-B56A2RV5.js → chunk-NRTXRI33.js} +155 -1
- package/vendor/linkque-agent-appwrite-integrate/dist/esm/runtime/handler/chat.js +222 -4
- package/vendor/linkque-agent-appwrite-integrate/dist/esm/runtime/handler/clientCapability.js +1 -1
- package/vendor/linkque-agent-appwrite-integrate/dist/esm/runtime/protocol.js +1 -1
- package/worker.js +618 -63
package/linkquejs.js
CHANGED
|
@@ -19550,6 +19550,30 @@ var SEARCH_DATABASE_ID = "linkque";
|
|
|
19550
19550
|
var SEARCH_TABLE_ID = "linkque_conversation_message_search";
|
|
19551
19551
|
var CLIENT_CAPABILITY_DATABASE_ID = "linkque";
|
|
19552
19552
|
var CLIENT_CAPABILITY_TABLE_ID = "linkque_client_cap_invocations";
|
|
19553
|
+
var A2UI_ACTION_DATABASE_ID = "linkque";
|
|
19554
|
+
var A2UI_ACTION_TABLE_ID = "linkque_a2ui_action_invocations";
|
|
19555
|
+
var A2UI_ACTION_TABLE_COLUMNS = [
|
|
19556
|
+
...["ownerId", "agentId", "threadId", "actionId", "cardRef", "cardRevision", "surfaceId", "componentId"].map((key) => ({
|
|
19557
|
+
key,
|
|
19558
|
+
type: "varchar",
|
|
19559
|
+
required: true,
|
|
19560
|
+
array: false,
|
|
19561
|
+
size: 128,
|
|
19562
|
+
default: null
|
|
19563
|
+
})),
|
|
19564
|
+
{ key: "cardInstanceId", type: "varchar", required: true, array: false, size: 256, default: null },
|
|
19565
|
+
{ key: "status", type: "varchar", required: true, array: false, size: 32, default: null },
|
|
19566
|
+
{ key: "actionContext", type: "text", required: true, array: false, size: 0, default: null },
|
|
19567
|
+
{ key: "tradeNO", type: "varchar", required: false, array: false, size: 64, default: null },
|
|
19568
|
+
{ key: "callback", type: "text", required: false, array: false, size: 0, default: null },
|
|
19569
|
+
{ key: "verification", type: "text", required: false, array: false, size: 0, default: null },
|
|
19570
|
+
{ key: "createdAt", type: "varchar", required: true, array: false, size: 32, default: null },
|
|
19571
|
+
{ key: "expiresAt", type: "varchar", required: true, array: false, size: 32, default: null }
|
|
19572
|
+
];
|
|
19573
|
+
var A2UI_ACTION_TABLE_INDEXES = [
|
|
19574
|
+
{ key: "thread_status", type: "key", columns: ["threadId", "status"], orders: ["asc", "asc"] },
|
|
19575
|
+
{ key: "expires_at", type: "key", columns: ["expiresAt"], orders: ["asc"] }
|
|
19576
|
+
];
|
|
19553
19577
|
var CLIENT_CAPABILITY_TABLE_COLUMNS = [
|
|
19554
19578
|
...["ownerId", "agentId", "threadId", "runId", "toolCallId"].map((key) => ({
|
|
19555
19579
|
key,
|
|
@@ -20148,6 +20172,8 @@ var FUNCTION_DEFS = [
|
|
|
20148
20172
|
value: CLIENT_CAPABILITY_DATABASE_ID
|
|
20149
20173
|
},
|
|
20150
20174
|
{ key: "CLIENT_CAPABILITY_TABLE_ID", value: CLIENT_CAPABILITY_TABLE_ID },
|
|
20175
|
+
{ key: "A2UI_ACTION_DATABASE_ID", value: A2UI_ACTION_DATABASE_ID },
|
|
20176
|
+
{ key: "A2UI_ACTION_TABLE_ID", value: A2UI_ACTION_TABLE_ID },
|
|
20151
20177
|
{ key: "RUN_CHUNK_SIZE", value: "10" },
|
|
20152
20178
|
{ key: "RUN_CANCEL_POLL_INTERVAL_MS", value: "500" },
|
|
20153
20179
|
// linkquegw 出站地址:线上套(无 x-linkque-env 头时生效)。
|
|
@@ -20198,6 +20224,8 @@ var FUNCTION_DEFS = [
|
|
|
20198
20224
|
value: CLIENT_CAPABILITY_DATABASE_ID
|
|
20199
20225
|
},
|
|
20200
20226
|
{ key: "CLIENT_CAPABILITY_TABLE_ID", value: CLIENT_CAPABILITY_TABLE_ID },
|
|
20227
|
+
{ key: "A2UI_ACTION_DATABASE_ID", value: A2UI_ACTION_DATABASE_ID },
|
|
20228
|
+
{ key: "A2UI_ACTION_TABLE_ID", value: A2UI_ACTION_TABLE_ID },
|
|
20201
20229
|
{
|
|
20202
20230
|
key: "LINKQUEGW_MCP_SIGN_ENDPOINT",
|
|
20203
20231
|
value: LINKQUEGW_MCP_SIGN_ENDPOINT
|
|
@@ -20273,6 +20301,16 @@ var TABLES = [
|
|
|
20273
20301
|
permissions: [],
|
|
20274
20302
|
columns: CLIENT_CAPABILITY_TABLE_COLUMNS,
|
|
20275
20303
|
indexes: CLIENT_CAPABILITY_TABLE_INDEXES
|
|
20304
|
+
},
|
|
20305
|
+
{
|
|
20306
|
+
$id: A2UI_ACTION_TABLE_ID,
|
|
20307
|
+
databaseId: A2UI_ACTION_DATABASE_ID,
|
|
20308
|
+
name: A2UI_ACTION_TABLE_ID,
|
|
20309
|
+
enabled: true,
|
|
20310
|
+
rowSecurity: true,
|
|
20311
|
+
permissions: [],
|
|
20312
|
+
columns: A2UI_ACTION_TABLE_COLUMNS,
|
|
20313
|
+
indexes: A2UI_ACTION_TABLE_INDEXES
|
|
20276
20314
|
}
|
|
20277
20315
|
];
|
|
20278
20316
|
var buildAppwriteConfigTemplate = (envOverrides = {}) => ({
|
|
@@ -21040,6 +21078,7 @@ var AGENT_LOG = ".linkque/logs/agent.log";
|
|
|
21040
21078
|
var HEALTH_PATH = "/healthz";
|
|
21041
21079
|
var RUNS_PATH = "/runs";
|
|
21042
21080
|
var HOME_DATA_PATH = "/home-data";
|
|
21081
|
+
var A2UI_ACTION_PATH = "/a2ui-action";
|
|
21043
21082
|
var AGENT_RUNTIME_PROTOCOL = "conversation-runs-v2";
|
|
21044
21083
|
var API_KEY_ENV = "LINKQUE_AGENT_API_KEY";
|
|
21045
21084
|
var stopped = (health) => ({
|
|
@@ -21307,6 +21346,7 @@ var spawnAgentOwner = (cwd, config2, port, apiKey, statePath) => {
|
|
|
21307
21346
|
cwd,
|
|
21308
21347
|
healthPath: HEALTH_PATH,
|
|
21309
21348
|
homeDataPath: HOME_DATA_PATH,
|
|
21349
|
+
a2uiActionPath: A2UI_ACTION_PATH,
|
|
21310
21350
|
logPath: import_path15.default.join(cwd, AGENT_LOG),
|
|
21311
21351
|
port,
|
|
21312
21352
|
runsPath: RUNS_PATH,
|
|
@@ -25304,6 +25344,7 @@ function analyzeA2UIBindings(protocol) {
|
|
|
25304
25344
|
const listRoots = inferListRoots(components, issues);
|
|
25305
25345
|
for (const component of components) {
|
|
25306
25346
|
const context = {
|
|
25347
|
+
surfaceId,
|
|
25307
25348
|
component,
|
|
25308
25349
|
listRoot: listRoots.get(component.id),
|
|
25309
25350
|
occurrences,
|
|
@@ -25317,6 +25358,7 @@ function analyzeA2UIBindings(protocol) {
|
|
|
25317
25358
|
issues.push(issue2(`card.surfaces.${surfaceId}.components.${component.id}.children.path`, "INVALID_BINDING_PATH", `Invalid list path ${children.path}`));
|
|
25318
25359
|
} else {
|
|
25319
25360
|
occurrences.push({
|
|
25361
|
+
surfaceId,
|
|
25320
25362
|
componentId: component.id,
|
|
25321
25363
|
componentName: component.component,
|
|
25322
25364
|
propertyPath: "children.path",
|
|
@@ -25335,6 +25377,16 @@ function analyzeA2UIBindings(protocol) {
|
|
|
25335
25377
|
issues
|
|
25336
25378
|
};
|
|
25337
25379
|
}
|
|
25380
|
+
function collectReferencedA2UICardBusinessFieldIds(protocol, fields) {
|
|
25381
|
+
const businessFields = fields.filter((field) => field.source === "business").slice().sort((left, right) => right.path.length - left.path.length);
|
|
25382
|
+
const result = /* @__PURE__ */ new Set();
|
|
25383
|
+
for (const occurrence of analyzeA2UIBindings(protocol).occurrences) {
|
|
25384
|
+
const field = businessFields.find((candidate) => occurrence.canonicalPath === candidate.path || occurrence.canonicalPath.startsWith(`${candidate.path}/*/`) || occurrence.canonicalPath.startsWith(`${candidate.path}/`));
|
|
25385
|
+
if (field)
|
|
25386
|
+
result.add(field.fieldId);
|
|
25387
|
+
}
|
|
25388
|
+
return result;
|
|
25389
|
+
}
|
|
25338
25390
|
function visitValue(value, propertyPath, context) {
|
|
25339
25391
|
if (propertyPath === "action.local.path" && typeof value === "string") {
|
|
25340
25392
|
addOccurrence(value, propertyPath, "local", context);
|
|
@@ -25366,6 +25418,7 @@ function addOccurrence(rawPath, propertyPath, usage, context) {
|
|
|
25366
25418
|
return;
|
|
25367
25419
|
}
|
|
25368
25420
|
context.occurrences.push({
|
|
25421
|
+
surfaceId: context.surfaceId,
|
|
25369
25422
|
componentId: context.component.id,
|
|
25370
25423
|
componentName: context.component.component,
|
|
25371
25424
|
propertyPath,
|
|
@@ -26051,12 +26104,15 @@ function validateA2UICardBindings(input, options = {}) {
|
|
|
26051
26104
|
readyCandidates.set(candidateKey(candidate.resourceId, candidate.tool.toolName), candidate.tool);
|
|
26052
26105
|
}
|
|
26053
26106
|
const bindingByField = new Map(bindings.map((binding) => [binding.fieldId, binding]));
|
|
26107
|
+
const requiredFields = collectReferencedA2UICardBusinessFieldIds(input.draft.card, input.draft.manifest.fields.filter((field) => field.required));
|
|
26054
26108
|
for (const field of input.draft.manifest.fields) {
|
|
26055
26109
|
if (field.source !== "business")
|
|
26056
26110
|
continue;
|
|
26057
26111
|
if (selectedFields && !selectedFields.has(field.fieldId))
|
|
26058
26112
|
continue;
|
|
26059
26113
|
const binding = bindingByField.get(field.fieldId);
|
|
26114
|
+
if (!selectedFields && !requiredFields.has(field.fieldId) && !binding)
|
|
26115
|
+
continue;
|
|
26060
26116
|
if (!binding) {
|
|
26061
26117
|
issues.push(issue2(`fields.${field.fieldId}`, "CARD_BINDING_INCOMPLETE", `business field ${field.fieldId} has not been bound`));
|
|
26062
26118
|
continue;
|
|
@@ -26300,6 +26356,192 @@ function candidateKey(resourceId, toolName) {
|
|
|
26300
26356
|
return `${resourceId}\0${toolName}`;
|
|
26301
26357
|
}
|
|
26302
26358
|
|
|
26359
|
+
// ../agent-a2ui-card-contract/dist/esm/payment-action.js
|
|
26360
|
+
var A2UI_PAYMENT_ACTION_NAME = "payment.trade-pay";
|
|
26361
|
+
var A2UI_PAYMENT_CALLBACK_MAX_BYTES = 16 * 1024;
|
|
26362
|
+
function getA2UICardActionBindingIssues(value, path22 = "actionBindings") {
|
|
26363
|
+
if (!Array.isArray(value)) {
|
|
26364
|
+
return [issue2(path22, "INVALID_ACTION_BINDINGS", "actionBindings must be an array")];
|
|
26365
|
+
}
|
|
26366
|
+
const issues = [];
|
|
26367
|
+
const actionIds = /* @__PURE__ */ new Set();
|
|
26368
|
+
value.forEach((candidate, index) => {
|
|
26369
|
+
const itemPath = `${path22}[${index}]`;
|
|
26370
|
+
if (!isPlainRecord(candidate)) {
|
|
26371
|
+
issues.push(issue2(itemPath, "INVALID_ACTION_BINDING", "action binding must be an object"));
|
|
26372
|
+
return;
|
|
26373
|
+
}
|
|
26374
|
+
if (!isSafeIdentifier(candidate.actionId)) {
|
|
26375
|
+
issues.push(issue2(`${itemPath}.actionId`, "INVALID_ACTION_ID", "actionId is invalid"));
|
|
26376
|
+
} else if (actionIds.has(candidate.actionId)) {
|
|
26377
|
+
issues.push(issue2(`${itemPath}.actionId`, "DUPLICATE_ACTION_ID", "actionId must be unique"));
|
|
26378
|
+
} else {
|
|
26379
|
+
actionIds.add(candidate.actionId);
|
|
26380
|
+
}
|
|
26381
|
+
if (candidate.kind !== "payment") {
|
|
26382
|
+
issues.push(issue2(`${itemPath}.kind`, "INVALID_ACTION_KIND", "kind must be payment"));
|
|
26383
|
+
return;
|
|
26384
|
+
}
|
|
26385
|
+
rejectExtraKeys(candidate, ["actionId", "kind", "createTrade", "queryTrade"], itemPath, issues);
|
|
26386
|
+
validateToolBinding(candidate.createTrade, `${itemPath}.createTrade`, "create", issues);
|
|
26387
|
+
validateToolBinding(candidate.queryTrade, `${itemPath}.queryTrade`, "query", issues);
|
|
26388
|
+
});
|
|
26389
|
+
return issues;
|
|
26390
|
+
}
|
|
26391
|
+
function getA2UIPaymentActionReferenceIssues(card, bindings, path22 = "a2uiTemplate.content") {
|
|
26392
|
+
const issues = [];
|
|
26393
|
+
const bindingIds = new Set(bindings.map((binding) => binding.actionId));
|
|
26394
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
26395
|
+
card.forEach((instruction, instructionIndex) => {
|
|
26396
|
+
const instructionRecord = instruction;
|
|
26397
|
+
const update = isPlainRecord(instructionRecord.updateComponents) ? instructionRecord.updateComponents : void 0;
|
|
26398
|
+
const components = update && Array.isArray(update.components) ? update.components : [];
|
|
26399
|
+
components.forEach((component, componentIndex) => {
|
|
26400
|
+
if (!isPlainRecord(component))
|
|
26401
|
+
return;
|
|
26402
|
+
collectPaymentActionReferences(component.action, `${path22}[${instructionIndex}].updateComponents.components[${componentIndex}].action`, referenced, issues);
|
|
26403
|
+
});
|
|
26404
|
+
});
|
|
26405
|
+
for (const actionId of referenced) {
|
|
26406
|
+
if (!bindingIds.has(actionId)) {
|
|
26407
|
+
issues.push(issue2(path22, "PAYMENT_ACTION_BINDING_MISSING", `payment action ${actionId} has no action binding`));
|
|
26408
|
+
}
|
|
26409
|
+
}
|
|
26410
|
+
for (const actionId of bindingIds) {
|
|
26411
|
+
if (!referenced.has(actionId)) {
|
|
26412
|
+
issues.push(issue2(path22, "PAYMENT_ACTION_UNUSED", `payment action binding ${actionId} is not referenced`));
|
|
26413
|
+
}
|
|
26414
|
+
}
|
|
26415
|
+
return issues;
|
|
26416
|
+
}
|
|
26417
|
+
function validateToolBinding(value, path22, phase, issues) {
|
|
26418
|
+
if (!isPlainRecord(value)) {
|
|
26419
|
+
issues.push(issue2(path22, "INVALID_PAYMENT_TOOL", `${path22} must be an object`));
|
|
26420
|
+
return;
|
|
26421
|
+
}
|
|
26422
|
+
rejectExtraKeys(value, phase === "create" ? ["mcpResourceId", "toolName", "inputBindings", "tradeNoPath"] : ["mcpResourceId", "toolName", "inputBindings", "statusPath", "paidValues"], path22, issues);
|
|
26423
|
+
if (!isSafeIdentifier(value.mcpResourceId)) {
|
|
26424
|
+
issues.push(issue2(`${path22}.mcpResourceId`, "INVALID_MCP_RESOURCE_ID", "mcpResourceId is invalid"));
|
|
26425
|
+
}
|
|
26426
|
+
if (typeof value.toolName !== "string" || !value.toolName.trim()) {
|
|
26427
|
+
issues.push(issue2(`${path22}.toolName`, "INVALID_TOOL_NAME", "toolName is required"));
|
|
26428
|
+
}
|
|
26429
|
+
validateInputBindings2(value.inputBindings, `${path22}.inputBindings`, phase, issues);
|
|
26430
|
+
if (phase === "create") {
|
|
26431
|
+
if (!isSafeJsonPointer(value.tradeNoPath)) {
|
|
26432
|
+
issues.push(issue2(`${path22}.tradeNoPath`, "INVALID_TRADE_NO_PATH", "tradeNoPath must be an absolute JSON Pointer"));
|
|
26433
|
+
}
|
|
26434
|
+
return;
|
|
26435
|
+
}
|
|
26436
|
+
if (!isSafeJsonPointer(value.statusPath)) {
|
|
26437
|
+
issues.push(issue2(`${path22}.statusPath`, "INVALID_PAYMENT_STATUS_PATH", "statusPath must be an absolute JSON Pointer"));
|
|
26438
|
+
}
|
|
26439
|
+
if (!Array.isArray(value.inputBindings) || !value.inputBindings.some((binding) => isPlainRecord(binding) && isPlainRecord(binding.source) && binding.source.kind === "trade-no")) {
|
|
26440
|
+
issues.push(issue2(`${path22}.inputBindings`, "PAYMENT_TRADE_NO_BINDING_MISSING", "queryTrade must map tradeNO"));
|
|
26441
|
+
}
|
|
26442
|
+
if (!Array.isArray(value.paidValues) || value.paidValues.length === 0 || value.paidValues.some((entry) => !isJsonPrimitive(entry))) {
|
|
26443
|
+
issues.push(issue2(`${path22}.paidValues`, "INVALID_PAID_VALUES", "paidValues must contain JSON primitives"));
|
|
26444
|
+
}
|
|
26445
|
+
}
|
|
26446
|
+
function collectPaymentActionReferences(value, path22, referenced, issues) {
|
|
26447
|
+
if (!isPlainRecord(value))
|
|
26448
|
+
return;
|
|
26449
|
+
if (isPlainRecord(value.client) && value.client.name === A2UI_PAYMENT_ACTION_NAME) {
|
|
26450
|
+
const context = value.client.context;
|
|
26451
|
+
if (!isPlainRecord(context) || !isSafeIdentifier(context.actionId)) {
|
|
26452
|
+
issues.push(issue2(path22, "INVALID_PAYMENT_ACTION", "payment action requires a safe actionId"));
|
|
26453
|
+
return;
|
|
26454
|
+
}
|
|
26455
|
+
rejectExtraKeys(context, ["actionId", "actionContext"], `${path22}.client.context`, issues);
|
|
26456
|
+
if (!isPlainRecord(context.actionContext)) {
|
|
26457
|
+
issues.push(issue2(path22, "INVALID_PAYMENT_ACTION", "payment action requires an object actionContext"));
|
|
26458
|
+
}
|
|
26459
|
+
referenced.add(context.actionId);
|
|
26460
|
+
return;
|
|
26461
|
+
}
|
|
26462
|
+
if (!isPlainRecord(value.select))
|
|
26463
|
+
return;
|
|
26464
|
+
if (Array.isArray(value.select.cases)) {
|
|
26465
|
+
value.select.cases.forEach((entry, index) => {
|
|
26466
|
+
if (isPlainRecord(entry)) {
|
|
26467
|
+
collectPaymentActionReferences(entry.action, `${path22}.select.cases[${index}].action`, referenced, issues);
|
|
26468
|
+
}
|
|
26469
|
+
});
|
|
26470
|
+
}
|
|
26471
|
+
collectPaymentActionReferences(value.select.default, `${path22}.select.default`, referenced, issues);
|
|
26472
|
+
}
|
|
26473
|
+
function validateInputBindings2(value, path22, phase, issues) {
|
|
26474
|
+
if (!Array.isArray(value) || value.length > 128) {
|
|
26475
|
+
issues.push(issue2(path22, "INVALID_PAYMENT_INPUT_BINDINGS", "inputBindings must be an array of at most 128 entries"));
|
|
26476
|
+
return;
|
|
26477
|
+
}
|
|
26478
|
+
const targets = /* @__PURE__ */ new Set();
|
|
26479
|
+
value.forEach((candidate, index) => {
|
|
26480
|
+
const itemPath = `${path22}[${index}]`;
|
|
26481
|
+
if (!isPlainRecord(candidate) || !isSafeJsonPointer(candidate.inputPath)) {
|
|
26482
|
+
issues.push(issue2(itemPath, "INVALID_PAYMENT_INPUT_BINDING", "inputPath must be an absolute JSON Pointer"));
|
|
26483
|
+
return;
|
|
26484
|
+
}
|
|
26485
|
+
rejectExtraKeys(candidate, ["inputPath", "source"], itemPath, issues);
|
|
26486
|
+
if (targets.has(candidate.inputPath)) {
|
|
26487
|
+
issues.push(issue2(`${itemPath}.inputPath`, "DUPLICATE_PAYMENT_INPUT_PATH", "inputPath must be unique"));
|
|
26488
|
+
}
|
|
26489
|
+
targets.add(candidate.inputPath);
|
|
26490
|
+
validateInputSource(candidate.source, `${itemPath}.source`, phase, issues);
|
|
26491
|
+
});
|
|
26492
|
+
}
|
|
26493
|
+
function validateInputSource(value, path22, phase, issues) {
|
|
26494
|
+
if (!isPlainRecord(value) || typeof value.kind !== "string") {
|
|
26495
|
+
issues.push(issue2(path22, "INVALID_PAYMENT_INPUT_SOURCE", "source must be an object"));
|
|
26496
|
+
return;
|
|
26497
|
+
}
|
|
26498
|
+
if (value.kind === "action-context") {
|
|
26499
|
+
rejectExtraKeys(value, ["kind", "path"], path22, issues);
|
|
26500
|
+
if (!isSafeJsonPointer(value.path, true)) {
|
|
26501
|
+
issues.push(issue2(`${path22}.path`, "INVALID_PAYMENT_SOURCE_PATH", "action context path must be a JSON Pointer"));
|
|
26502
|
+
}
|
|
26503
|
+
return;
|
|
26504
|
+
}
|
|
26505
|
+
if (value.kind === "runtime-context") {
|
|
26506
|
+
rejectExtraKeys(value, ["kind", "key"], path22, issues);
|
|
26507
|
+
if (!RUNTIME_CONTEXT_KEYS2.has(String(value.key))) {
|
|
26508
|
+
issues.push(issue2(`${path22}.key`, "INVALID_RUNTIME_CONTEXT_KEY", "runtime context key is unsupported"));
|
|
26509
|
+
}
|
|
26510
|
+
return;
|
|
26511
|
+
}
|
|
26512
|
+
if (value.kind === "literal") {
|
|
26513
|
+
rejectExtraKeys(value, ["kind", "value"], path22, issues);
|
|
26514
|
+
if (!isJsonValue(value.value)) {
|
|
26515
|
+
issues.push(issue2(`${path22}.value`, "INVALID_PAYMENT_LITERAL", "literal must be JSON"));
|
|
26516
|
+
}
|
|
26517
|
+
return;
|
|
26518
|
+
}
|
|
26519
|
+
if (phase === "query" && value.kind === "trade-no") {
|
|
26520
|
+
rejectExtraKeys(value, ["kind"], path22, issues);
|
|
26521
|
+
return;
|
|
26522
|
+
}
|
|
26523
|
+
if (phase === "query" && value.kind === "callback") {
|
|
26524
|
+
rejectExtraKeys(value, ["kind", "path"], path22, issues);
|
|
26525
|
+
if (!isSafeJsonPointer(value.path, true)) {
|
|
26526
|
+
issues.push(issue2(`${path22}.path`, "INVALID_PAYMENT_CALLBACK_PATH", "callback path must be a JSON Pointer"));
|
|
26527
|
+
}
|
|
26528
|
+
return;
|
|
26529
|
+
}
|
|
26530
|
+
issues.push(issue2(path22, "INVALID_PAYMENT_INPUT_SOURCE", `${value.kind} is unavailable for ${phase}`));
|
|
26531
|
+
}
|
|
26532
|
+
function rejectExtraKeys(value, allowed, path22, issues) {
|
|
26533
|
+
const allowedKeys = new Set(allowed);
|
|
26534
|
+
for (const key of Object.keys(value)) {
|
|
26535
|
+
if (!allowedKeys.has(key)) {
|
|
26536
|
+
issues.push(issue2(`${path22}.${key}`, "UNEXPECTED_ACTION_BINDING_FIELD", `${key} is not allowed`));
|
|
26537
|
+
}
|
|
26538
|
+
}
|
|
26539
|
+
}
|
|
26540
|
+
function isJsonPrimitive(value) {
|
|
26541
|
+
return value === null || ["string", "number", "boolean"].includes(typeof value);
|
|
26542
|
+
}
|
|
26543
|
+
var RUNTIME_CONTEXT_KEYS2 = /* @__PURE__ */ new Set(["agentId", "tenantId", "threadId", "userId"]);
|
|
26544
|
+
|
|
26303
26545
|
// ../agent-a2ui-card-contract/dist/esm/validation.js
|
|
26304
26546
|
var FIELD_TYPES = [
|
|
26305
26547
|
"array",
|
|
@@ -26315,7 +26557,7 @@ var FIELD_SOURCES = [
|
|
|
26315
26557
|
"runtime-context",
|
|
26316
26558
|
"local"
|
|
26317
26559
|
];
|
|
26318
|
-
var
|
|
26560
|
+
var RUNTIME_CONTEXT_KEYS3 = /* @__PURE__ */ new Set([
|
|
26319
26561
|
"agentId",
|
|
26320
26562
|
"tenantId",
|
|
26321
26563
|
"threadId",
|
|
@@ -26339,11 +26581,14 @@ function getA2UICardDraftBundleIssues(value) {
|
|
|
26339
26581
|
}
|
|
26340
26582
|
const issues = [];
|
|
26341
26583
|
exactKeys(value, ["manifest", "card", "fixture"], "draft", issues);
|
|
26342
|
-
validateManifest(value.manifest, "draft.manifest", issues);
|
|
26584
|
+
const manifest = validateManifest(value.manifest, "draft.manifest", issues);
|
|
26343
26585
|
const card = validateProtocol(value.card, "draft.card", issues);
|
|
26344
26586
|
if (card) {
|
|
26345
26587
|
const analysis = analyzeA2UIBindings(card);
|
|
26346
26588
|
issues.push(...analysis.issues);
|
|
26589
|
+
if (manifest?.schemaVersion === "3") {
|
|
26590
|
+
issues.push(...getA2UIPaymentActionReferenceIssues(card, manifest.actionBindings ?? [], "draft.card"));
|
|
26591
|
+
}
|
|
26347
26592
|
}
|
|
26348
26593
|
return issues;
|
|
26349
26594
|
}
|
|
@@ -26394,9 +26639,9 @@ function getA2UICardResourceConfigIssues(value) {
|
|
|
26394
26639
|
validateLooseJsonSchema(value.presentationInputSchema, "config.presentationInputSchema", issues);
|
|
26395
26640
|
const fields = validateFields(value.fields, "config.fields", issues);
|
|
26396
26641
|
const resolver = validateResolver(value.resolver, "config.resolver", issues);
|
|
26397
|
-
validateTemplate(value.a2uiTemplate, fields, issues);
|
|
26642
|
+
const card = validateTemplate(value.a2uiTemplate, fields, issues);
|
|
26398
26643
|
if (fields && resolver)
|
|
26399
|
-
validateResolverCoverage(fields, resolver, issues);
|
|
26644
|
+
validateResolverCoverage(fields, resolver, issues, card);
|
|
26400
26645
|
return issues;
|
|
26401
26646
|
}
|
|
26402
26647
|
function getA2UICardResourceConfigV3Issues(value) {
|
|
@@ -26411,6 +26656,7 @@ function getA2UICardResourceConfigV3Issues(value) {
|
|
|
26411
26656
|
"fields",
|
|
26412
26657
|
"dataSources",
|
|
26413
26658
|
"bindings",
|
|
26659
|
+
"actionBindings",
|
|
26414
26660
|
"a2uiTemplate"
|
|
26415
26661
|
], "config", issues);
|
|
26416
26662
|
if (value.schemaVersion !== A2UI_CARD_SCHEMA_VERSION) {
|
|
@@ -26429,9 +26675,14 @@ function getA2UICardResourceConfigV3Issues(value) {
|
|
|
26429
26675
|
}
|
|
26430
26676
|
const dataSources = validateDataSources(value.dataSources, "config.dataSources", issues);
|
|
26431
26677
|
const bindings = validateV3OutputBindings(value.bindings, "config.bindings", issues);
|
|
26432
|
-
validateTemplate(value.a2uiTemplate, fields, issues);
|
|
26678
|
+
const card = validateTemplate(value.a2uiTemplate, fields, issues);
|
|
26679
|
+
const actionBindings = value.actionBindings === void 0 ? [] : value.actionBindings;
|
|
26680
|
+
issues.push(...getA2UICardActionBindingIssues(actionBindings, "config.actionBindings"));
|
|
26681
|
+
if (card && Array.isArray(actionBindings)) {
|
|
26682
|
+
issues.push(...getA2UIPaymentActionReferenceIssues(card, actionBindings, "config.a2uiTemplate.content"));
|
|
26683
|
+
}
|
|
26433
26684
|
if (fields && dataSources && bindings) {
|
|
26434
|
-
validateV3Coverage(fields, dataSources, bindings, issues);
|
|
26685
|
+
validateV3Coverage(fields, dataSources, bindings, issues, { card });
|
|
26435
26686
|
}
|
|
26436
26687
|
return issues;
|
|
26437
26688
|
}
|
|
@@ -26545,7 +26796,8 @@ function validateV3Coverage(fields, dataSources, bindings, issues, options = {})
|
|
|
26545
26796
|
bound.add(binding.fieldId);
|
|
26546
26797
|
});
|
|
26547
26798
|
if (options.requireComplete !== false) {
|
|
26548
|
-
|
|
26799
|
+
const requiredFields = options.card ? collectReferencedA2UICardBusinessFieldIds(options.card, fields.filter((field) => field.required)) : new Set(fields.filter((field) => field.source === "business" && field.required).map((field) => field.fieldId));
|
|
26800
|
+
for (const fieldId of requiredFields) {
|
|
26549
26801
|
if (bound.has(fieldId))
|
|
26550
26802
|
continue;
|
|
26551
26803
|
issues.push(issue2("config.bindings", "CARD_BINDING_INCOMPLETE", `business field ${fieldId} is not bound`));
|
|
@@ -26634,7 +26886,8 @@ function validateManifest(value, path22, issues) {
|
|
|
26634
26886
|
"whenToUse",
|
|
26635
26887
|
"whenNotToUse",
|
|
26636
26888
|
"fields",
|
|
26637
|
-
"bindings"
|
|
26889
|
+
"bindings",
|
|
26890
|
+
"actionBindings"
|
|
26638
26891
|
], path22, issues);
|
|
26639
26892
|
if (!isSafeIdentifier(value.draftId)) {
|
|
26640
26893
|
issues.push(issue2(`${path22}.draftId`, "INVALID_DRAFT_ID", "draftId is invalid"));
|
|
@@ -26654,6 +26907,9 @@ function validateManifest(value, path22, issues) {
|
|
|
26654
26907
|
if (value.bindings !== void 0) {
|
|
26655
26908
|
validateV3BindingSelections(value.bindings, `${path22}.bindings`, fields2, issues);
|
|
26656
26909
|
}
|
|
26910
|
+
if (value.actionBindings !== void 0) {
|
|
26911
|
+
issues.push(...getA2UICardActionBindingIssues(value.actionBindings, `${path22}.actionBindings`));
|
|
26912
|
+
}
|
|
26657
26913
|
return fields2 ? value : void 0;
|
|
26658
26914
|
}
|
|
26659
26915
|
exactKeys(value, [
|
|
@@ -26761,7 +27017,7 @@ function validateFieldSource(candidate, itemPath, issues) {
|
|
|
26761
27017
|
issues.push(issue2(`${itemPath}.sourcePath`, "UNEXPECTED_FIELD_SOURCE_PATH", "sourcePath is only allowed for invocation fields"));
|
|
26762
27018
|
}
|
|
26763
27019
|
if (candidate.source === "runtime-context") {
|
|
26764
|
-
if (!
|
|
27020
|
+
if (!RUNTIME_CONTEXT_KEYS3.has(candidate.runtimeContextKey)) {
|
|
26765
27021
|
issues.push(issue2(`${itemPath}.runtimeContextKey`, "INVALID_RUNTIME_CONTEXT", "runtime-context fields require an allowed runtimeContextKey"));
|
|
26766
27022
|
}
|
|
26767
27023
|
} else if (candidate.runtimeContextKey !== void 0) {
|
|
@@ -26813,11 +27069,11 @@ function validateResolverCalls(value, path22, issues) {
|
|
|
26813
27069
|
issues.push(issue2(`${itemPath}.mcpResourceId`, "INVALID_MCP_RESOURCE_ID", "mcpResourceId is invalid"));
|
|
26814
27070
|
}
|
|
26815
27071
|
requiredString(candidate.toolName, `${itemPath}.toolName`, issues);
|
|
26816
|
-
|
|
27072
|
+
validateInputBindings3(candidate.inputBindings, `${itemPath}.inputBindings`, issues);
|
|
26817
27073
|
});
|
|
26818
27074
|
return value;
|
|
26819
27075
|
}
|
|
26820
|
-
function
|
|
27076
|
+
function validateInputBindings3(value, path22, issues) {
|
|
26821
27077
|
if (!Array.isArray(value)) {
|
|
26822
27078
|
issues.push(issue2(path22, "INVALID_INPUT_BINDINGS", "inputBindings must be an array"));
|
|
26823
27079
|
return void 0;
|
|
@@ -26837,11 +27093,11 @@ function validateInputBindings2(value, path22, issues) {
|
|
|
26837
27093
|
} else {
|
|
26838
27094
|
paths.add(candidate.inputPath);
|
|
26839
27095
|
}
|
|
26840
|
-
|
|
27096
|
+
validateInputSource2(candidate.source, `${itemPath}.source`, issues);
|
|
26841
27097
|
});
|
|
26842
27098
|
return value;
|
|
26843
27099
|
}
|
|
26844
|
-
function
|
|
27100
|
+
function validateInputSource2(value, path22, issues) {
|
|
26845
27101
|
if (!isPlainRecord(value)) {
|
|
26846
27102
|
issues.push(issue2(path22, "INVALID_INPUT_SOURCE", "source must be an object"));
|
|
26847
27103
|
return;
|
|
@@ -26855,7 +27111,7 @@ function validateInputSource(value, path22, issues) {
|
|
|
26855
27111
|
}
|
|
26856
27112
|
if (value.kind === "runtime-context") {
|
|
26857
27113
|
exactKeys(value, ["kind", "key"], path22, issues);
|
|
26858
|
-
if (!
|
|
27114
|
+
if (!RUNTIME_CONTEXT_KEYS3.has(value.key)) {
|
|
26859
27115
|
issues.push(issue2(`${path22}.key`, "INVALID_RUNTIME_CONTEXT", "runtime context key is invalid"));
|
|
26860
27116
|
}
|
|
26861
27117
|
return;
|
|
@@ -26983,13 +27239,13 @@ function validateInstructionShape(instruction, path22, issues) {
|
|
|
26983
27239
|
function validateTemplate(value, fields, issues) {
|
|
26984
27240
|
if (!isPlainRecord(value)) {
|
|
26985
27241
|
issues.push(issue2("config.a2uiTemplate", "INVALID_A2UI_TEMPLATE", "a2uiTemplate must be an object"));
|
|
26986
|
-
return;
|
|
27242
|
+
return void 0;
|
|
26987
27243
|
}
|
|
26988
27244
|
exactKeys(value, ["argsSchema", "content"], "config.a2uiTemplate", issues);
|
|
26989
27245
|
validateLooseJsonSchema(value.argsSchema, "config.a2uiTemplate.argsSchema", issues);
|
|
26990
27246
|
if (typeof value.content !== "string") {
|
|
26991
27247
|
issues.push(issue2("config.a2uiTemplate.content", "INVALID_A2UI_CONTENT", "a2uiTemplate.content must be serialized A2UI JSON"));
|
|
26992
|
-
return;
|
|
27248
|
+
return void 0;
|
|
26993
27249
|
}
|
|
26994
27250
|
try {
|
|
26995
27251
|
const card = validateProtocol(JSON.parse(value.content), "config.a2uiTemplate.content", issues);
|
|
@@ -26997,9 +27253,11 @@ function validateTemplate(value, fields, issues) {
|
|
|
26997
27253
|
const analysis = analyzeA2UIBindings(card);
|
|
26998
27254
|
issues.push(...analysis.issues);
|
|
26999
27255
|
}
|
|
27256
|
+
return card;
|
|
27000
27257
|
} catch {
|
|
27001
27258
|
issues.push(issue2("config.a2uiTemplate.content", "INVALID_A2UI_CONTENT", "a2uiTemplate.content must contain valid JSON"));
|
|
27002
27259
|
}
|
|
27260
|
+
return void 0;
|
|
27003
27261
|
}
|
|
27004
27262
|
function validateLooseJsonSchema(value, path22, issues) {
|
|
27005
27263
|
const schemaIssues = getJsonSchemaIssues(value, path22, {
|
|
@@ -27008,7 +27266,7 @@ function validateLooseJsonSchema(value, path22, issues) {
|
|
|
27008
27266
|
issues.push(...schemaIssues);
|
|
27009
27267
|
return schemaIssues.length === 0;
|
|
27010
27268
|
}
|
|
27011
|
-
function validateResolverCoverage(fields, resolver, issues) {
|
|
27269
|
+
function validateResolverCoverage(fields, resolver, issues, card) {
|
|
27012
27270
|
const businessFields = new Set(fields.filter((field) => field.source === "business").map((field) => field.fieldId));
|
|
27013
27271
|
const bound = /* @__PURE__ */ new Set();
|
|
27014
27272
|
resolver.outputBindings.forEach((binding, index) => {
|
|
@@ -27017,7 +27275,8 @@ function validateResolverCoverage(fields, resolver, issues) {
|
|
|
27017
27275
|
}
|
|
27018
27276
|
bound.add(binding.fieldId);
|
|
27019
27277
|
});
|
|
27020
|
-
|
|
27278
|
+
const requiredFields = card ? collectReferencedA2UICardBusinessFieldIds(card, fields.filter((field) => field.required)) : new Set(fields.filter((field) => field.source === "business" && field.required).map((field) => field.fieldId));
|
|
27279
|
+
for (const fieldId of requiredFields) {
|
|
27021
27280
|
if (!bound.has(fieldId)) {
|
|
27022
27281
|
issues.push(issue2("config.resolver.outputBindings", "CARD_BINDING_INCOMPLETE", `business field ${fieldId} is not bound`));
|
|
27023
27282
|
}
|
|
@@ -27065,6 +27324,7 @@ function compileA2UICardResource(input) {
|
|
|
27065
27324
|
}
|
|
27066
27325
|
const fieldById = new Map(draft.manifest.fields.map((field) => [field.fieldId, field]));
|
|
27067
27326
|
const selectionByField = /* @__PURE__ */ new Map();
|
|
27327
|
+
const requiredFields = collectReferencedA2UICardBusinessFieldIds(draft.card, draft.manifest.fields.filter((field) => field.required));
|
|
27068
27328
|
for (const selection of input.bindings) {
|
|
27069
27329
|
const field = fieldById.get(selection.fieldId);
|
|
27070
27330
|
if (!field || field.source !== "business") {
|
|
@@ -27078,9 +27338,9 @@ function compileA2UICardResource(input) {
|
|
|
27078
27338
|
}
|
|
27079
27339
|
selectionByField.set(selection.fieldId, cloneSelection(selection));
|
|
27080
27340
|
}
|
|
27081
|
-
for (const
|
|
27082
|
-
if (
|
|
27083
|
-
throw new A2UICardCompileError("CARD_BINDING_INCOMPLETE", `
|
|
27341
|
+
for (const fieldId of requiredFields) {
|
|
27342
|
+
if (!selectionByField.has(fieldId)) {
|
|
27343
|
+
throw new A2UICardCompileError("CARD_BINDING_INCOMPLETE", `Required referenced business field ${fieldId} has not been bound`);
|
|
27084
27344
|
}
|
|
27085
27345
|
}
|
|
27086
27346
|
const resolver = compileResolver([...selectionByField.values()]);
|
|
@@ -27172,6 +27432,7 @@ function compileV3Resource(draft, revision, bindings, tools) {
|
|
|
27172
27432
|
const sourceById = /* @__PURE__ */ new Map();
|
|
27173
27433
|
const outputBindings = [];
|
|
27174
27434
|
const boundFields = /* @__PURE__ */ new Set();
|
|
27435
|
+
const requiredFields = collectReferencedA2UICardBusinessFieldIds(draft.card, draft.manifest.fields.filter((field) => field.required));
|
|
27175
27436
|
for (const binding of bindings) {
|
|
27176
27437
|
const field = fieldById.get(binding.fieldId);
|
|
27177
27438
|
if (!field || field.source !== "business" || boundFields.has(binding.fieldId)) {
|
|
@@ -27199,9 +27460,9 @@ function compileV3Resource(draft, revision, bindings, tools) {
|
|
|
27199
27460
|
transform: binding.transform
|
|
27200
27461
|
});
|
|
27201
27462
|
}
|
|
27202
|
-
for (const
|
|
27203
|
-
if (
|
|
27204
|
-
throw new A2UICardCompileError("CARD_BINDING_INCOMPLETE", `
|
|
27463
|
+
for (const fieldId of requiredFields) {
|
|
27464
|
+
if (!boundFields.has(fieldId)) {
|
|
27465
|
+
throw new A2UICardCompileError("CARD_BINDING_INCOMPLETE", `Required referenced business field ${fieldId} has not been bound`);
|
|
27205
27466
|
}
|
|
27206
27467
|
}
|
|
27207
27468
|
const resource = {
|
|
@@ -27214,6 +27475,9 @@ function compileV3Resource(draft, revision, bindings, tools) {
|
|
|
27214
27475
|
fields: draft.manifest.fields,
|
|
27215
27476
|
dataSources: [...sourceById.values()].sort((left, right) => left.sourceId.localeCompare(right.sourceId)),
|
|
27216
27477
|
bindings: outputBindings.sort((left, right) => left.fieldId.localeCompare(right.fieldId)),
|
|
27478
|
+
...draft.manifest.actionBindings?.length ? {
|
|
27479
|
+
actionBindings: JSON.parse(JSON.stringify(draft.manifest.actionBindings))
|
|
27480
|
+
} : {},
|
|
27217
27481
|
a2uiTemplate: {
|
|
27218
27482
|
argsSchema: createA2UICardViewModelSchema(draft.manifest.fields),
|
|
27219
27483
|
content: stableJsonStringify(draft.card)
|
|
@@ -27404,9 +27668,6 @@ function createA2UICardDraftStructureChecksum(draft) {
|
|
|
27404
27668
|
delete cloned.manifest.bindings;
|
|
27405
27669
|
return sha256Hex(stableJsonStringify(cloned));
|
|
27406
27670
|
}
|
|
27407
|
-
function createA2UICardMcpConfigChecksum(resources) {
|
|
27408
|
-
return sha256Hex(stableJsonStringify(resources));
|
|
27409
|
-
}
|
|
27410
27671
|
function createA2UICardRevision(draftChecksum2, bindings) {
|
|
27411
27672
|
const digest = sha256Hex(`${draftChecksum2}
|
|
27412
27673
|
${stableJsonStringify(bindings)}`);
|
|
@@ -27485,6 +27746,410 @@ var SHA256_CONSTANTS = [
|
|
|
27485
27746
|
3329325298
|
|
27486
27747
|
];
|
|
27487
27748
|
|
|
27749
|
+
// ../agent-a2ui-card-contract/dist/esm/authoring-model.js
|
|
27750
|
+
var INTERNAL_KEYS = /* @__PURE__ */ new Set([
|
|
27751
|
+
"__proto__",
|
|
27752
|
+
"constructor",
|
|
27753
|
+
"prototype",
|
|
27754
|
+
"id",
|
|
27755
|
+
"component",
|
|
27756
|
+
"surfaceId",
|
|
27757
|
+
"version",
|
|
27758
|
+
"child",
|
|
27759
|
+
"children"
|
|
27760
|
+
]);
|
|
27761
|
+
var MERCHANT_CONTENT_KEYS = /* @__PURE__ */ new Set([
|
|
27762
|
+
"caption",
|
|
27763
|
+
"description",
|
|
27764
|
+
"label",
|
|
27765
|
+
"placeholder",
|
|
27766
|
+
"subtitle",
|
|
27767
|
+
"text",
|
|
27768
|
+
"title"
|
|
27769
|
+
]);
|
|
27770
|
+
var DEFAULT_AUTHORING_PROJECTION = {
|
|
27771
|
+
directTargets: "content"
|
|
27772
|
+
};
|
|
27773
|
+
var COMPONENT_AUTHORING_PROJECTIONS = {
|
|
27774
|
+
Image: {
|
|
27775
|
+
directTargets: "image-source",
|
|
27776
|
+
fieldProperties: ["src"],
|
|
27777
|
+
groupLabel: "\u56FE\u7247"
|
|
27778
|
+
}
|
|
27779
|
+
};
|
|
27780
|
+
function extractA2UICardAuthoringModel(bundle, options = {}) {
|
|
27781
|
+
const analysis = analyzeA2UIBindings(bundle.card);
|
|
27782
|
+
const bindingByField = new Map((bundle.manifest.bindings ?? []).map((binding) => [
|
|
27783
|
+
binding.fieldId,
|
|
27784
|
+
binding
|
|
27785
|
+
]));
|
|
27786
|
+
const fields = bundle.manifest.fields.filter((field) => field.source === "business");
|
|
27787
|
+
const registered = options.registeredComponents ? new Set(options.registeredComponents) : void 0;
|
|
27788
|
+
const occurrencesByComponent = groupBindingOccurrences(analysis.occurrences);
|
|
27789
|
+
const allFieldOccurrenceTargets = [];
|
|
27790
|
+
const groups = [];
|
|
27791
|
+
let order = 0;
|
|
27792
|
+
for (const { surfaceId, component } of orderedComponents(bundle)) {
|
|
27793
|
+
const occurrenceId = createA2UICardAuthoringOccurrenceId(surfaceId, component.id);
|
|
27794
|
+
const targets = [];
|
|
27795
|
+
if (!registered || registered.has(component.component)) {
|
|
27796
|
+
const projection = componentAuthoringProjection(component);
|
|
27797
|
+
const componentOccurrences = occurrencesByComponent.get(componentKey(surfaceId, component.id)) ?? [];
|
|
27798
|
+
discoverComponentTargets(component, occurrenceId, projection, targets);
|
|
27799
|
+
allFieldOccurrenceTargets.push(...fieldTargets(component, occurrenceId, fields, componentOccurrences, false));
|
|
27800
|
+
targets.push(...fieldTargets(component, occurrenceId, fields, componentOccurrences, true));
|
|
27801
|
+
targets.push(...actionTargets(component, occurrenceId, bundle.manifest.schemaVersion === "3" ? bundle.manifest.actionBindings : void 0));
|
|
27802
|
+
}
|
|
27803
|
+
if (targets.length === 0) {
|
|
27804
|
+
targets.push({
|
|
27805
|
+
kind: "agent-only",
|
|
27806
|
+
targetId: createA2UICardAuthoringTargetId(occurrenceId, "agent-only"),
|
|
27807
|
+
occurrenceId,
|
|
27808
|
+
label: "\u901A\u8FC7 Coding Agent \u4FEE\u6539",
|
|
27809
|
+
reason: registered && !registered.has(component.component) ? "\u7EC4\u4EF6\u672A\u5728\u5F53\u524D Catalog \u6CE8\u518C" : "\u6CA1\u6709\u53EF\u5B89\u5168\u76F4\u63A5\u7F16\u8F91\u7684\u4E1A\u52A1\u5B57\u6BB5"
|
|
27810
|
+
});
|
|
27811
|
+
}
|
|
27812
|
+
groups.push({
|
|
27813
|
+
occurrenceId,
|
|
27814
|
+
surfaceId,
|
|
27815
|
+
componentId: component.id,
|
|
27816
|
+
componentName: component.component,
|
|
27817
|
+
order: order++,
|
|
27818
|
+
label: componentAuthoringProjection(component).groupLabel ?? componentLabel(component),
|
|
27819
|
+
targets: deduplicateTargets(targets),
|
|
27820
|
+
agentOnly: targets.every((target) => target.kind === "agent-only")
|
|
27821
|
+
});
|
|
27822
|
+
}
|
|
27823
|
+
const fieldStates = fields.map((field) => {
|
|
27824
|
+
const occurrences = allFieldOccurrenceTargets.filter((target) => target.fieldId === field.fieldId);
|
|
27825
|
+
const binding = bindingByField.get(field.fieldId);
|
|
27826
|
+
return {
|
|
27827
|
+
field,
|
|
27828
|
+
...binding ? { binding: cloneBinding(binding) } : {},
|
|
27829
|
+
occurrences,
|
|
27830
|
+
usageCount: new Set(occurrences.map((target) => `${target.occurrenceId}\0${target.propertyPath}`)).size,
|
|
27831
|
+
status: occurrences.length === 0 ? "unused" : binding ? "bound" : field.required ? "unbound-required" : "unbound-optional"
|
|
27832
|
+
};
|
|
27833
|
+
});
|
|
27834
|
+
const diagnostics = getA2UICardAuthoringDiagnostics(bundle, {
|
|
27835
|
+
groups,
|
|
27836
|
+
fields: fieldStates
|
|
27837
|
+
});
|
|
27838
|
+
return {
|
|
27839
|
+
draftId: bundle.manifest.draftId,
|
|
27840
|
+
resourceId: bundle.manifest.resourceId,
|
|
27841
|
+
groups,
|
|
27842
|
+
fields: fieldStates,
|
|
27843
|
+
diagnostics
|
|
27844
|
+
};
|
|
27845
|
+
}
|
|
27846
|
+
function componentAuthoringProjection(component) {
|
|
27847
|
+
return COMPONENT_AUTHORING_PROJECTIONS[component.component] ?? DEFAULT_AUTHORING_PROJECTION;
|
|
27848
|
+
}
|
|
27849
|
+
function discoverComponentTargets(component, occurrenceId, projection, targets) {
|
|
27850
|
+
if (projection.directTargets === "content") {
|
|
27851
|
+
discoverTargets(component, occurrenceId, "", targets);
|
|
27852
|
+
return;
|
|
27853
|
+
}
|
|
27854
|
+
if (typeof component.src !== "string")
|
|
27855
|
+
return;
|
|
27856
|
+
targets.push({
|
|
27857
|
+
kind: "image",
|
|
27858
|
+
targetId: createA2UICardAuthoringTargetId(occurrenceId, "src"),
|
|
27859
|
+
occurrenceId,
|
|
27860
|
+
propertyPath: "src",
|
|
27861
|
+
label: "\u56FE\u7247\u5730\u5740",
|
|
27862
|
+
value: component.src,
|
|
27863
|
+
role: "src"
|
|
27864
|
+
});
|
|
27865
|
+
}
|
|
27866
|
+
function getA2UICardAuthoringDiagnostics(bundle, partial) {
|
|
27867
|
+
const diagnostics = getA2UICardDraftBundleIssues(bundle).map((entry) => ({
|
|
27868
|
+
...entry,
|
|
27869
|
+
severity: "hard-error"
|
|
27870
|
+
}));
|
|
27871
|
+
const model = partial ?? extractA2UICardAuthoringModel(bundle);
|
|
27872
|
+
for (const field of model.fields) {
|
|
27873
|
+
if (field.status === "unbound-required") {
|
|
27874
|
+
diagnostics.push({
|
|
27875
|
+
code: "REQUIRED_BINDING_MISSING",
|
|
27876
|
+
path: `manifest.fields.${field.field.fieldId}`,
|
|
27877
|
+
message: `\u5FC5\u586B\u5B57\u6BB5\u201C${field.field.label}\u201D\u5C1A\u672A\u7ED1\u5B9A`,
|
|
27878
|
+
severity: "hard-error",
|
|
27879
|
+
fieldId: field.field.fieldId
|
|
27880
|
+
});
|
|
27881
|
+
}
|
|
27882
|
+
}
|
|
27883
|
+
for (const group of model.groups) {
|
|
27884
|
+
const component = findComponent(bundle, group.surfaceId, group.componentId);
|
|
27885
|
+
const action = isPlainRecord(component?.action) ? component.action : void 0;
|
|
27886
|
+
const client = action && isPlainRecord(action.client) ? action.client : void 0;
|
|
27887
|
+
if (client?.name === "ui.page.open") {
|
|
27888
|
+
const context = isPlainRecord(client.context) ? client.context : void 0;
|
|
27889
|
+
if (typeof context?.url === "string" && !isSafePageUrl(context.url)) {
|
|
27890
|
+
diagnostics.push({
|
|
27891
|
+
code: "UNSAFE_PAGE_URL",
|
|
27892
|
+
path: `${group.occurrenceId}.action.client.context.url`,
|
|
27893
|
+
message: "\u9875\u9762\u5730\u5740\u5FC5\u987B\u662F\u5B89\u5168\u7684 http(s) \u5730\u5740\u6216\u7AD9\u5185\u7EDD\u5BF9\u8DEF\u5F84",
|
|
27894
|
+
severity: "hard-error",
|
|
27895
|
+
occurrenceId: group.occurrenceId
|
|
27896
|
+
});
|
|
27897
|
+
}
|
|
27898
|
+
} else if (client && typeof client.name === "string" && !["ui.overlay.close", "ui.surface.open"].includes(client.name)) {
|
|
27899
|
+
diagnostics.push({
|
|
27900
|
+
code: "UNREGISTERED_CLIENT_ACTION",
|
|
27901
|
+
path: `${group.occurrenceId}.action.client.name`,
|
|
27902
|
+
message: `\u672A\u6CE8\u518C\u7684 Client Action\uFF1A${client.name}`,
|
|
27903
|
+
severity: "hard-error",
|
|
27904
|
+
occurrenceId: group.occurrenceId
|
|
27905
|
+
});
|
|
27906
|
+
}
|
|
27907
|
+
}
|
|
27908
|
+
return deduplicateDiagnostics(diagnostics);
|
|
27909
|
+
}
|
|
27910
|
+
function validateA2UICardForPublish(bundle) {
|
|
27911
|
+
return getA2UICardAuthoringDiagnostics(bundle).filter((diagnostic) => diagnostic.severity === "hard-error");
|
|
27912
|
+
}
|
|
27913
|
+
function createA2UICardAuthoringOccurrenceId(surfaceId, componentId) {
|
|
27914
|
+
return `${encodeURIComponent(surfaceId)}:${encodeURIComponent(componentId)}`;
|
|
27915
|
+
}
|
|
27916
|
+
function createA2UICardAuthoringTargetId(occurrenceId, propertyPath) {
|
|
27917
|
+
return `${occurrenceId}:${encodeURIComponent(propertyPath)}`;
|
|
27918
|
+
}
|
|
27919
|
+
function isSafePageUrl(url) {
|
|
27920
|
+
const trimmed = url.trim();
|
|
27921
|
+
if (trimmed.length === 0 || trimmed.length > 2e3)
|
|
27922
|
+
return false;
|
|
27923
|
+
const token = /\{\{([^{}]+)\}\}/gu;
|
|
27924
|
+
let tokenCount = 0;
|
|
27925
|
+
for (const match of trimmed.matchAll(token)) {
|
|
27926
|
+
tokenCount++;
|
|
27927
|
+
if (tokenCount > 32)
|
|
27928
|
+
return false;
|
|
27929
|
+
const path22 = match[1]?.trim() ?? "";
|
|
27930
|
+
if (!(path22 === "/" || path22.startsWith("/") || path22.startsWith("./")) || path22.split("/").some((part) => ["..", "__proto__", "constructor", "prototype"].includes(part))) {
|
|
27931
|
+
return false;
|
|
27932
|
+
}
|
|
27933
|
+
}
|
|
27934
|
+
const candidate = trimmed.replace(token, "value");
|
|
27935
|
+
if (candidate.includes("{{") || candidate.includes("}}"))
|
|
27936
|
+
return false;
|
|
27937
|
+
try {
|
|
27938
|
+
return ["https:", "alipays:"].includes(new URL(candidate).protocol);
|
|
27939
|
+
} catch {
|
|
27940
|
+
return false;
|
|
27941
|
+
}
|
|
27942
|
+
}
|
|
27943
|
+
function orderedComponents(bundle) {
|
|
27944
|
+
const result = [];
|
|
27945
|
+
for (const message of bundle.card) {
|
|
27946
|
+
for (const instruction of message.datas) {
|
|
27947
|
+
if (!("updateComponents" in instruction))
|
|
27948
|
+
continue;
|
|
27949
|
+
for (const component of instruction.updateComponents.components) {
|
|
27950
|
+
result.push({
|
|
27951
|
+
surfaceId: instruction.updateComponents.surfaceId,
|
|
27952
|
+
component
|
|
27953
|
+
});
|
|
27954
|
+
}
|
|
27955
|
+
}
|
|
27956
|
+
}
|
|
27957
|
+
return result;
|
|
27958
|
+
}
|
|
27959
|
+
function discoverTargets(value, occurrenceId, propertyPath, targets) {
|
|
27960
|
+
if (Array.isArray(value)) {
|
|
27961
|
+
value.forEach((entry, index) => discoverTargets(entry, occurrenceId, appendPath(propertyPath, String(index)), targets));
|
|
27962
|
+
return;
|
|
27963
|
+
}
|
|
27964
|
+
if (!isPlainRecord(value))
|
|
27965
|
+
return;
|
|
27966
|
+
if (typeof value.path === "string")
|
|
27967
|
+
return;
|
|
27968
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
27969
|
+
const nextPath = appendPath(propertyPath, key);
|
|
27970
|
+
if (INTERNAL_KEYS.has(key) || key === "action")
|
|
27971
|
+
continue;
|
|
27972
|
+
if (Array.isArray(entry) || isPlainRecord(entry)) {
|
|
27973
|
+
discoverTargets(entry, occurrenceId, nextPath, targets);
|
|
27974
|
+
continue;
|
|
27975
|
+
}
|
|
27976
|
+
if (!isJsonValue(entry))
|
|
27977
|
+
continue;
|
|
27978
|
+
if (typeof entry !== "string" || !MERCHANT_CONTENT_KEYS.has(key))
|
|
27979
|
+
continue;
|
|
27980
|
+
targets.push({
|
|
27981
|
+
kind: "literal",
|
|
27982
|
+
targetId: createA2UICardAuthoringTargetId(occurrenceId, nextPath),
|
|
27983
|
+
occurrenceId,
|
|
27984
|
+
propertyPath: nextPath,
|
|
27985
|
+
label: propertyLabel(nextPath),
|
|
27986
|
+
value: entry
|
|
27987
|
+
});
|
|
27988
|
+
}
|
|
27989
|
+
}
|
|
27990
|
+
function actionTargets(component, occurrenceId, actionBindings) {
|
|
27991
|
+
if (!isPlainRecord(component.action))
|
|
27992
|
+
return [];
|
|
27993
|
+
const action = component.action;
|
|
27994
|
+
const event = isPlainRecord(action.event) ? action.event : void 0;
|
|
27995
|
+
if (event) {
|
|
27996
|
+
const message = event["x2-user-message"];
|
|
27997
|
+
if (typeof message === "string" && typeof event.name === "string") {
|
|
27998
|
+
return [
|
|
27999
|
+
{
|
|
28000
|
+
kind: "agent-event",
|
|
28001
|
+
targetId: createA2UICardAuthoringTargetId(occurrenceId, "action"),
|
|
28002
|
+
occurrenceId,
|
|
28003
|
+
propertyPath: "action",
|
|
28004
|
+
label: "\u70B9\u51FB\u540E",
|
|
28005
|
+
message,
|
|
28006
|
+
eventName: event.name,
|
|
28007
|
+
hasContext: isPlainRecord(event.context)
|
|
28008
|
+
}
|
|
28009
|
+
];
|
|
28010
|
+
}
|
|
28011
|
+
return [agentOnlyAction(occurrenceId, "\u52A8\u6001\u6216\u590D\u6742\u4E8B\u4EF6\u901A\u8FC7\u667A\u80FD\u4F53\u4FEE\u6539")];
|
|
28012
|
+
}
|
|
28013
|
+
const client = isPlainRecord(action.client) ? action.client : void 0;
|
|
28014
|
+
if (client?.name === "ui.page.open") {
|
|
28015
|
+
const context = isPlainRecord(client.context) ? client.context : void 0;
|
|
28016
|
+
const url = context?.url;
|
|
28017
|
+
return [
|
|
28018
|
+
{
|
|
28019
|
+
kind: "page-open",
|
|
28020
|
+
targetId: createA2UICardAuthoringTargetId(occurrenceId, "action"),
|
|
28021
|
+
occurrenceId,
|
|
28022
|
+
propertyPath: "action",
|
|
28023
|
+
label: "\u6253\u5F00\u9875\u9762",
|
|
28024
|
+
...typeof url === "string" ? { url } : {},
|
|
28025
|
+
dynamic: isPlainRecord(url) && typeof url.path === "string"
|
|
28026
|
+
}
|
|
28027
|
+
];
|
|
28028
|
+
}
|
|
28029
|
+
if (client?.name === "payment.trade-pay") {
|
|
28030
|
+
const context = isPlainRecord(client.context) ? client.context : void 0;
|
|
28031
|
+
const actionId = typeof context?.actionId === "string" ? context.actionId : "";
|
|
28032
|
+
const actionContext = isPlainRecord(context?.actionContext) ? context.actionContext : {};
|
|
28033
|
+
const binding = actionBindings?.find((candidate) => candidate.kind === "payment" && candidate.actionId === actionId);
|
|
28034
|
+
return [{
|
|
28035
|
+
kind: "payment",
|
|
28036
|
+
targetId: createA2UICardAuthoringTargetId(occurrenceId, "action"),
|
|
28037
|
+
occurrenceId,
|
|
28038
|
+
propertyPath: "action",
|
|
28039
|
+
label: "\u652F\u4ED8",
|
|
28040
|
+
actionId,
|
|
28041
|
+
actionContext,
|
|
28042
|
+
...binding ? { binding } : {},
|
|
28043
|
+
configured: Boolean(actionId && binding)
|
|
28044
|
+
}];
|
|
28045
|
+
}
|
|
28046
|
+
return [agentOnlyAction(occurrenceId, "\u8BE5\u884C\u4E3A\u53EA\u80FD\u901A\u8FC7 Coding Agent \u4FEE\u6539")];
|
|
28047
|
+
}
|
|
28048
|
+
function fieldTargets(component, occurrenceId, fields, occurrences, projectedOnly) {
|
|
28049
|
+
const projection = componentAuthoringProjection(component);
|
|
28050
|
+
const ordered = [...fields].sort((left, right) => right.path.length - left.path.length);
|
|
28051
|
+
return occurrences.flatMap((occurrence) => {
|
|
28052
|
+
if (projectedOnly && !isProjectedFieldOccurrence(occurrence.propertyPath, projection)) {
|
|
28053
|
+
return [];
|
|
28054
|
+
}
|
|
28055
|
+
const field = ordered.find((candidate) => occurrence.canonicalPath === candidate.path || occurrence.canonicalPath.startsWith(`${candidate.path}/*/`) || occurrence.canonicalPath.startsWith(`${candidate.path}/`));
|
|
28056
|
+
if (!field)
|
|
28057
|
+
return [];
|
|
28058
|
+
const suffix = occurrence.canonicalPath.slice(field.path.length).replace(/^\/\*/u, "");
|
|
28059
|
+
const targetPath = suffix ? suffix.startsWith("/") ? suffix : `/${suffix}` : void 0;
|
|
28060
|
+
return [
|
|
28061
|
+
{
|
|
28062
|
+
kind: "field-occurrence",
|
|
28063
|
+
targetId: createA2UICardAuthoringTargetId(occurrenceId, occurrence.propertyPath),
|
|
28064
|
+
occurrenceId,
|
|
28065
|
+
propertyPath: occurrence.propertyPath,
|
|
28066
|
+
label: fieldOccurrenceLabel(targetPath, occurrence.propertyPath),
|
|
28067
|
+
fieldId: field.fieldId,
|
|
28068
|
+
...targetPath ? { targetPath } : {}
|
|
28069
|
+
}
|
|
28070
|
+
];
|
|
28071
|
+
});
|
|
28072
|
+
}
|
|
28073
|
+
function isProjectedFieldOccurrence(propertyPath, projection) {
|
|
28074
|
+
if (propertyPath === "action" || propertyPath.startsWith("action.")) {
|
|
28075
|
+
return false;
|
|
28076
|
+
}
|
|
28077
|
+
return projection.fieldProperties === void 0 || projection.fieldProperties.includes(propertyPath);
|
|
28078
|
+
}
|
|
28079
|
+
function fieldOccurrenceLabel(targetPath, propertyPath) {
|
|
28080
|
+
if (!targetPath)
|
|
28081
|
+
return propertyLabel(propertyPath);
|
|
28082
|
+
const segment = targetPath.split("/").filter(Boolean).at(-1);
|
|
28083
|
+
return segment ? decodeJsonPointerSegment(segment) : propertyLabel(propertyPath);
|
|
28084
|
+
}
|
|
28085
|
+
function decodeJsonPointerSegment(segment) {
|
|
28086
|
+
return segment.replace(/~1/gu, "/").replace(/~0/gu, "~");
|
|
28087
|
+
}
|
|
28088
|
+
function groupBindingOccurrences(occurrences) {
|
|
28089
|
+
const result = /* @__PURE__ */ new Map();
|
|
28090
|
+
for (const occurrence of occurrences) {
|
|
28091
|
+
if (!occurrence.surfaceId)
|
|
28092
|
+
continue;
|
|
28093
|
+
const key = componentKey(occurrence.surfaceId, occurrence.componentId);
|
|
28094
|
+
const entries = result.get(key) ?? [];
|
|
28095
|
+
entries.push(occurrence);
|
|
28096
|
+
result.set(key, entries);
|
|
28097
|
+
}
|
|
28098
|
+
return result;
|
|
28099
|
+
}
|
|
28100
|
+
function findComponent(bundle, surfaceId, componentId) {
|
|
28101
|
+
return orderedComponents(bundle).find((entry) => entry.surfaceId === surfaceId && entry.component.id === componentId)?.component;
|
|
28102
|
+
}
|
|
28103
|
+
function agentOnlyAction(occurrenceId, reason) {
|
|
28104
|
+
return {
|
|
28105
|
+
kind: "agent-only",
|
|
28106
|
+
targetId: createA2UICardAuthoringTargetId(occurrenceId, "action"),
|
|
28107
|
+
occurrenceId,
|
|
28108
|
+
propertyPath: "action",
|
|
28109
|
+
label: "\u901A\u8FC7 Coding Agent \u4FEE\u6539\u884C\u4E3A",
|
|
28110
|
+
reason
|
|
28111
|
+
};
|
|
28112
|
+
}
|
|
28113
|
+
function componentLabel(component) {
|
|
28114
|
+
const candidate = component.label ?? component.title ?? component.text;
|
|
28115
|
+
return typeof candidate === "string" && candidate.trim() ? candidate : component.component;
|
|
28116
|
+
}
|
|
28117
|
+
function propertyLabel(propertyPath) {
|
|
28118
|
+
const key = propertyPath.split(".").at(-1) ?? propertyPath;
|
|
28119
|
+
const labels = {
|
|
28120
|
+
text: "\u6587\u6848",
|
|
28121
|
+
title: "\u6807\u9898",
|
|
28122
|
+
subtitle: "\u526F\u6807\u9898",
|
|
28123
|
+
label: "\u6807\u7B7E",
|
|
28124
|
+
description: "\u8BF4\u660E",
|
|
28125
|
+
value: "\u5185\u5BB9",
|
|
28126
|
+
alt: "\u56FE\u7247\u8BF4\u660E"
|
|
28127
|
+
};
|
|
28128
|
+
return labels[key] ?? key;
|
|
28129
|
+
}
|
|
28130
|
+
function appendPath(base, segment) {
|
|
28131
|
+
return base ? `${base}.${segment}` : segment;
|
|
28132
|
+
}
|
|
28133
|
+
function componentKey(surfaceId, componentId) {
|
|
28134
|
+
return `${surfaceId}\0${componentId}`;
|
|
28135
|
+
}
|
|
28136
|
+
function deduplicateTargets(targets) {
|
|
28137
|
+
return [
|
|
28138
|
+
...new Map(targets.map((target) => [target.targetId, target])).values()
|
|
28139
|
+
];
|
|
28140
|
+
}
|
|
28141
|
+
function deduplicateDiagnostics(diagnostics) {
|
|
28142
|
+
return [
|
|
28143
|
+
...new Map(diagnostics.map((entry) => [
|
|
28144
|
+
`${entry.code}\0${entry.path}\0${entry.fieldId ?? ""}`,
|
|
28145
|
+
entry
|
|
28146
|
+
])).values()
|
|
28147
|
+
];
|
|
28148
|
+
}
|
|
28149
|
+
function cloneBinding(binding) {
|
|
28150
|
+
return JSON.parse(JSON.stringify(binding));
|
|
28151
|
+
}
|
|
28152
|
+
|
|
27488
28153
|
// src/commands/card/index.ts
|
|
27489
28154
|
var DRAFT_ROOT = ".card-drafts";
|
|
27490
28155
|
var AGENT_CONFIG_PATH = "agent_config.json";
|
|
@@ -27614,36 +28279,20 @@ async function validateCardBindingsFromDisk(cwd, draftId, fieldId) {
|
|
|
27614
28279
|
const directory = draftDirectory(cwd, draftId);
|
|
27615
28280
|
const [bundle, contextValue, configValue] = await Promise.all([
|
|
27616
28281
|
readBundle(directory, draftId),
|
|
27617
|
-
|
|
28282
|
+
readOptionalJson(import_path22.default.join(directory, "binding-context.json")),
|
|
27618
28283
|
readRequiredJson(import_path22.default.join(cwd, AGENT_CONFIG_PATH))
|
|
27619
28284
|
]);
|
|
27620
|
-
const
|
|
27621
|
-
if (!contextResult.success) {
|
|
27622
|
-
throw new LinkqueCliError(
|
|
27623
|
-
"CARD_BINDING_CONTEXT_INVALID",
|
|
27624
|
-
contextResult.issues.map((entry) => entry.message).join("; ")
|
|
27625
|
-
);
|
|
27626
|
-
}
|
|
27627
|
-
const context = contextResult.data;
|
|
28285
|
+
const context = parseOptionalBindingContext(contextValue);
|
|
27628
28286
|
const draftStructureChecksum = createA2UICardDraftStructureChecksum(bundle);
|
|
27629
28287
|
const config2 = parseAgentConfig(configValue);
|
|
27630
28288
|
const mcpResources = normalizedMcpResources(config2);
|
|
27631
|
-
|
|
27632
|
-
|
|
27633
|
-
"CARD_BINDING_CONTEXT_STALE",
|
|
27634
|
-
"MCP \u914D\u7F6E\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77 AI \u8F85\u52A9\u7ED1\u5B9A"
|
|
27635
|
-
);
|
|
27636
|
-
}
|
|
27637
|
-
const issues = validateA2UICardBindings(
|
|
27638
|
-
{
|
|
27639
|
-
draft: bundle,
|
|
27640
|
-
context,
|
|
27641
|
-
mountedMcpResourceIds: new Set(
|
|
27642
|
-
mcpResources.map((resource) => String(resource.resourceId))
|
|
27643
|
-
)
|
|
27644
|
-
},
|
|
27645
|
-
fieldId ? { fieldIds: /* @__PURE__ */ new Set([fieldId]) } : void 0
|
|
28289
|
+
const mountedMcpResourceIds = new Set(
|
|
28290
|
+
mcpResources.map((resource) => String(resource.resourceId))
|
|
27646
28291
|
);
|
|
28292
|
+
const issues = context ? validateA2UICardBindings(
|
|
28293
|
+
{ draft: bundle, context, mountedMcpResourceIds },
|
|
28294
|
+
fieldId ? { fieldIds: /* @__PURE__ */ new Set([fieldId]) } : void 0
|
|
28295
|
+
) : validateBindingsWithoutContext(bundle, mountedMcpResourceIds, fieldId);
|
|
27647
28296
|
if (issues.length > 0) {
|
|
27648
28297
|
throw new LinkqueCliError(
|
|
27649
28298
|
"CARD_BINDING_VALIDATION_FAILED",
|
|
@@ -27674,15 +28323,9 @@ async function finalizeCardWhileLocked(cwd, draftId, expectedDraftChecksum) {
|
|
|
27674
28323
|
}
|
|
27675
28324
|
const directory = draftDirectory(cwd, draftId);
|
|
27676
28325
|
const bundle = await readBundle(directory, draftId);
|
|
27677
|
-
const
|
|
27678
|
-
await
|
|
28326
|
+
const context = parseOptionalBindingContext(
|
|
28327
|
+
await readOptionalJson(import_path22.default.join(directory, "binding-context.json"))
|
|
27679
28328
|
);
|
|
27680
|
-
if (!contextResult.success) {
|
|
27681
|
-
throw new LinkqueCliError(
|
|
27682
|
-
"CARD_BINDING_CONTEXT_INVALID",
|
|
27683
|
-
contextResult.issues.map((entry) => entry.message).join("; ")
|
|
27684
|
-
);
|
|
27685
|
-
}
|
|
27686
28329
|
const configPath = import_path22.default.join(cwd, AGENT_CONFIG_PATH);
|
|
27687
28330
|
const oldConfigBytes = await (0, import_promises16.readFile)(configPath);
|
|
27688
28331
|
const config2 = parseAgentConfig(
|
|
@@ -27716,7 +28359,7 @@ async function finalizeCardWhileLocked(cwd, draftId, expectedDraftChecksum) {
|
|
|
27716
28359
|
// V3,让 Agent 直接按真实 MCP inputSchema 决定参数;不再把 presentation input
|
|
27717
28360
|
// 或 inputBindings 固化进 CARD。
|
|
27718
28361
|
...bindings.length > 0 ? { targetSchemaVersion: "3" } : {},
|
|
27719
|
-
tools:
|
|
28362
|
+
tools: (context?.candidates ?? []).flatMap(
|
|
27720
28363
|
(candidate) => candidate.status === "ready" ? [candidate.tool] : []
|
|
27721
28364
|
)
|
|
27722
28365
|
});
|
|
@@ -28043,6 +28686,47 @@ function normalizedMcpResources(config2) {
|
|
|
28043
28686
|
(left, right) => stableJsonStringify(left).localeCompare(stableJsonStringify(right))
|
|
28044
28687
|
);
|
|
28045
28688
|
}
|
|
28689
|
+
function parseOptionalBindingContext(value) {
|
|
28690
|
+
if (value === void 0) return void 0;
|
|
28691
|
+
const result = parseA2UICardBindingContext(value);
|
|
28692
|
+
if (!result.success) {
|
|
28693
|
+
throw new LinkqueCliError(
|
|
28694
|
+
"CARD_BINDING_CONTEXT_INVALID",
|
|
28695
|
+
result.issues.map((entry) => entry.message).join("; ")
|
|
28696
|
+
);
|
|
28697
|
+
}
|
|
28698
|
+
return result.data;
|
|
28699
|
+
}
|
|
28700
|
+
function validateBindingsWithoutContext(bundle, mountedMcpResourceIds, fieldId) {
|
|
28701
|
+
const selectedFields = fieldId ? /* @__PURE__ */ new Set([fieldId]) : void 0;
|
|
28702
|
+
const bindings = (bundle.manifest.bindings ?? []).filter(
|
|
28703
|
+
(binding) => !selectedFields || selectedFields.has(binding.fieldId)
|
|
28704
|
+
);
|
|
28705
|
+
const issues = getA2UICardBindingSelectionIssues(
|
|
28706
|
+
bindings,
|
|
28707
|
+
"draft.manifest.bindings",
|
|
28708
|
+
bundle.manifest.fields
|
|
28709
|
+
);
|
|
28710
|
+
for (const binding of bindings) {
|
|
28711
|
+
if (!mountedMcpResourceIds.has(binding.mcpResourceId)) {
|
|
28712
|
+
issues.push({
|
|
28713
|
+
code: "MCP_TOOL_UNAVAILABLE",
|
|
28714
|
+
message: `MCP resource ${binding.mcpResourceId} is not mounted`,
|
|
28715
|
+
path: `draft.manifest.bindings.${binding.fieldId}`
|
|
28716
|
+
});
|
|
28717
|
+
}
|
|
28718
|
+
}
|
|
28719
|
+
if (!selectedFields) {
|
|
28720
|
+
issues.push(
|
|
28721
|
+
...validateA2UICardForPublish(bundle).map((diagnostic) => ({
|
|
28722
|
+
code: diagnostic.code,
|
|
28723
|
+
message: diagnostic.message,
|
|
28724
|
+
path: diagnostic.path
|
|
28725
|
+
}))
|
|
28726
|
+
);
|
|
28727
|
+
}
|
|
28728
|
+
return issues;
|
|
28729
|
+
}
|
|
28046
28730
|
function isJsonObject(value) {
|
|
28047
28731
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
28048
28732
|
}
|