@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.
Files changed (148) hide show
  1. package/dist/a2a/client/client.d.ts +7 -2
  2. package/dist/a2a/client/client.js +34 -5
  3. package/dist/a2a/client/client.js.map +1 -1
  4. package/dist/a2a/client/index.d.ts +1 -1
  5. package/dist/a2a/client/index.js.map +1 -1
  6. package/dist/a2a/client/jsonrpc.js +2 -1
  7. package/dist/a2a/client/jsonrpc.js.map +1 -1
  8. package/dist/a2a/client/parse.js +6 -2
  9. package/dist/a2a/client/parse.js.map +1 -1
  10. package/dist/a2a/client/types.d.ts +11 -0
  11. package/dist/a2a/client/url-policy.js +29 -0
  12. package/dist/a2a/client/url-policy.js.map +1 -1
  13. package/dist/a2a/node.d.ts +10 -0
  14. package/dist/a2a/node.js +91 -14
  15. package/dist/a2a/node.js.map +1 -1
  16. package/dist/a2a/server/card.d.ts +6 -0
  17. package/dist/a2a/server/card.js +55 -1
  18. package/dist/a2a/server/card.js.map +1 -1
  19. package/dist/a2a/server/errors.d.ts +12 -2
  20. package/dist/a2a/server/errors.js +13 -2
  21. package/dist/a2a/server/errors.js.map +1 -1
  22. package/dist/a2a/server/generic-responder.d.ts +48 -0
  23. package/dist/a2a/server/generic-responder.js +57 -0
  24. package/dist/a2a/server/generic-responder.js.map +1 -0
  25. package/dist/a2a/server/index.d.ts +2 -0
  26. package/dist/a2a/server/index.js +2 -0
  27. package/dist/a2a/server/index.js.map +1 -1
  28. package/dist/a2a/server/merchant-handler.d.ts +12 -1
  29. package/dist/a2a/server/merchant-handler.js +96 -22
  30. package/dist/a2a/server/merchant-handler.js.map +1 -1
  31. package/dist/a2a/server/pipeline.d.ts +28 -0
  32. package/dist/a2a/server/pipeline.js +66 -3
  33. package/dist/a2a/server/pipeline.js.map +1 -1
  34. package/dist/a2a/server/server.d.ts +12 -0
  35. package/dist/a2a/server/server.js +255 -11
  36. package/dist/a2a/server/server.js.map +1 -1
  37. package/dist/a2a/server/task-registry.d.ts +8 -0
  38. package/dist/a2a/server/task-registry.js +17 -0
  39. package/dist/a2a/server/task-registry.js.map +1 -1
  40. package/dist/a2a/server/throttle.d.ts +13 -0
  41. package/dist/a2a/server/throttle.js +41 -1
  42. package/dist/a2a/server/throttle.js.map +1 -1
  43. package/dist/a2a/server/types.d.ts +14 -1
  44. package/dist/a2a/signing-key.d.ts +55 -0
  45. package/dist/a2a/signing-key.js +142 -0
  46. package/dist/a2a/signing-key.js.map +1 -0
  47. package/dist/a2a/v1/headers.d.ts +33 -0
  48. package/dist/a2a/v1/headers.js +56 -0
  49. package/dist/a2a/v1/headers.js.map +1 -0
  50. package/dist/a2a/v1/methods.d.ts +52 -0
  51. package/dist/a2a/v1/methods.js +75 -0
  52. package/dist/a2a/v1/methods.js.map +1 -0
  53. package/dist/a2a/v1/part.d.ts +48 -0
  54. package/dist/a2a/v1/part.js +63 -0
  55. package/dist/a2a/v1/part.js.map +1 -0
  56. package/dist/a2a/v1/types.d.ts +94 -0
  57. package/dist/a2a/v1/types.js +62 -0
  58. package/dist/a2a/v1/types.js.map +1 -0
  59. package/dist/agent/buyer/buyer-tools.js +92 -19
  60. package/dist/agent/buyer/buyer-tools.js.map +1 -1
  61. package/dist/agent/buyer/search-loop.js +17 -2
  62. package/dist/agent/buyer/search-loop.js.map +1 -1
  63. package/dist/agent/buyer/types.js +4 -1
  64. package/dist/agent/buyer/types.js.map +1 -1
  65. package/dist/agent/kernel.js +70 -25
  66. package/dist/agent/kernel.js.map +1 -1
  67. package/dist/agent/merchant/action-candidate.d.ts +10 -0
  68. package/dist/agent/merchant/action-candidate.js +36 -4
  69. package/dist/agent/merchant/action-candidate.js.map +1 -1
  70. package/dist/agent/merchant/merchant-client.d.ts +4 -1
  71. package/dist/agent/merchant/merchant-client.js +41 -29
  72. package/dist/agent/merchant/merchant-client.js.map +1 -1
  73. package/dist/agent/negotiation-chat.d.ts +11 -0
  74. package/dist/agent/negotiation-chat.js +15 -0
  75. package/dist/agent/negotiation-chat.js.map +1 -1
  76. package/dist/agent/system-prompt.js +1 -1
  77. package/dist/agent/system-prompt.js.map +1 -1
  78. package/dist/agent/write-gate.d.ts +4 -0
  79. package/dist/agent/write-gate.js +9 -3
  80. package/dist/agent/write-gate.js.map +1 -1
  81. package/dist/cli.d.ts +7 -0
  82. package/dist/cli.js +51 -7
  83. package/dist/cli.js.map +1 -1
  84. package/dist/commerce/data-source.d.ts +11 -1
  85. package/dist/commerce/data-source.js.map +1 -1
  86. package/dist/commerce/shopping-cli-source.js +10 -0
  87. package/dist/commerce/shopping-cli-source.js.map +1 -1
  88. package/dist/config/profile.d.ts +2 -0
  89. package/dist/config/profile.js +21 -7
  90. package/dist/config/profile.js.map +1 -1
  91. package/dist/counterparty/a2a-direct/index.d.ts +4 -0
  92. package/dist/counterparty/a2a-direct/index.js +5 -0
  93. package/dist/counterparty/a2a-direct/index.js.map +1 -1
  94. package/dist/demo/demo-runner.d.ts +81 -0
  95. package/dist/demo/demo-runner.js +344 -0
  96. package/dist/demo/demo-runner.js.map +1 -0
  97. package/dist/discovery/agent-card/types.d.ts +3 -0
  98. package/dist/discovery/agent-card/validate.js +7 -0
  99. package/dist/discovery/agent-card/validate.js.map +1 -1
  100. package/dist/discovery/catalog-source/register.d.ts +5 -1
  101. package/dist/discovery/catalog-source/register.js +14 -3
  102. package/dist/discovery/catalog-source/register.js.map +1 -1
  103. package/dist/discovery/resolve.js +3 -0
  104. package/dist/discovery/resolve.js.map +1 -1
  105. package/dist/discovery/ucp/resolver.d.ts +2 -0
  106. package/dist/discovery/ucp/resolver.js +3 -1
  107. package/dist/discovery/ucp/resolver.js.map +1 -1
  108. package/dist/handoff/ledger.js +23 -5
  109. package/dist/handoff/ledger.js.map +1 -1
  110. package/dist/handoff/order-record.js +26 -4
  111. package/dist/handoff/order-record.js.map +1 -1
  112. package/dist/handoff/transaction.js +13 -3
  113. package/dist/handoff/transaction.js.map +1 -1
  114. package/dist/negotiation/lease/store.js +14 -4
  115. package/dist/negotiation/lease/store.js.map +1 -1
  116. package/dist/negotiation/state/phase.js +12 -0
  117. package/dist/negotiation/state/phase.js.map +1 -1
  118. package/dist/net/safe-http.js +9 -1
  119. package/dist/net/safe-http.js.map +1 -1
  120. package/dist/operator/controller.d.ts +3 -0
  121. package/dist/operator/controller.js +18 -5
  122. package/dist/operator/controller.js.map +1 -1
  123. package/dist/product-buyer.d.ts +5 -0
  124. package/dist/product-buyer.js +1 -1
  125. package/dist/product-buyer.js.map +1 -1
  126. package/dist/product-cli.d.ts +10 -3
  127. package/dist/product-cli.js +13 -5
  128. package/dist/product-cli.js.map +1 -1
  129. package/dist/product-init.d.ts +6 -0
  130. package/dist/product-init.js +1 -1
  131. package/dist/product-init.js.map +1 -1
  132. package/dist/supervisor/manage.d.ts +5 -1
  133. package/dist/supervisor/manage.js +100 -9
  134. package/dist/supervisor/manage.js.map +1 -1
  135. package/dist/trust/identity/auth-verifier.d.ts +8 -0
  136. package/dist/trust/identity/auth-verifier.js +14 -0
  137. package/dist/trust/identity/auth-verifier.js.map +1 -1
  138. package/dist/weixin/credentials.js +15 -4
  139. package/dist/weixin/credentials.js.map +1 -1
  140. package/dist/weixin/ilink-client.d.ts +9 -0
  141. package/dist/weixin/ilink-client.js +46 -0
  142. package/dist/weixin/ilink-client.js.map +1 -1
  143. package/dist/weixin/login.js +5 -2
  144. package/dist/weixin/login.js.map +1 -1
  145. package/examples/profiles/merchant.deepseek.yaml +1 -0
  146. package/examples/profiles/merchant.fake.yaml +1 -0
  147. package/examples/profiles/merchant.local.yaml +1 -0
  148. 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" && Number.isFinite(v.target_unit_price) && v.target_unit_price >= 0) {
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" && Number.isFinite(v.max_unit_price) && v.max_unit_price >= 0) {
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", `select:${p.task_id}:${p.candidate_id}:${uuidv7()}`);
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 { ok: false, task_id: a.task_id, negotiation_id: result.negotiationId, error: result.error };
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.recordNegotiation?.({
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
- }).catch(() => undefined); // 记忆尽力而为,失败不阻塞任务推进
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: { type: "object", description: "结构化意图(category/query_text/city/needed_by/quantity/target_unit_price 等;target_unit_price = 砍价目标单价)" },
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({ goal_text: goalText, intent, constraints, expires_at: expiresAt ?? null }))
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: ["price_below", "stock_available", "delivery_before", "new_candidate", "periodic_review"],
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({ mode: deps.mode, approvals: deps.approvals, profile, now, registerPending: deps.registerPending }, {
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: { type: "string", description: "已达成非绑定协议(selected_nonbinding)的 buyer 任务" },
1083
+ task_id: {
1084
+ type: "string",
1085
+ description: "已达成非绑定协议(selected_nonbinding)的 buyer 任务",
1086
+ },
1033
1087
  display_summary_merchant: { type: "string", description: "展示用商家名(缺省取协议商家)" },
1034
- display_summary_text: { type: "string", description: "展示用摘要(如“200 units, CNY 8999.00/unit”)" },
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.events(agreement.negotiation_id).find((e) => e.event_kind === "handoff_delivered" &&
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({ mode: deps.mode, approvals: deps.approvals, profile, now: deps.now, registerPending: deps.registerPending }, {
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})。需重新生成候选。`, { status: "stale" });
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 { ok: false, status: "stale", error: "任务记录缺少 agreement 快照,交接中止(fail-closed)" };
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 永远显示"从未获批"的候选,