@harrylabsj/kiwi 0.7.4 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/a2a/client/client.d.ts +7 -2
- package/dist/a2a/client/client.js +34 -5
- package/dist/a2a/client/client.js.map +1 -1
- package/dist/a2a/client/index.d.ts +1 -1
- package/dist/a2a/client/index.js.map +1 -1
- package/dist/a2a/client/jsonrpc.js +2 -1
- package/dist/a2a/client/jsonrpc.js.map +1 -1
- package/dist/a2a/client/parse.js +6 -2
- package/dist/a2a/client/parse.js.map +1 -1
- package/dist/a2a/client/types.d.ts +11 -0
- package/dist/a2a/client/url-policy.js +29 -0
- package/dist/a2a/client/url-policy.js.map +1 -1
- package/dist/a2a/node.d.ts +10 -0
- package/dist/a2a/node.js +91 -14
- package/dist/a2a/node.js.map +1 -1
- package/dist/a2a/server/card.d.ts +6 -0
- package/dist/a2a/server/card.js +55 -1
- package/dist/a2a/server/card.js.map +1 -1
- package/dist/a2a/server/errors.d.ts +12 -2
- package/dist/a2a/server/errors.js +13 -2
- package/dist/a2a/server/errors.js.map +1 -1
- package/dist/a2a/server/generic-responder.d.ts +48 -0
- package/dist/a2a/server/generic-responder.js +57 -0
- package/dist/a2a/server/generic-responder.js.map +1 -0
- package/dist/a2a/server/index.d.ts +2 -0
- package/dist/a2a/server/index.js +2 -0
- package/dist/a2a/server/index.js.map +1 -1
- package/dist/a2a/server/merchant-handler.d.ts +12 -1
- package/dist/a2a/server/merchant-handler.js +96 -22
- package/dist/a2a/server/merchant-handler.js.map +1 -1
- package/dist/a2a/server/pipeline.d.ts +28 -0
- package/dist/a2a/server/pipeline.js +66 -3
- package/dist/a2a/server/pipeline.js.map +1 -1
- package/dist/a2a/server/server.d.ts +12 -0
- package/dist/a2a/server/server.js +255 -11
- package/dist/a2a/server/server.js.map +1 -1
- package/dist/a2a/server/task-registry.d.ts +8 -0
- package/dist/a2a/server/task-registry.js +17 -0
- package/dist/a2a/server/task-registry.js.map +1 -1
- package/dist/a2a/server/throttle.d.ts +13 -0
- package/dist/a2a/server/throttle.js +41 -1
- package/dist/a2a/server/throttle.js.map +1 -1
- package/dist/a2a/server/types.d.ts +14 -1
- package/dist/a2a/signing-key.d.ts +55 -0
- package/dist/a2a/signing-key.js +142 -0
- package/dist/a2a/signing-key.js.map +1 -0
- package/dist/a2a/v1/headers.d.ts +33 -0
- package/dist/a2a/v1/headers.js +56 -0
- package/dist/a2a/v1/headers.js.map +1 -0
- package/dist/a2a/v1/methods.d.ts +52 -0
- package/dist/a2a/v1/methods.js +75 -0
- package/dist/a2a/v1/methods.js.map +1 -0
- package/dist/a2a/v1/part.d.ts +48 -0
- package/dist/a2a/v1/part.js +63 -0
- package/dist/a2a/v1/part.js.map +1 -0
- package/dist/a2a/v1/types.d.ts +94 -0
- package/dist/a2a/v1/types.js +62 -0
- package/dist/a2a/v1/types.js.map +1 -0
- package/dist/agent/buyer/buyer-tools.js +92 -19
- package/dist/agent/buyer/buyer-tools.js.map +1 -1
- package/dist/agent/buyer/search-loop.js +17 -2
- package/dist/agent/buyer/search-loop.js.map +1 -1
- package/dist/agent/buyer/types.js +4 -1
- package/dist/agent/buyer/types.js.map +1 -1
- package/dist/agent/kernel.js +70 -25
- package/dist/agent/kernel.js.map +1 -1
- package/dist/agent/merchant/action-candidate.d.ts +10 -0
- package/dist/agent/merchant/action-candidate.js +36 -4
- package/dist/agent/merchant/action-candidate.js.map +1 -1
- package/dist/agent/merchant/merchant-client.d.ts +4 -1
- package/dist/agent/merchant/merchant-client.js +41 -29
- package/dist/agent/merchant/merchant-client.js.map +1 -1
- package/dist/agent/negotiation-chat.d.ts +11 -0
- package/dist/agent/negotiation-chat.js +15 -0
- package/dist/agent/negotiation-chat.js.map +1 -1
- package/dist/agent/system-prompt.js +1 -1
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/agent/write-gate.d.ts +4 -0
- package/dist/agent/write-gate.js +9 -3
- package/dist/agent/write-gate.js.map +1 -1
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +51 -7
- package/dist/cli.js.map +1 -1
- package/dist/commerce/data-source.d.ts +11 -1
- package/dist/commerce/data-source.js.map +1 -1
- package/dist/commerce/shopping-cli-source.js +10 -0
- package/dist/commerce/shopping-cli-source.js.map +1 -1
- package/dist/config/profile.d.ts +2 -0
- package/dist/config/profile.js +21 -7
- package/dist/config/profile.js.map +1 -1
- package/dist/counterparty/a2a-direct/index.d.ts +4 -0
- package/dist/counterparty/a2a-direct/index.js +5 -0
- package/dist/counterparty/a2a-direct/index.js.map +1 -1
- package/dist/demo/demo-runner.d.ts +81 -0
- package/dist/demo/demo-runner.js +344 -0
- package/dist/demo/demo-runner.js.map +1 -0
- package/dist/discovery/agent-card/types.d.ts +3 -0
- package/dist/discovery/agent-card/validate.js +7 -0
- package/dist/discovery/agent-card/validate.js.map +1 -1
- package/dist/discovery/catalog-source/register.d.ts +5 -1
- package/dist/discovery/catalog-source/register.js +14 -3
- package/dist/discovery/catalog-source/register.js.map +1 -1
- package/dist/discovery/resolve.js +3 -0
- package/dist/discovery/resolve.js.map +1 -1
- package/dist/discovery/ucp/resolver.d.ts +2 -0
- package/dist/discovery/ucp/resolver.js +3 -1
- package/dist/discovery/ucp/resolver.js.map +1 -1
- package/dist/handoff/ledger.js +23 -5
- package/dist/handoff/ledger.js.map +1 -1
- package/dist/handoff/order-record.js +26 -4
- package/dist/handoff/order-record.js.map +1 -1
- package/dist/handoff/transaction.js +13 -3
- package/dist/handoff/transaction.js.map +1 -1
- package/dist/negotiation/lease/store.js +14 -4
- package/dist/negotiation/lease/store.js.map +1 -1
- package/dist/negotiation/state/phase.js +12 -0
- package/dist/negotiation/state/phase.js.map +1 -1
- package/dist/net/safe-http.js +9 -1
- package/dist/net/safe-http.js.map +1 -1
- package/dist/operator/controller.d.ts +3 -0
- package/dist/operator/controller.js +18 -5
- package/dist/operator/controller.js.map +1 -1
- package/dist/product-buyer.d.ts +5 -0
- package/dist/product-buyer.js +1 -1
- package/dist/product-buyer.js.map +1 -1
- package/dist/product-cli.d.ts +10 -3
- package/dist/product-cli.js +13 -5
- package/dist/product-cli.js.map +1 -1
- package/dist/product-init.d.ts +6 -0
- package/dist/product-init.js +1 -1
- package/dist/product-init.js.map +1 -1
- package/dist/supervisor/manage.d.ts +5 -1
- package/dist/supervisor/manage.js +100 -9
- package/dist/supervisor/manage.js.map +1 -1
- package/dist/trust/identity/auth-verifier.d.ts +8 -0
- package/dist/trust/identity/auth-verifier.js +14 -0
- package/dist/trust/identity/auth-verifier.js.map +1 -1
- package/dist/weixin/credentials.js +15 -4
- package/dist/weixin/credentials.js.map +1 -1
- package/dist/weixin/ilink-client.d.ts +9 -0
- package/dist/weixin/ilink-client.js +46 -0
- package/dist/weixin/ilink-client.js.map +1 -1
- package/dist/weixin/login.js +5 -2
- package/dist/weixin/login.js.map +1 -1
- package/examples/profiles/merchant.deepseek.yaml +1 -0
- package/examples/profiles/merchant.fake.yaml +1 -0
- package/examples/profiles/merchant.local.yaml +1 -0
- package/package.json +8 -3
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 harrylabsj
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* A2A JSON-RPC 方法名(issue 03)。
|
|
18
|
+
*
|
|
19
|
+
* 1.0 方法名与 0.3 legacy 不同:`SendMessage` vs `message/send`、`GetTask` vs
|
|
20
|
+
* `tasks/get` 等。本文件是方法名 + v1↔legacy 映射的单一来源——声明门禁
|
|
21
|
+
* (tests/a2a-version-declaration.test.ts)与双栈 client/server 都从这读取。
|
|
22
|
+
*/
|
|
23
|
+
/** A2A 1.0 方法名常量。 */
|
|
24
|
+
export const METHOD_SEND_MESSAGE = "SendMessage";
|
|
25
|
+
export const METHOD_GET_TASK = "GetTask";
|
|
26
|
+
export const METHOD_LIST_TASKS = "ListTasks";
|
|
27
|
+
export const METHOD_CANCEL_TASK = "CancelTask";
|
|
28
|
+
export const METHOD_SUBSCRIBE_TO_TASK = "SubscribeToTask";
|
|
29
|
+
/** A2A 1.0: GetExtendedAgentCard(issue 10 / TCK CARD-EXT-001)。 */
|
|
30
|
+
export const METHOD_GET_EXTENDED_AGENT_CARD = "GetExtendedAgentCard";
|
|
31
|
+
/** A2A 1.0: SendStreamingMessage(issue 10 / TCK JSONRPC-SSE-002:不支持流式 → -32004)。 */
|
|
32
|
+
export const METHOD_SEND_STREAMING_MESSAGE = "SendStreamingMessage";
|
|
33
|
+
/** A2A 1.0 push 通知配置方法(issue 10 / TCK JSONRPC-SSE-002:不支持推送 → -32003)。 */
|
|
34
|
+
export const METHOD_CREATE_PUSH_CONFIG = "CreateTaskPushNotificationConfig";
|
|
35
|
+
export const METHOD_GET_PUSH_CONFIG = "GetTaskPushNotificationConfig";
|
|
36
|
+
export const METHOD_LIST_PUSH_CONFIGS = "ListTaskPushNotificationConfigs";
|
|
37
|
+
export const METHOD_DELETE_PUSH_CONFIG = "DeleteTaskPushNotificationConfig";
|
|
38
|
+
/** A2A 0.3 legacy 方法名常量。 */
|
|
39
|
+
export const LEGACY_METHOD_SEND_MESSAGE = "message/send";
|
|
40
|
+
export const LEGACY_METHOD_GET_TASK = "tasks/get";
|
|
41
|
+
/** 1.0 → legacy 映射(1.0 方法名 → 0.3 方法名)。 */
|
|
42
|
+
export const V1_TO_LEGACY = {
|
|
43
|
+
[METHOD_SEND_MESSAGE]: LEGACY_METHOD_SEND_MESSAGE,
|
|
44
|
+
[METHOD_GET_TASK]: LEGACY_METHOD_GET_TASK,
|
|
45
|
+
};
|
|
46
|
+
/** legacy → 1.0 映射(0.3 方法名 → 1.0 方法名)。 */
|
|
47
|
+
export const LEGACY_TO_V1 = Object.fromEntries(Object.entries(V1_TO_LEGACY).map(([v1, legacy]) => [legacy, v1]));
|
|
48
|
+
/** 全部 1.0 方法名集合。 */
|
|
49
|
+
export const V1_METHODS = new Set([
|
|
50
|
+
METHOD_SEND_MESSAGE,
|
|
51
|
+
METHOD_GET_TASK,
|
|
52
|
+
METHOD_LIST_TASKS,
|
|
53
|
+
METHOD_CANCEL_TASK,
|
|
54
|
+
METHOD_SUBSCRIBE_TO_TASK,
|
|
55
|
+
METHOD_GET_EXTENDED_AGENT_CARD,
|
|
56
|
+
METHOD_SEND_STREAMING_MESSAGE,
|
|
57
|
+
METHOD_CREATE_PUSH_CONFIG,
|
|
58
|
+
METHOD_GET_PUSH_CONFIG,
|
|
59
|
+
METHOD_LIST_PUSH_CONFIGS,
|
|
60
|
+
METHOD_DELETE_PUSH_CONFIG,
|
|
61
|
+
]);
|
|
62
|
+
/** 全部 0.3 legacy 方法名集合。 */
|
|
63
|
+
export const LEGACY_METHODS = new Set([
|
|
64
|
+
LEGACY_METHOD_SEND_MESSAGE,
|
|
65
|
+
LEGACY_METHOD_GET_TASK,
|
|
66
|
+
]);
|
|
67
|
+
/** 给定方法名是否为 1.0 方法。 */
|
|
68
|
+
export function isV1Method(method) {
|
|
69
|
+
return V1_METHODS.has(method);
|
|
70
|
+
}
|
|
71
|
+
/** 给定方法名是否为 0.3 legacy 方法。 */
|
|
72
|
+
export function isLegacyMethod(method) {
|
|
73
|
+
return LEGACY_METHODS.has(method);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=methods.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"methods.js","sourceRoot":"","sources":["../../../src/a2a/v1/methods.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;GAMG;AAEH,qBAAqB;AACrB,MAAM,CAAC,MAAM,mBAAmB,GAAG,aAAa,CAAC;AACjD,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC;AACzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAC;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,YAAY,CAAC;AAC/C,MAAM,CAAC,MAAM,wBAAwB,GAAG,iBAAiB,CAAC;AAC1D,kEAAkE;AAClE,MAAM,CAAC,MAAM,8BAA8B,GAAG,sBAAsB,CAAC;AACrE,oFAAoF;AACpF,MAAM,CAAC,MAAM,6BAA6B,GAAG,sBAAsB,CAAC;AACpE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,yBAAyB,GAAG,kCAAkC,CAAC;AAC5E,MAAM,CAAC,MAAM,sBAAsB,GAAG,+BAA+B,CAAC;AACtE,MAAM,CAAC,MAAM,wBAAwB,GAAG,iCAAiC,CAAC;AAC1E,MAAM,CAAC,MAAM,yBAAyB,GAAG,kCAAkC,CAAC;AAE5E,4BAA4B;AAC5B,MAAM,CAAC,MAAM,0BAA0B,GAAG,cAAc,CAAC;AACzD,MAAM,CAAC,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAElD,0CAA0C;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAqC;IAC5D,CAAC,mBAAmB,CAAC,EAAE,0BAA0B;IACjD,CAAC,eAAe,CAAC,EAAE,sBAAsB;CAC1C,CAAC;AAEF,0CAA0C;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAqC,MAAM,CAAC,WAAW,CAC9E,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CACjE,CAAC;AAEF,oBAAoB;AACpB,MAAM,CAAC,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC;IACrD,mBAAmB;IACnB,eAAe;IACf,iBAAiB;IACjB,kBAAkB;IAClB,wBAAwB;IACxB,8BAA8B;IAC9B,6BAA6B;IAC7B,yBAAyB;IACzB,sBAAsB;IACtB,wBAAwB;IACxB,yBAAyB;CAC1B,CAAC,CAAC;AAEH,2BAA2B;AAC3B,MAAM,CAAC,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC;IACzD,0BAA0B;IAC1B,sBAAsB;CACvB,CAAC,CAAC;AAEH,uBAAuB;AACvB,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,8BAA8B;AAC9B,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 harrylabsj
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* A2A Part 编解码 + KNP DataPart 映射(issue 04,高风险点)。
|
|
18
|
+
*
|
|
19
|
+
* 0.3(`{kind:"text"|"data"}`)↔ 1.0(统一 Part,字段存在性判别)双向转换。
|
|
20
|
+
* KNP 载荷(`knp_envelope` / `agreement`)必须无损往返——磋商内核(envelope
|
|
21
|
+
* 解析/相位机)100% 复用,只在传输帧层转换。
|
|
22
|
+
*/
|
|
23
|
+
import type { A2APart } from "../client/types.js";
|
|
24
|
+
import type { A2AV1Part, DataPart } from "./types.js";
|
|
25
|
+
/** KNP 载荷承载的 mediaType(KNP envelope 是 JSON)。 */
|
|
26
|
+
export declare const KNP_DATA_MEDIA_TYPE = "application/json";
|
|
27
|
+
/**
|
|
28
|
+
* 0.3 Part → 1.0 统一 Part。
|
|
29
|
+
* - text → TextPart;
|
|
30
|
+
* - data → DataPart(data 原样 + KNP mediaType)。
|
|
31
|
+
*/
|
|
32
|
+
export declare function encodeV1Part(legacy: A2APart): A2AV1Part;
|
|
33
|
+
/**
|
|
34
|
+
* 1.0 统一 Part → 0.3 Part。
|
|
35
|
+
* - TextPart → text;
|
|
36
|
+
* - DataPart → data(原样还原)。
|
|
37
|
+
* - URLPart/FilePart 在 0.3 模型无等价(0.3 只建模 text/data)→ fail-closed。
|
|
38
|
+
*/
|
|
39
|
+
export declare function decodeV1Part(v1: A2AV1Part): A2APart;
|
|
40
|
+
/** 识别 KNP 载荷 Part(data 内含 knp_envelope 或 agreement)。 */
|
|
41
|
+
export declare function isKnpDataPart(part: A2AV1Part): part is DataPart;
|
|
42
|
+
/**
|
|
43
|
+
* 输入 Part 是否受支持(issue 10 / TCK CORE-SEND-003)。fail-closed:未知
|
|
44
|
+
* 形状一律不支持。TextPart 直接支持;DataPart 仅当 mediaType 为
|
|
45
|
+
* application/json(含 KNP);带其他 mediaType(如 raw/file/url)不支持 →
|
|
46
|
+
* server 返回 ContentTypeNotSupportedError(-32005),而非内部错误。
|
|
47
|
+
*/
|
|
48
|
+
export declare function isV1InputPartSupported(part: A2AV1Part): boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 harrylabsj
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/** KNP 载荷承载的 mediaType(KNP envelope 是 JSON)。 */
|
|
17
|
+
export const KNP_DATA_MEDIA_TYPE = "application/json";
|
|
18
|
+
/**
|
|
19
|
+
* 0.3 Part → 1.0 统一 Part。
|
|
20
|
+
* - text → TextPart;
|
|
21
|
+
* - data → DataPart(data 原样 + KNP mediaType)。
|
|
22
|
+
*/
|
|
23
|
+
export function encodeV1Part(legacy) {
|
|
24
|
+
if (legacy.kind === "text")
|
|
25
|
+
return { text: legacy.text };
|
|
26
|
+
return { data: legacy.data, mediaType: KNP_DATA_MEDIA_TYPE };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 1.0 统一 Part → 0.3 Part。
|
|
30
|
+
* - TextPart → text;
|
|
31
|
+
* - DataPart → data(原样还原)。
|
|
32
|
+
* - URLPart/FilePart 在 0.3 模型无等价(0.3 只建模 text/data)→ fail-closed。
|
|
33
|
+
*/
|
|
34
|
+
export function decodeV1Part(v1) {
|
|
35
|
+
if ("text" in v1)
|
|
36
|
+
return { kind: "text", text: v1.text };
|
|
37
|
+
if ("data" in v1)
|
|
38
|
+
return { kind: "data", data: v1.data };
|
|
39
|
+
throw new Error(`v1 Part 无法映射到 0.3 模型(0.3 只建模 text/data):${JSON.stringify(v1).slice(0, 200)}`);
|
|
40
|
+
}
|
|
41
|
+
/** 识别 KNP 载荷 Part(data 内含 knp_envelope 或 agreement)。 */
|
|
42
|
+
export function isKnpDataPart(part) {
|
|
43
|
+
if (!("data" in part))
|
|
44
|
+
return false;
|
|
45
|
+
return "knp_envelope" in part.data || "agreement" in part.data;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 输入 Part 是否受支持(issue 10 / TCK CORE-SEND-003)。fail-closed:未知
|
|
49
|
+
* 形状一律不支持。TextPart 直接支持;DataPart 仅当 mediaType 为
|
|
50
|
+
* application/json(含 KNP);带其他 mediaType(如 raw/file/url)不支持 →
|
|
51
|
+
* server 返回 ContentTypeNotSupportedError(-32005),而非内部错误。
|
|
52
|
+
*/
|
|
53
|
+
export function isV1InputPartSupported(part) {
|
|
54
|
+
if (typeof part !== "object" || part === null)
|
|
55
|
+
return false;
|
|
56
|
+
if ("text" in part)
|
|
57
|
+
return true;
|
|
58
|
+
const mediaType = "mediaType" in part && typeof part.mediaType === "string" ? part.mediaType : undefined;
|
|
59
|
+
if (mediaType === undefined)
|
|
60
|
+
return false;
|
|
61
|
+
return mediaType.startsWith("text/") || mediaType === "application/json";
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=part.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"part.js","sourceRoot":"","sources":["../../../src/a2a/v1/part.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAaH,gDAAgD;AAChD,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IACzD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC;AAC/D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,EAAa;IACxC,IAAI,MAAM,IAAI,EAAE;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IACzD,IAAI,MAAM,IAAI,EAAE;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IACzD,MAAM,IAAI,KAAK,CACb,2CAA2C,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC9E,CAAC;AACJ,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,IAAe;IAC3C,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,OAAO,cAAc,IAAI,IAAI,CAAC,IAAI,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,CAAC;AACjE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAe;IACpD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5D,IAAI,MAAM,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,SAAS,GACb,WAAW,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACzF,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,SAAS,KAAK,kBAAkB,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 harrylabsj
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* A2A 1.0 域模型(issue 02)。
|
|
18
|
+
*
|
|
19
|
+
* 与 0.3(`src/a2a/client/types.ts`)的差异:
|
|
20
|
+
* - **统一 Part**:字段存在性判别(有 `text`=Text / `data`=Data / `url`=URL /
|
|
21
|
+
* `raw`=File),**无 `kind` 字段**;`mimeType` → `mediaType`;
|
|
22
|
+
* - **Role**:`ROLE_AGENT` / `ROLE_USER` 命名常量;
|
|
23
|
+
* - **TaskState**:大写下划线枚举,含 1.0 新增 `REJECTED` / `AUTH_REQUIRED` /
|
|
24
|
+
* `UNSPECIFIED`(0.3 的 `unknown` 在 1.0 语义为 `UNSPECIFIED`)。
|
|
25
|
+
*
|
|
26
|
+
* 词表单一来源(仿照 `handoff/destination.ts` 纪律):本文件是 v1 词表的唯一
|
|
27
|
+
* 权威,编解码/双栈/TCK 全部从这里导入,不散落字符串。
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* A2A 1.0 Role(wire 值 = proto 枚举名,官方 SDK `@a2a-js/sdk` 如此序列化)。
|
|
31
|
+
* 输入侧兼容小写 `user`/`agent`(server 归一化,见 normalizeV1Role)。
|
|
32
|
+
*/
|
|
33
|
+
export declare const ROLE_AGENT: "ROLE_AGENT";
|
|
34
|
+
export declare const ROLE_USER: "ROLE_USER";
|
|
35
|
+
export type A2ARole = typeof ROLE_AGENT | typeof ROLE_USER;
|
|
36
|
+
/**
|
|
37
|
+
* A2A 1.0 TaskState(wire 值 = proto 枚举名 `TASK_STATE_*`,官方 SDK 如此)。
|
|
38
|
+
* 0.3 `unknown` → 1.0 `TASK_STATE_UNSPECIFIED`。
|
|
39
|
+
*/
|
|
40
|
+
export declare const A2A_TASK_STATES: readonly ["TASK_STATE_SUBMITTED", "TASK_STATE_WORKING", "TASK_STATE_INPUT_REQUIRED", "TASK_STATE_COMPLETED", "TASK_STATE_CANCELED", "TASK_STATE_FAILED", "TASK_STATE_REJECTED", "TASK_STATE_AUTH_REQUIRED", "TASK_STATE_UNSPECIFIED"];
|
|
41
|
+
export type A2ATaskState = (typeof A2A_TASK_STATES)[number];
|
|
42
|
+
/** 0.3 内部小写状态 → 1.0 wire TaskState(server 出站用)。 */
|
|
43
|
+
export declare const LEGACY_TO_V1_STATE: Readonly<Record<string, string>>;
|
|
44
|
+
/** 1.0 wire TaskState → 0.3 内部小写状态(client 入站用)。 */
|
|
45
|
+
export declare const V1_STATE_TO_LEGACY: Readonly<Record<string, string>>;
|
|
46
|
+
/** A2A 1.0 统一 Part:字段存在性判别(无 `kind`)。 */
|
|
47
|
+
export type A2AV1Part = TextPart | DataPart | FilePart | URLPart;
|
|
48
|
+
/** Text Part(有 `text` 字段即判别)。 */
|
|
49
|
+
export interface TextPart {
|
|
50
|
+
text: string;
|
|
51
|
+
}
|
|
52
|
+
/** Data Part(有 `data` 字段即判别;KNP 载荷载体,`mediaType` 可选)。 */
|
|
53
|
+
export interface DataPart {
|
|
54
|
+
data: Record<string, unknown>;
|
|
55
|
+
mediaType?: string;
|
|
56
|
+
}
|
|
57
|
+
/** File Part(有 `raw` 字段即判别;`raw` 为 base64,必须带 `mediaType`)。 */
|
|
58
|
+
export interface FilePart {
|
|
59
|
+
raw: string;
|
|
60
|
+
mediaType: string;
|
|
61
|
+
}
|
|
62
|
+
/** URL Part(有 `url` 字段即判别)。 */
|
|
63
|
+
export interface URLPart {
|
|
64
|
+
url: string;
|
|
65
|
+
}
|
|
66
|
+
/** A2A 1.0 Message。 */
|
|
67
|
+
export interface A2AV1Message {
|
|
68
|
+
role: A2ARole;
|
|
69
|
+
parts: A2AV1Part[];
|
|
70
|
+
messageId: string;
|
|
71
|
+
taskId?: string;
|
|
72
|
+
contextId?: string;
|
|
73
|
+
metadata?: Record<string, unknown>;
|
|
74
|
+
}
|
|
75
|
+
/** A2A 1.0 TaskStatus。 */
|
|
76
|
+
export interface A2AV1TaskStatus {
|
|
77
|
+
state: A2ATaskState;
|
|
78
|
+
message?: A2AV1Message;
|
|
79
|
+
timestamp?: string;
|
|
80
|
+
}
|
|
81
|
+
/** A2A 1.0 Artifact。 */
|
|
82
|
+
export interface A2AV1Artifact {
|
|
83
|
+
parts: A2AV1Part[];
|
|
84
|
+
artifactId?: string;
|
|
85
|
+
metadata?: Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
/** A2A 1.0 Task。 */
|
|
88
|
+
export interface A2AV1Task {
|
|
89
|
+
id: string;
|
|
90
|
+
status: A2AV1TaskStatus;
|
|
91
|
+
artifacts?: A2AV1Artifact[];
|
|
92
|
+
contextId?: string;
|
|
93
|
+
metadata?: Record<string, unknown>;
|
|
94
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 harrylabsj
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* A2A 1.0 域模型(issue 02)。
|
|
18
|
+
*
|
|
19
|
+
* 与 0.3(`src/a2a/client/types.ts`)的差异:
|
|
20
|
+
* - **统一 Part**:字段存在性判别(有 `text`=Text / `data`=Data / `url`=URL /
|
|
21
|
+
* `raw`=File),**无 `kind` 字段**;`mimeType` → `mediaType`;
|
|
22
|
+
* - **Role**:`ROLE_AGENT` / `ROLE_USER` 命名常量;
|
|
23
|
+
* - **TaskState**:大写下划线枚举,含 1.0 新增 `REJECTED` / `AUTH_REQUIRED` /
|
|
24
|
+
* `UNSPECIFIED`(0.3 的 `unknown` 在 1.0 语义为 `UNSPECIFIED`)。
|
|
25
|
+
*
|
|
26
|
+
* 词表单一来源(仿照 `handoff/destination.ts` 纪律):本文件是 v1 词表的唯一
|
|
27
|
+
* 权威,编解码/双栈/TCK 全部从这里导入,不散落字符串。
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* A2A 1.0 Role(wire 值 = proto 枚举名,官方 SDK `@a2a-js/sdk` 如此序列化)。
|
|
31
|
+
* 输入侧兼容小写 `user`/`agent`(server 归一化,见 normalizeV1Role)。
|
|
32
|
+
*/
|
|
33
|
+
export const ROLE_AGENT = "ROLE_AGENT";
|
|
34
|
+
export const ROLE_USER = "ROLE_USER";
|
|
35
|
+
/**
|
|
36
|
+
* A2A 1.0 TaskState(wire 值 = proto 枚举名 `TASK_STATE_*`,官方 SDK 如此)。
|
|
37
|
+
* 0.3 `unknown` → 1.0 `TASK_STATE_UNSPECIFIED`。
|
|
38
|
+
*/
|
|
39
|
+
export const A2A_TASK_STATES = [
|
|
40
|
+
"TASK_STATE_SUBMITTED",
|
|
41
|
+
"TASK_STATE_WORKING",
|
|
42
|
+
"TASK_STATE_INPUT_REQUIRED",
|
|
43
|
+
"TASK_STATE_COMPLETED",
|
|
44
|
+
"TASK_STATE_CANCELED",
|
|
45
|
+
"TASK_STATE_FAILED",
|
|
46
|
+
"TASK_STATE_REJECTED",
|
|
47
|
+
"TASK_STATE_AUTH_REQUIRED",
|
|
48
|
+
"TASK_STATE_UNSPECIFIED",
|
|
49
|
+
];
|
|
50
|
+
/** 0.3 内部小写状态 → 1.0 wire TaskState(server 出站用)。 */
|
|
51
|
+
export const LEGACY_TO_V1_STATE = {
|
|
52
|
+
submitted: "TASK_STATE_SUBMITTED",
|
|
53
|
+
working: "TASK_STATE_WORKING",
|
|
54
|
+
"input-required": "TASK_STATE_INPUT_REQUIRED",
|
|
55
|
+
completed: "TASK_STATE_COMPLETED",
|
|
56
|
+
canceled: "TASK_STATE_CANCELED",
|
|
57
|
+
failed: "TASK_STATE_FAILED",
|
|
58
|
+
unknown: "TASK_STATE_UNSPECIFIED",
|
|
59
|
+
};
|
|
60
|
+
/** 1.0 wire TaskState → 0.3 内部小写状态(client 入站用)。 */
|
|
61
|
+
export const V1_STATE_TO_LEGACY = Object.fromEntries(Object.entries(LEGACY_TO_V1_STATE).map(([legacy, v1]) => [v1, legacy]));
|
|
62
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/a2a/v1/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;GAYG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,YAAqB,CAAC;AAChD,MAAM,CAAC,MAAM,SAAS,GAAG,WAAoB,CAAC;AAG9C;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,sBAAsB;IACtB,oBAAoB;IACpB,2BAA2B;IAC3B,sBAAsB;IACtB,qBAAqB;IACrB,mBAAmB;IACnB,qBAAqB;IACrB,0BAA0B;IAC1B,wBAAwB;CAChB,CAAC;AAGX,mDAAmD;AACnD,MAAM,CAAC,MAAM,kBAAkB,GAAqC;IAClE,SAAS,EAAE,sBAAsB;IACjC,OAAO,EAAE,oBAAoB;IAC7B,gBAAgB,EAAE,2BAA2B;IAC7C,SAAS,EAAE,sBAAsB;IACjC,QAAQ,EAAE,qBAAqB;IAC/B,MAAM,EAAE,mBAAmB;IAC3B,OAAO,EAAE,wBAAwB;CAClC,CAAC;AAEF,mDAAmD;AACnD,MAAM,CAAC,MAAM,kBAAkB,GAAqC,MAAM,CAAC,WAAW,CACpF,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CACvE,CAAC"}
|
|
@@ -58,7 +58,9 @@ function parseIntent(value) {
|
|
|
58
58
|
if (typeof v.quantity === "number" && Number.isInteger(v.quantity) && v.quantity > 0) {
|
|
59
59
|
out.quantity = v.quantity;
|
|
60
60
|
}
|
|
61
|
-
if (typeof v.target_unit_price === "number" &&
|
|
61
|
+
if (typeof v.target_unit_price === "number" &&
|
|
62
|
+
Number.isFinite(v.target_unit_price) &&
|
|
63
|
+
v.target_unit_price >= 0) {
|
|
62
64
|
out.target_unit_price = v.target_unit_price;
|
|
63
65
|
}
|
|
64
66
|
if (Array.isArray(v.preferences))
|
|
@@ -80,7 +82,9 @@ function parseConstraints(value) {
|
|
|
80
82
|
}
|
|
81
83
|
const v = value;
|
|
82
84
|
const out = {};
|
|
83
|
-
if (typeof v.max_unit_price === "number" &&
|
|
85
|
+
if (typeof v.max_unit_price === "number" &&
|
|
86
|
+
Number.isFinite(v.max_unit_price) &&
|
|
87
|
+
v.max_unit_price >= 0) {
|
|
84
88
|
out.max_unit_price = v.max_unit_price;
|
|
85
89
|
}
|
|
86
90
|
if (typeof v.max_total_price === "number" && Number.isFinite(v.max_total_price)) {
|
|
@@ -140,7 +144,10 @@ function executeSelection(store, now, args) {
|
|
|
140
144
|
authorization: p.user_instruction,
|
|
141
145
|
score_explanation: candidate.score_explanation ?? null,
|
|
142
146
|
boundary: "未创建订单;非绑定选定不声明价格、库存或交期仍然有效",
|
|
143
|
-
}, "user",
|
|
147
|
+
}, "user",
|
|
148
|
+
// 审查 K-L26:幂等键内容寻址(此前 uuidv7 随机,重试产生重复 selected
|
|
149
|
+
// 事件)。与 transition 键同源,重试去重。
|
|
150
|
+
`select:${p.task_id}:${p.candidate_id}`);
|
|
144
151
|
store.updateCandidate(p.candidate_id, { candidate_status: "selected" });
|
|
145
152
|
store.transitionTask({
|
|
146
153
|
task_id: p.task_id,
|
|
@@ -251,7 +258,12 @@ async function executeNegotiateBuyerTask(deps, args) {
|
|
|
251
258
|
negotiation_id: result.negotiationId,
|
|
252
259
|
catalog_agent_id: result.catalogAgentId,
|
|
253
260
|
}, "model", eventKey);
|
|
254
|
-
return {
|
|
261
|
+
return {
|
|
262
|
+
ok: false,
|
|
263
|
+
task_id: a.task_id,
|
|
264
|
+
negotiation_id: result.negotiationId,
|
|
265
|
+
error: result.error,
|
|
266
|
+
};
|
|
255
267
|
}
|
|
256
268
|
const facts = result.facts;
|
|
257
269
|
try {
|
|
@@ -313,7 +325,8 @@ async function executeNegotiateBuyerTask(deps, args) {
|
|
|
313
325
|
steps: result.steps,
|
|
314
326
|
boundary: "非绑定协议 — 不创建订单、不锁库存、不授权支付",
|
|
315
327
|
}, "model", eventKey);
|
|
316
|
-
await deps
|
|
328
|
+
await deps
|
|
329
|
+
.recordNegotiation?.({
|
|
317
330
|
negotiationId: result.negotiationId,
|
|
318
331
|
catalogAgentId: result.catalogAgentId,
|
|
319
332
|
sku: facts?.sku ?? sku,
|
|
@@ -323,7 +336,8 @@ async function executeNegotiateBuyerTask(deps, args) {
|
|
|
323
336
|
agreementId: typeof result.agreement?.agreement_id === "string"
|
|
324
337
|
? result.agreement.agreement_id
|
|
325
338
|
: undefined,
|
|
326
|
-
})
|
|
339
|
+
})
|
|
340
|
+
.catch(() => undefined); // 记忆尽力而为,失败不阻塞任务推进
|
|
327
341
|
return {
|
|
328
342
|
ok: true,
|
|
329
343
|
task_id: a.task_id,
|
|
@@ -500,6 +514,23 @@ export function buildBuyerTools(deps) {
|
|
|
500
514
|
.join("\n"));
|
|
501
515
|
},
|
|
502
516
|
};
|
|
517
|
+
const listPendingApprovals = {
|
|
518
|
+
name: "list_pending_approvals",
|
|
519
|
+
label: "列出待审批候选",
|
|
520
|
+
description: "读取当前仍可执行的写操作审批候选。涉及 /approve 时必须先调用本工具;只允许使用本次返回的 candidate_id," +
|
|
521
|
+
"不能从历史对话、任务事件或记忆中复制旧 ID。",
|
|
522
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
523
|
+
execute: async () => {
|
|
524
|
+
const pending = deps.approvals?.listPending() ?? [];
|
|
525
|
+
if (pending.length === 0) {
|
|
526
|
+
return textResult("当前没有等待批准且仍可执行的写操作候选。候选在重启后会自动失效;如需继续,请重新生成候选。");
|
|
527
|
+
}
|
|
528
|
+
return textResult(pending
|
|
529
|
+
.map((candidate, index) => `${index + 1}. ${candidate.candidate_id} [${candidate.risk}] ${candidate.tool}` +
|
|
530
|
+
`${candidate.task_id === undefined ? "" : ` task=${candidate.task_id}`}(截止 ${candidate.expires_at})`)
|
|
531
|
+
.join("\n"));
|
|
532
|
+
},
|
|
533
|
+
};
|
|
503
534
|
const getTask = {
|
|
504
535
|
name: "get_buyer_task",
|
|
505
536
|
label: "任务详情",
|
|
@@ -550,9 +581,7 @@ export function buildBuyerTools(deps) {
|
|
|
550
581
|
.at(-1);
|
|
551
582
|
if (negotiated !== undefined) {
|
|
552
583
|
const p = negotiated.payload;
|
|
553
|
-
const fmt = (minor) => typeof minor === "number" && Number.isFinite(minor)
|
|
554
|
-
? (minor / 100).toFixed(2)
|
|
555
|
-
: "?";
|
|
584
|
+
const fmt = (minor) => typeof minor === "number" && Number.isFinite(minor) ? (minor / 100).toFixed(2) : "?";
|
|
556
585
|
const sku = typeof p.sku === "string" ? p.sku : "?";
|
|
557
586
|
const qty = typeof p.quantity === "number" ? String(p.quantity) : "?";
|
|
558
587
|
const agreement = typeof p.agreement_id === "string" ? p.agreement_id : "?";
|
|
@@ -577,7 +606,10 @@ export function buildBuyerTools(deps) {
|
|
|
577
606
|
type: "object",
|
|
578
607
|
properties: {
|
|
579
608
|
goal_text: { type: "string", description: "用户原始目标的简洁表达" },
|
|
580
|
-
intent: {
|
|
609
|
+
intent: {
|
|
610
|
+
type: "object",
|
|
611
|
+
description: "结构化意图(category/query_text/city/needed_by/quantity/target_unit_price 等;target_unit_price = 砍价目标单价)",
|
|
612
|
+
},
|
|
581
613
|
constraints: {
|
|
582
614
|
type: "object",
|
|
583
615
|
description: "硬约束(max_total_price 私有预算/latest_eta/required_terms/exclude_out_of_stock)",
|
|
@@ -606,7 +638,12 @@ export function buildBuyerTools(deps) {
|
|
|
606
638
|
// Content-addressed: a model retry with the same args replays the
|
|
607
639
|
// existing task instead of creating a duplicate.
|
|
608
640
|
idempotency_key: `create:${createHash("sha256")
|
|
609
|
-
.update(JSON.stringify({
|
|
641
|
+
.update(JSON.stringify({
|
|
642
|
+
goal_text: goalText,
|
|
643
|
+
intent,
|
|
644
|
+
constraints,
|
|
645
|
+
expires_at: expiresAt ?? null,
|
|
646
|
+
}))
|
|
610
647
|
.digest("hex")
|
|
611
648
|
.slice(0, 16)}`,
|
|
612
649
|
});
|
|
@@ -739,7 +776,13 @@ export function buildBuyerTools(deps) {
|
|
|
739
776
|
candidate_id: { type: "string" },
|
|
740
777
|
rule_type: {
|
|
741
778
|
type: "string",
|
|
742
|
-
enum: [
|
|
779
|
+
enum: [
|
|
780
|
+
"price_below",
|
|
781
|
+
"stock_available",
|
|
782
|
+
"delivery_before",
|
|
783
|
+
"new_candidate",
|
|
784
|
+
"periodic_review",
|
|
785
|
+
],
|
|
743
786
|
},
|
|
744
787
|
condition: { type: "object", description: "如 {threshold: 90} 或 {eta_before: RFC3339}" },
|
|
745
788
|
interval_seconds: { type: "integer" },
|
|
@@ -916,6 +959,7 @@ export function buildBuyerTools(deps) {
|
|
|
916
959
|
registerPending: deps.registerPending,
|
|
917
960
|
}, {
|
|
918
961
|
tool: "start_consultation",
|
|
962
|
+
task_id: p.task_id,
|
|
919
963
|
arguments: args,
|
|
920
964
|
preconditions: consultationPreconditions(store, p.task_id, p.candidate_id),
|
|
921
965
|
risk: "send_consultation",
|
|
@@ -982,8 +1026,15 @@ export function buildBuyerTools(deps) {
|
|
|
982
1026
|
? { catalog_agent_id: p.catalog_agent_id }
|
|
983
1027
|
: {}),
|
|
984
1028
|
};
|
|
985
|
-
const outcome = await routeWriteCandidate({
|
|
1029
|
+
const outcome = await routeWriteCandidate({
|
|
1030
|
+
mode: deps.mode,
|
|
1031
|
+
approvals: deps.approvals,
|
|
1032
|
+
profile,
|
|
1033
|
+
now,
|
|
1034
|
+
registerPending: deps.registerPending,
|
|
1035
|
+
}, {
|
|
986
1036
|
tool: "negotiate_buyer_task",
|
|
1037
|
+
task_id: p.task_id,
|
|
987
1038
|
arguments: args,
|
|
988
1039
|
preconditions: negotiationPreconditions(store, true, p.task_id),
|
|
989
1040
|
risk: "send_negotiation_message",
|
|
@@ -1029,9 +1080,15 @@ export function buildBuyerTools(deps) {
|
|
|
1029
1080
|
parameters: {
|
|
1030
1081
|
type: "object",
|
|
1031
1082
|
properties: {
|
|
1032
|
-
task_id: {
|
|
1083
|
+
task_id: {
|
|
1084
|
+
type: "string",
|
|
1085
|
+
description: "已达成非绑定协议(selected_nonbinding)的 buyer 任务",
|
|
1086
|
+
},
|
|
1033
1087
|
display_summary_merchant: { type: "string", description: "展示用商家名(缺省取协议商家)" },
|
|
1034
|
-
display_summary_text: {
|
|
1088
|
+
display_summary_text: {
|
|
1089
|
+
type: "string",
|
|
1090
|
+
description: "展示用摘要(如“200 units, CNY 8999.00/unit”)",
|
|
1091
|
+
},
|
|
1035
1092
|
expires_in_hours: { type: "number", description: "候选有效期(小时,缺省 24)" },
|
|
1036
1093
|
},
|
|
1037
1094
|
required: ["task_id"],
|
|
@@ -1067,7 +1124,9 @@ export function buildBuyerTools(deps) {
|
|
|
1067
1124
|
// 协议级去重:同一磋商、同一目的地已交付过 → 拒绝(LLM 重试会生成
|
|
1068
1125
|
// 新候选 → 新 digest,绕过 (candidate_id, digest) 幂等键,导致同协议
|
|
1069
1126
|
// 二次交付/二次 URL 探测、negotiation_to_handoff_rate 虚高)。
|
|
1070
|
-
const priorDelivery = handoff.ledger
|
|
1127
|
+
const priorDelivery = handoff.ledger
|
|
1128
|
+
.events(agreement.negotiation_id)
|
|
1129
|
+
.find((e) => e.event_kind === "handoff_delivered" &&
|
|
1071
1130
|
e.destination?.ref === destination.ref &&
|
|
1072
1131
|
e.destination?.type === destination.type);
|
|
1073
1132
|
if (priorDelivery !== undefined) {
|
|
@@ -1105,8 +1164,15 @@ export function buildBuyerTools(deps) {
|
|
|
1105
1164
|
: {}),
|
|
1106
1165
|
requires_user_action: true,
|
|
1107
1166
|
});
|
|
1108
|
-
const outcome = await routeWriteCandidate({
|
|
1167
|
+
const outcome = await routeWriteCandidate({
|
|
1168
|
+
mode: deps.mode,
|
|
1169
|
+
approvals: deps.approvals,
|
|
1170
|
+
profile,
|
|
1171
|
+
now: deps.now,
|
|
1172
|
+
registerPending: deps.registerPending,
|
|
1173
|
+
}, {
|
|
1109
1174
|
tool: "handoff_agreement",
|
|
1175
|
+
task_id: p.task_id,
|
|
1110
1176
|
arguments: { task_id: p.task_id, candidate },
|
|
1111
1177
|
preconditions: { agreement_bound: true },
|
|
1112
1178
|
risk: "handoff_delivery",
|
|
@@ -1280,6 +1346,7 @@ export function buildBuyerTools(deps) {
|
|
|
1280
1346
|
return [
|
|
1281
1347
|
...(catalogFirst ? [] : [searchProducts, getProduct]),
|
|
1282
1348
|
listTasks,
|
|
1349
|
+
listPendingApprovals,
|
|
1283
1350
|
getTask,
|
|
1284
1351
|
createTask,
|
|
1285
1352
|
updateConstraints,
|
|
@@ -1384,7 +1451,9 @@ function handoffResultText(result) {
|
|
|
1384
1451
|
status: "already_delivered",
|
|
1385
1452
|
});
|
|
1386
1453
|
case "stale":
|
|
1387
|
-
return textResult(`交接未执行:候选已失效(${result.reason})。需重新生成候选。`, {
|
|
1454
|
+
return textResult(`交接未执行:候选已失效(${result.reason})。需重新生成候选。`, {
|
|
1455
|
+
status: "stale",
|
|
1456
|
+
});
|
|
1388
1457
|
case "probe_failed":
|
|
1389
1458
|
return textResult(`交接未执行:目的地探测瞬时失败(${result.reason})。候选保持可用,可重试。`, {
|
|
1390
1459
|
status: "probe_failed",
|
|
@@ -1404,7 +1473,11 @@ async function executeHandoffForCandidate(deps, args) {
|
|
|
1404
1473
|
}
|
|
1405
1474
|
const agreement = agreementFromTask(deps.store.taskEvents(a.task_id));
|
|
1406
1475
|
if (agreement === undefined) {
|
|
1407
|
-
return {
|
|
1476
|
+
return {
|
|
1477
|
+
ok: false,
|
|
1478
|
+
status: "stale",
|
|
1479
|
+
error: "任务记录缺少 agreement 快照,交接中止(fail-closed)",
|
|
1480
|
+
};
|
|
1408
1481
|
}
|
|
1409
1482
|
// created 事件在审批通过后才落链:被 /reject 的候选不在 Ledger 留下悬空的
|
|
1410
1483
|
// PROPOSED(此前先落链,/reject 后 /handoff 永远显示"从未获批"的候选,
|