@harrylabsj/kiwi 0.7.9 → 0.7.11
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/contracts/commerce-intent/1.0/schema.json +124 -0
- package/contracts/delegation-policy/1.0/schema.json +143 -0
- package/contracts/effective-authorization/1.0/schema.json +167 -0
- package/contracts/kiwi-contracts.lock.json +1 -1
- package/contracts/manifest.json +25 -1
- package/contracts/persistent-task/1.0/schema.json +191 -0
- package/contracts/vectors/commerce-intent.invalid-no-items.json +7 -0
- package/contracts/vectors/commerce-intent.valid.json +32 -0
- package/contracts/vectors/delegation-policy.invalid-payment-auto.json +15 -0
- package/contracts/vectors/delegation-policy.valid.json +25 -0
- package/contracts/vectors/effective-authorization.invalid-deny-wins.json +20 -0
- package/contracts/vectors/effective-authorization.valid.json +23 -0
- package/contracts/vectors/persistent-task.invalid-no-idempotency.json +7 -0
- package/contracts/vectors/persistent-task.valid.json +45 -0
- package/dist/a2a/client/url-policy.js +52 -14
- package/dist/a2a/client/url-policy.js.map +1 -1
- package/dist/a2a/node.js +27 -1
- package/dist/a2a/node.js.map +1 -1
- package/dist/a2a/server/merchant-handler.d.ts +27 -0
- package/dist/a2a/server/merchant-handler.js +88 -10
- package/dist/a2a/server/merchant-handler.js.map +1 -1
- package/dist/a2a/signing-key.js +18 -0
- package/dist/a2a/signing-key.js.map +1 -1
- package/dist/buyer-core/a2a-knp.d.ts +71 -0
- package/dist/buyer-core/a2a-knp.js +139 -0
- package/dist/buyer-core/a2a-knp.js.map +1 -0
- package/dist/buyer-core/a2a-negotiator.d.ts +37 -0
- package/dist/buyer-core/a2a-negotiator.js +152 -0
- package/dist/buyer-core/a2a-negotiator.js.map +1 -0
- package/dist/buyer-core/a2a-quote-fetcher.d.ts +44 -0
- package/dist/buyer-core/a2a-quote-fetcher.js +178 -0
- package/dist/buyer-core/a2a-quote-fetcher.js.map +1 -0
- package/dist/buyer-core/build-service.d.ts +44 -0
- package/dist/buyer-core/build-service.js +76 -0
- package/dist/buyer-core/build-service.js.map +1 -0
- package/dist/buyer-core/errors.d.ts +31 -0
- package/dist/buyer-core/errors.js +52 -0
- package/dist/buyer-core/errors.js.map +1 -0
- package/dist/buyer-core/merchant-index.d.ts +64 -0
- package/dist/buyer-core/merchant-index.js +213 -0
- package/dist/buyer-core/merchant-index.js.map +1 -0
- package/dist/buyer-core/negotiator.d.ts +51 -0
- package/dist/buyer-core/negotiator.js +181 -0
- package/dist/buyer-core/negotiator.js.map +1 -0
- package/dist/buyer-core/quote-fetcher.d.ts +46 -0
- package/dist/buyer-core/quote-fetcher.js +166 -0
- package/dist/buyer-core/quote-fetcher.js.map +1 -0
- package/dist/buyer-core/service.d.ts +218 -0
- package/dist/buyer-core/service.js +558 -0
- package/dist/buyer-core/service.js.map +1 -0
- package/dist/buyer-core/store.d.ts +84 -0
- package/dist/buyer-core/store.js +256 -0
- package/dist/buyer-core/store.js.map +1 -0
- package/dist/cli.js +25 -0
- package/dist/cli.js.map +1 -1
- package/dist/config/profile.d.ts +9 -0
- package/dist/config/profile.js +23 -0
- package/dist/config/profile.js.map +1 -1
- package/dist/contracts/northbound-schema.d.ts +27 -0
- package/dist/contracts/northbound-schema.js +102 -0
- package/dist/contracts/northbound-schema.js.map +1 -0
- package/dist/handoff/destination.js +16 -5
- package/dist/handoff/destination.js.map +1 -1
- package/dist/http/cli.d.ts +16 -0
- package/dist/http/cli.js +123 -0
- package/dist/http/cli.js.map +1 -0
- package/dist/http/server.d.ts +39 -0
- package/dist/http/server.js +297 -0
- package/dist/http/server.js.map +1 -0
- package/dist/mcp/cli.d.ts +70 -0
- package/dist/mcp/cli.js +147 -0
- package/dist/mcp/cli.js.map +1 -0
- package/dist/mcp/server.d.ts +41 -0
- package/dist/mcp/server.js +141 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +28 -0
- package/dist/mcp/tools.js +299 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/types.d.ts +107 -0
- package/dist/mcp/types.js +52 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/merchant/decision-backend.d.ts +82 -0
- package/dist/merchant/decision-backend.js +275 -0
- package/dist/merchant/decision-backend.js.map +1 -0
- package/dist/merchant/ops.d.ts +80 -0
- package/dist/merchant/ops.js +75 -0
- package/dist/merchant/ops.js.map +1 -0
- package/dist/merchant/ucp-profile.d.ts +39 -0
- package/dist/merchant/ucp-profile.js +55 -0
- package/dist/merchant/ucp-profile.js.map +1 -0
- package/dist/product-cli.d.ts +1 -1
- package/dist/product-cli.js +1 -1
- package/dist/product-cli.js.map +1 -1
- package/dist/trust/identity/auth-verifier.js +19 -1
- package/dist/trust/identity/auth-verifier.js.map +1 -1
- package/dist/trust/identity/message-signature.js +14 -8
- package/dist/trust/identity/message-signature.js.map +1 -1
- package/examples/profiles/merchant.deepseek.yaml +5 -0
- package/examples/profiles/merchant.fake.yaml +4 -0
- package/package.json +4 -2
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
import { type NegotiationDecision } from "../negotiation/types.js";
|
|
17
|
+
import type { MerchantPolicy } from "../config/profile.js";
|
|
18
|
+
/** merchant role bounds:只产 propose / counter(决策 action 枚举的子集)。 */
|
|
19
|
+
export type MerchantDecisionAction = "propose" | "counter";
|
|
20
|
+
export interface MerchantDecisionSuggestionInput {
|
|
21
|
+
action: MerchantDecisionAction;
|
|
22
|
+
sku: string;
|
|
23
|
+
quantity: number;
|
|
24
|
+
/** list 价(resolveProduct 输出的 minor units)+ 真实商品事实(供 prompt)。 */
|
|
25
|
+
product: {
|
|
26
|
+
priceMinor: number;
|
|
27
|
+
currency: string;
|
|
28
|
+
stock?: number;
|
|
29
|
+
title?: string;
|
|
30
|
+
};
|
|
31
|
+
/** merchant policy(floor / max discount 等)。safe subset 进 prompt,floor 不进。 */
|
|
32
|
+
policy?: MerchantPolicy;
|
|
33
|
+
conversationId: string;
|
|
34
|
+
/** 决策 schema 要求整数 ≥1;KNP 用字符串消息 id,此处传合成值(harness 约定)。 */
|
|
35
|
+
inReplyToMessageId?: number;
|
|
36
|
+
snapshot?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/** 受限推理后端:返回不可信、schema 合法候选;任何失败 → null。永不写。 */
|
|
39
|
+
export interface MerchantDecisionBackend {
|
|
40
|
+
suggest(input: MerchantDecisionSuggestionInput): Promise<NegotiationDecision | null>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 从模型输出提取 JSON 对象(容忍 ```json 围栏 / 前后缀文本)。复制自 harness。
|
|
44
|
+
*/
|
|
45
|
+
export declare function extractJson(text: string): unknown;
|
|
46
|
+
/**
|
|
47
|
+
* 白名单消毒 + 冻结契约校验。未知键先剥(模型可能加 thinking/注释);再
|
|
48
|
+
* validateAgainst("decision")(复用生产校验器,src/contracts/schemas.ts)。
|
|
49
|
+
* merchant role bound:action ∈ {propose, counter} 且 proposal 存在才接受。
|
|
50
|
+
*/
|
|
51
|
+
export declare function sanitizeDecision(raw: unknown): NegotiationDecision | null;
|
|
52
|
+
export interface DeepSeekDecisionBackendOptions {
|
|
53
|
+
/** resolver 惰性读 env(resolveSecret),请求时才解析。 */
|
|
54
|
+
apiKey: string | (() => string);
|
|
55
|
+
model?: string;
|
|
56
|
+
baseUrl?: string;
|
|
57
|
+
timeoutMs?: number;
|
|
58
|
+
temperature?: number;
|
|
59
|
+
fetchImpl?: typeof fetch;
|
|
60
|
+
}
|
|
61
|
+
export declare class DeepSeekDecisionBackend implements MerchantDecisionBackend {
|
|
62
|
+
private readonly opts;
|
|
63
|
+
constructor(opts: DeepSeekDecisionBackendOptions);
|
|
64
|
+
suggest(input: MerchantDecisionSuggestionInput): Promise<NegotiationDecision | null>;
|
|
65
|
+
}
|
|
66
|
+
export declare class MockDecisionBackend implements MerchantDecisionBackend {
|
|
67
|
+
suggest(input: MerchantDecisionSuggestionInput): Promise<NegotiationDecision | null>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 候选 → {minor 单价, 可选公开说明} | null。backend 不可信:任何失败/缺失/
|
|
71
|
+
* 有损转换 → null(调用方回落确定性基线)。**backed 从不写。**
|
|
72
|
+
*/
|
|
73
|
+
export declare function consultDecisionBackend(backend: MerchantDecisionBackend | undefined, input: MerchantDecisionSuggestionInput): Promise<{
|
|
74
|
+
unitPriceMinor: number;
|
|
75
|
+
note?: string;
|
|
76
|
+
} | null>;
|
|
77
|
+
/**
|
|
78
|
+
* 从外发公开消息中剔除私有 floor token(镜像 negotiation-chat.redactPrivateFloor
|
|
79
|
+
* 的纯函数语义;复制避免 a2a→agent 跨树 import)。floor 是私有值,绝不出现在
|
|
80
|
+
* 商家对外公开回复里。
|
|
81
|
+
*/
|
|
82
|
+
export declare function redactPublicMessage(text: string, floor: number | undefined): string;
|
|
@@ -0,0 +1,275 @@
|
|
|
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
|
+
* MerchantDecisionBackend —— merchant 运行时的受限推理后端(DeepSeek Harness 运行时
|
|
18
|
+
* 插件,战略 v2.5 §6.9 / §十一 Phase 2)。
|
|
19
|
+
*
|
|
20
|
+
* 设计边界(§3.3 / §6.9):
|
|
21
|
+
* - backend 只产出 **不可信** DecisionCandidate(`NegotiationDecision`),**不持有
|
|
22
|
+
* Commerce token、不拥有状态机、无任何最终写入权**(0 写 by construction——
|
|
23
|
+
* DeepSeek 后端只 fetch 模型端点,从不接触 commerce 端点)。
|
|
24
|
+
* - 调用方(createMerchantHandler)负责验证(冻结 decision 契约)+ 硬边界约束
|
|
25
|
+
* (floor / max auto discount / list 封顶)后应用;任何失败 → 回落确定性基线。
|
|
26
|
+
* - 独立运行:merchant 是独立进程,不依赖宿主(Standalone-first §7.4)。
|
|
27
|
+
*
|
|
28
|
+
* 与 integrations/harnesses/deepseek-harness/validate-contract-cases.mjs 共享同一
|
|
29
|
+
* schema 驱动 prompt 与 extractJson —— 两处 prompt 须保持同步(本文件是运行时消费
|
|
30
|
+
* 方,harness 是只读 contract-gate 验证面)。
|
|
31
|
+
*/
|
|
32
|
+
import { validateAgainst } from "../contracts/schemas.js";
|
|
33
|
+
import { PROTOCOL_VERSION } from "../negotiation/types.js";
|
|
34
|
+
import { toMinorUnits as losslessToMinorUnits } from "../protocol/legacy-shopping-negotiation/money.js";
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// JSON 提取 + 白名单消毒(schema additionalProperties:false 需先剥未知键)
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
function isObject(value) {
|
|
39
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40
|
+
}
|
|
41
|
+
function pick(obj, keys) {
|
|
42
|
+
const out = {};
|
|
43
|
+
for (const key of keys) {
|
|
44
|
+
if (obj[key] !== undefined)
|
|
45
|
+
out[key] = obj[key];
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
const DECISION_KEYS = [
|
|
50
|
+
"protocol_version",
|
|
51
|
+
"conversation_id",
|
|
52
|
+
"in_reply_to_message_id",
|
|
53
|
+
"action",
|
|
54
|
+
"proposal",
|
|
55
|
+
"open_issues",
|
|
56
|
+
"public_message",
|
|
57
|
+
"confidence",
|
|
58
|
+
"reason_codes",
|
|
59
|
+
"request_human_review",
|
|
60
|
+
];
|
|
61
|
+
const PROPOSAL_KEYS = [
|
|
62
|
+
"sku",
|
|
63
|
+
"quantity",
|
|
64
|
+
"unit_price",
|
|
65
|
+
"currency",
|
|
66
|
+
"stock",
|
|
67
|
+
"delivery",
|
|
68
|
+
"after_sales_policy_refs",
|
|
69
|
+
"valid_until",
|
|
70
|
+
];
|
|
71
|
+
const STOCK_KEYS = ["status", "quantity", "observed_at", "reserved"];
|
|
72
|
+
const DELIVERY_KEYS = ["eta_start", "eta_end", "fee"];
|
|
73
|
+
/**
|
|
74
|
+
* 从模型输出提取 JSON 对象(容忍 ```json 围栏 / 前后缀文本)。复制自 harness。
|
|
75
|
+
*/
|
|
76
|
+
export function extractJson(text) {
|
|
77
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
78
|
+
const candidate = fenced !== null && fenced[1] !== undefined ? fenced[1] : text;
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(candidate);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
const start = candidate.indexOf("{");
|
|
84
|
+
const end = candidate.lastIndexOf("}");
|
|
85
|
+
if (start >= 0 && end > start) {
|
|
86
|
+
try {
|
|
87
|
+
return JSON.parse(candidate.slice(start, end + 1));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 白名单消毒 + 冻结契约校验。未知键先剥(模型可能加 thinking/注释);再
|
|
98
|
+
* validateAgainst("decision")(复用生产校验器,src/contracts/schemas.ts)。
|
|
99
|
+
* merchant role bound:action ∈ {propose, counter} 且 proposal 存在才接受。
|
|
100
|
+
*/
|
|
101
|
+
export function sanitizeDecision(raw) {
|
|
102
|
+
if (!isObject(raw))
|
|
103
|
+
return null;
|
|
104
|
+
const decision = pick(raw, DECISION_KEYS);
|
|
105
|
+
if (isObject(decision.proposal)) {
|
|
106
|
+
const proposal = pick(decision.proposal, PROPOSAL_KEYS);
|
|
107
|
+
if (isObject(proposal.stock))
|
|
108
|
+
proposal.stock = pick(proposal.stock, STOCK_KEYS);
|
|
109
|
+
if (isObject(proposal.delivery))
|
|
110
|
+
proposal.delivery = pick(proposal.delivery, DELIVERY_KEYS);
|
|
111
|
+
decision.proposal = proposal;
|
|
112
|
+
}
|
|
113
|
+
const errors = validateAgainst("decision", decision);
|
|
114
|
+
if (errors.length > 0)
|
|
115
|
+
return null;
|
|
116
|
+
const candidate = decision;
|
|
117
|
+
if ((candidate.action !== "propose" && candidate.action !== "counter") || candidate.proposal === undefined) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return candidate;
|
|
121
|
+
}
|
|
122
|
+
const DEFAULT_MODEL = "deepseek-v4-flash";
|
|
123
|
+
const DEFAULT_BASE_URL = "https://api.deepseek.com";
|
|
124
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
125
|
+
const DEFAULT_TEMPERATURE = 0.2;
|
|
126
|
+
/** 构建 schema 驱动 prompt 模板(镜像 harness realCandidate;不含 floor)。 */
|
|
127
|
+
function decisionTemplate(input) {
|
|
128
|
+
const nowIso = new Date().toISOString();
|
|
129
|
+
const stock = input.product.stock ?? 180;
|
|
130
|
+
return {
|
|
131
|
+
protocol_version: PROTOCOL_VERSION,
|
|
132
|
+
conversation_id: input.conversationId,
|
|
133
|
+
in_reply_to_message_id: input.inReplyToMessageId ?? 1,
|
|
134
|
+
action: input.action,
|
|
135
|
+
open_issues: [],
|
|
136
|
+
public_message: "按报价提供。",
|
|
137
|
+
reason_codes: ["within_policy"],
|
|
138
|
+
request_human_review: false,
|
|
139
|
+
confidence: 0.8,
|
|
140
|
+
proposal: {
|
|
141
|
+
sku: input.sku,
|
|
142
|
+
quantity: input.quantity,
|
|
143
|
+
unit_price: input.product.priceMinor / 100, // major 元
|
|
144
|
+
currency: input.product.currency,
|
|
145
|
+
stock: { status: stock > 0 ? "available" : "out_of_stock", quantity: stock, observed_at: nowIso, reserved: false },
|
|
146
|
+
delivery: { eta_start: nowIso, eta_end: nowIso, fee: 8 },
|
|
147
|
+
after_sales_policy_refs: [],
|
|
148
|
+
valid_until: "2099-12-31T23:59:59Z",
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
export class DeepSeekDecisionBackend {
|
|
153
|
+
opts;
|
|
154
|
+
constructor(opts) {
|
|
155
|
+
this.opts = opts;
|
|
156
|
+
}
|
|
157
|
+
async suggest(input) {
|
|
158
|
+
let apiKey;
|
|
159
|
+
try {
|
|
160
|
+
apiKey = typeof this.opts.apiKey === "function" ? this.opts.apiKey() : this.opts.apiKey;
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return null; // resolveSecret 失败 → fail-safe 回落
|
|
164
|
+
}
|
|
165
|
+
if (apiKey === "")
|
|
166
|
+
return null;
|
|
167
|
+
const template = decisionTemplate(input);
|
|
168
|
+
const system = "你是商家磋商决策助手。只输出一个 JSON 对象,严格符合给定结构,不要任何额外文字、markdown 或注释。";
|
|
169
|
+
const user = `action=${input.action} sku=${input.sku} quantity=${input.quantity} 商品=${JSON.stringify(input.product)}。` +
|
|
170
|
+
`输出 action="${input.action}" 的 decision,结构如下(字段名与嵌套完全一致,不得增加字段):\n` +
|
|
171
|
+
JSON.stringify(template, null, 2);
|
|
172
|
+
const controller = new AbortController();
|
|
173
|
+
const timer = setTimeout(() => controller.abort(), this.opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
174
|
+
try {
|
|
175
|
+
const resp = await (this.opts.fetchImpl ?? globalThis.fetch)(`${this.opts.baseUrl ?? DEFAULT_BASE_URL}/v1/chat/completions`, {
|
|
176
|
+
method: "POST",
|
|
177
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${apiKey}` },
|
|
178
|
+
body: JSON.stringify({
|
|
179
|
+
model: this.opts.model ?? DEFAULT_MODEL,
|
|
180
|
+
messages: [
|
|
181
|
+
{ role: "system", content: system },
|
|
182
|
+
{ role: "user", content: user },
|
|
183
|
+
],
|
|
184
|
+
thinking: { type: "disabled" }, // harness C1 规则:非推理任务
|
|
185
|
+
response_format: { type: "json_object" },
|
|
186
|
+
temperature: this.opts.temperature ?? DEFAULT_TEMPERATURE,
|
|
187
|
+
}),
|
|
188
|
+
signal: controller.signal,
|
|
189
|
+
});
|
|
190
|
+
if (!resp.ok)
|
|
191
|
+
return null;
|
|
192
|
+
const data = (await resp.json());
|
|
193
|
+
return sanitizeDecision(extractJson(data.choices?.[0]?.message?.content ?? ""));
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return null; // 网络/超时/解析失败 → fail-safe
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
clearTimeout(timer); // 测试无悬挂 timer
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Mock 后端(确定性,镜像 harness mockCandidate;测试 + 离线冒烟)
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
export class MockDecisionBackend {
|
|
207
|
+
async suggest(input) {
|
|
208
|
+
const qty = input.product.stock ?? 180;
|
|
209
|
+
const nowIso = new Date().toISOString();
|
|
210
|
+
return {
|
|
211
|
+
protocol_version: PROTOCOL_VERSION,
|
|
212
|
+
conversation_id: input.conversationId,
|
|
213
|
+
in_reply_to_message_id: input.inReplyToMessageId ?? 1,
|
|
214
|
+
action: input.action,
|
|
215
|
+
open_issues: [],
|
|
216
|
+
public_message: "按报价提供。",
|
|
217
|
+
reason_codes: ["within_policy"],
|
|
218
|
+
request_human_review: false,
|
|
219
|
+
confidence: 0.8,
|
|
220
|
+
proposal: {
|
|
221
|
+
sku: input.sku,
|
|
222
|
+
quantity: input.quantity,
|
|
223
|
+
unit_price: input.product.priceMinor / 100, // major 元
|
|
224
|
+
currency: input.product.currency,
|
|
225
|
+
stock: { status: qty > 0 ? "available" : "out_of_stock", quantity: qty, observed_at: nowIso, reserved: false },
|
|
226
|
+
delivery: { eta_start: nowIso, eta_end: nowIso, fee: 8 },
|
|
227
|
+
after_sales_policy_refs: [],
|
|
228
|
+
valid_until: "2099-12-31T23:59:59Z",
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
// 桥接助手(handler 用)
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
/**
|
|
237
|
+
* 候选 → {minor 单价, 可选公开说明} | null。backend 不可信:任何失败/缺失/
|
|
238
|
+
* 有损转换 → null(调用方回落确定性基线)。**backed 从不写。**
|
|
239
|
+
*/
|
|
240
|
+
export async function consultDecisionBackend(backend, input) {
|
|
241
|
+
if (backend === undefined)
|
|
242
|
+
return null;
|
|
243
|
+
let decision = null;
|
|
244
|
+
try {
|
|
245
|
+
decision = await backend.suggest(input);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
if (decision === null)
|
|
251
|
+
return null;
|
|
252
|
+
const proposal = decision.proposal;
|
|
253
|
+
if (proposal === undefined || typeof proposal.unit_price !== "number")
|
|
254
|
+
return null;
|
|
255
|
+
const converted = losslessToMinorUnits(proposal.unit_price, 2);
|
|
256
|
+
if (!converted.lossless)
|
|
257
|
+
return null; // money.ts:有损 → fail-closed
|
|
258
|
+
return {
|
|
259
|
+
unitPriceMinor: converted.amount_minor,
|
|
260
|
+
...(decision.public_message !== undefined && decision.public_message !== ""
|
|
261
|
+
? { note: decision.public_message }
|
|
262
|
+
: {}),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* 从外发公开消息中剔除私有 floor token(镜像 negotiation-chat.redactPrivateFloor
|
|
267
|
+
* 的纯函数语义;复制避免 a2a→agent 跨树 import)。floor 是私有值,绝不出现在
|
|
268
|
+
* 商家对外公开回复里。
|
|
269
|
+
*/
|
|
270
|
+
export function redactPublicMessage(text, floor) {
|
|
271
|
+
if (floor === undefined || floor <= 0)
|
|
272
|
+
return text;
|
|
273
|
+
return text.split(String(floor)).join("[private-floor]");
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=decision-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-backend.js","sourceRoot":"","sources":["../../src/merchant/decision-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAA4B,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,YAAY,IAAI,oBAAoB,EAAE,MAAM,kDAAkD,CAAC;AA8BxG,8EAA8E;AAC9E,4DAA4D;AAC5D,8EAA8E;AAE9E,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,IAAI,CAAC,GAA4B,EAAE,IAAuB;IACjE,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,aAAa,GAAG;IACpB,kBAAkB;IAClB,iBAAiB;IACjB,wBAAwB;IACxB,QAAQ;IACR,UAAU;IACV,aAAa;IACb,gBAAgB;IAChB,YAAY;IACZ,cAAc;IACd,sBAAsB;CACd,CAAC;AACX,MAAM,aAAa,GAAG;IACpB,KAAK;IACL,UAAU;IACV,YAAY;IACZ,UAAU;IACV,OAAO;IACP,UAAU;IACV,yBAAyB;IACzB,aAAa;CACL,CAAC;AACX,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,CAAU,CAAC;AAC9E,MAAM,aAAa,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,CAAU,CAAC;AAE/D;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC1D,MAAM,SAAS,GAAG,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChF,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAY,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,KAAK,IAAI,CAAC,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAY,CAAC;YAChE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC1C,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QACxD,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAChF,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC5F,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC/B,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,SAAS,GAAG,QAA0C,CAAC;IAC7D,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,IAAI,SAAS,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3G,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAgBD,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAC1C,MAAM,gBAAgB,GAAG,0BAA0B,CAAC;AACpD,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,iEAAiE;AACjE,SAAS,gBAAgB,CAAC,KAAsC;IAC9D,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC;IACzC,OAAO;QACL,gBAAgB,EAAE,gBAAgB;QAClC,eAAe,EAAE,KAAK,CAAC,cAAc;QACrC,sBAAsB,EAAE,KAAK,CAAC,kBAAkB,IAAI,CAAC;QACrD,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,WAAW,EAAE,EAAE;QACf,cAAc,EAAE,QAAQ;QACxB,YAAY,EAAE,CAAC,eAAe,CAAC;QAC/B,oBAAoB,EAAE,KAAK;QAC3B,UAAU,EAAE,GAAG;QACf,QAAQ,EAAE;YACR,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,GAAG,EAAE,UAAU;YACtD,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ;YAChC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;YAClH,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE;YACxD,uBAAuB,EAAE,EAAE;YAC3B,WAAW,EAAE,sBAAsB;SACpC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,uBAAuB;IACjB,IAAI,CAAiC;IAEtD,YAAY,IAAoC;QAC9C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAsC;QAClD,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QAC1F,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,CAAC,kCAAkC;QACjD,CAAC;QACD,IAAI,MAAM,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QAE/B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,0DAA0D,CAAC;QAC1E,MAAM,IAAI,GACR,UAAU,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,GAAG,aAAa,KAAK,CAAC,QAAQ,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG;YACzG,cAAc,KAAK,CAAC,MAAM,yCAAyC;YACnE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAEpC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC,CAAC;QAC9F,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC,CAC1D,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,gBAAgB,sBAAsB,EAC9D;gBACE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;gBAClF,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,aAAa;oBACvC,QAAQ,EAAE;wBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE;wBACnC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;qBAChC;oBACD,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,sBAAsB;oBACtD,eAAe,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;oBACxC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,mBAAmB;iBAC1D,CAAC;gBACF,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CACF,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC;YAC1B,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAA4D,CAAC;YAC5F,OAAO,gBAAgB,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;QAClF,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,CAAC,yBAAyB;QACxC,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc;QACrC,CAAC;IACH,CAAC;CACF;AAED,8EAA8E;AAC9E,kDAAkD;AAClD,8EAA8E;AAE9E,MAAM,OAAO,mBAAmB;IAC9B,KAAK,CAAC,OAAO,CAAC,KAAsC;QAClD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACxC,OAAO;YACL,gBAAgB,EAAE,gBAAgB;YAClC,eAAe,EAAE,KAAK,CAAC,cAAc;YACrC,sBAAsB,EAAE,KAAK,CAAC,kBAAkB,IAAI,CAAC;YACrD,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,WAAW,EAAE,EAAE;YACf,cAAc,EAAE,QAAQ;YACxB,YAAY,EAAE,CAAC,eAAe,CAAC;YAC/B,oBAAoB,EAAE,KAAK;YAC3B,UAAU,EAAE,GAAG;YACf,QAAQ,EAAE;gBACR,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,GAAG,EAAE,UAAU;gBACtD,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ;gBAChC,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;gBAC9G,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE;gBACxD,uBAAuB,EAAE,EAAE;gBAC3B,WAAW,EAAE,sBAAsB;aACpC;SACF,CAAC;IACJ,CAAC;CACF;AAED,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA4C,EAC5C,KAAsC;IAEtC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,QAAQ,GAA+B,IAAI,CAAC;IAChD,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;IACnC,IAAI,QAAQ,KAAK,SAAS,IAAI,OAAO,QAAQ,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnF,MAAM,SAAS,GAAG,oBAAoB,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAC/D,IAAI,CAAC,SAAS,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC,CAAC,4BAA4B;IAClE,OAAO;QACL,cAAc,EAAE,SAAS,CAAC,YAAY;QACtC,GAAG,CAAC,QAAQ,CAAC,cAAc,KAAK,SAAS,IAAI,QAAQ,CAAC,cAAc,KAAK,EAAE;YACzE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,cAAc,EAAE;YACnC,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,KAAyB;IACzE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
* Merchant Ops API(战略 v2.5 §7.6 / §7.7)。
|
|
18
|
+
*
|
|
19
|
+
* Host/Hermes 作为 Operator Console 查看 RFQ 队列、处理 human_required、批准
|
|
20
|
+
* 异常报价、查看 Agreement/Analytics。Merchant Core 仍是状态权威;Ops 只投影
|
|
21
|
+
* 状态与接收审批输入(§7.5 Intelligence & Ops Plane)。
|
|
22
|
+
*
|
|
23
|
+
* 命名空间隔离(§7.7):本服务用 **merchant token** 认证(`kiwi.merchant.*`);
|
|
24
|
+
* 任何 Buyer token 不得访问 Merchant Ops,任何 Merchant Ops token 不得获得
|
|
25
|
+
* Buyer Principal Memory。与 `kiwi.buyer.*`(Buyer Kit)完全分离。
|
|
26
|
+
*/
|
|
27
|
+
export interface MerchantOpsServiceOptions {
|
|
28
|
+
baseUrl: string;
|
|
29
|
+
/** 商家 API token(merchant 作用域,非 buyer token)。 */
|
|
30
|
+
merchantToken: string;
|
|
31
|
+
timeoutMs?: number;
|
|
32
|
+
fetchImpl?: typeof fetch;
|
|
33
|
+
}
|
|
34
|
+
export interface RfqConversation {
|
|
35
|
+
id: string;
|
|
36
|
+
buyer_id: string;
|
|
37
|
+
sku: string;
|
|
38
|
+
status: string;
|
|
39
|
+
next_actor: string;
|
|
40
|
+
created_at: string;
|
|
41
|
+
updated_at: string;
|
|
42
|
+
last_sender: string;
|
|
43
|
+
message_count?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface HumanReviewConversation {
|
|
46
|
+
id: string;
|
|
47
|
+
buyer_id: string;
|
|
48
|
+
status: string;
|
|
49
|
+
reason?: string;
|
|
50
|
+
created_at?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface MerchantAnalytics {
|
|
53
|
+
merchant_id: string;
|
|
54
|
+
total_rfqs: number;
|
|
55
|
+
pending_rfqs: number;
|
|
56
|
+
completed_rfqs: number;
|
|
57
|
+
human_review_count: number;
|
|
58
|
+
}
|
|
59
|
+
export declare class MerchantOpsService {
|
|
60
|
+
private readonly baseUrl;
|
|
61
|
+
private readonly merchantToken;
|
|
62
|
+
private readonly timeoutMs;
|
|
63
|
+
private readonly fetchImpl;
|
|
64
|
+
constructor(options: MerchantOpsServiceOptions);
|
|
65
|
+
/** RFQ 队列:商家名下活跃会话(§7.6 查看 RFQ)。 */
|
|
66
|
+
listRfqQueue(merchantId: string): Promise<{
|
|
67
|
+
rfqs: RfqConversation[];
|
|
68
|
+
}>;
|
|
69
|
+
/** human_required 会话(§7.6 处理人工审核)。 */
|
|
70
|
+
listHumanReview(merchantId: string): Promise<{
|
|
71
|
+
conversations: HumanReviewConversation[];
|
|
72
|
+
}>;
|
|
73
|
+
/** 处理 human_required:resolve 会话(商家运营决定)。 */
|
|
74
|
+
resolveReview(merchantId: string, conversationId: string, decision: string): Promise<{
|
|
75
|
+
ok: boolean;
|
|
76
|
+
}>;
|
|
77
|
+
/** Analytics:RFQ 量、待办/完成、人工审核数(§7.6)。 */
|
|
78
|
+
analytics(merchantId: string): Promise<MerchantAnalytics>;
|
|
79
|
+
private getJson;
|
|
80
|
+
}
|
|
@@ -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
|
+
export class MerchantOpsService {
|
|
17
|
+
baseUrl;
|
|
18
|
+
merchantToken;
|
|
19
|
+
timeoutMs;
|
|
20
|
+
fetchImpl;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
23
|
+
this.merchantToken = options.merchantToken;
|
|
24
|
+
this.timeoutMs = options.timeoutMs ?? 5000;
|
|
25
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
26
|
+
}
|
|
27
|
+
/** RFQ 队列:商家名下活跃会话(§7.6 查看 RFQ)。 */
|
|
28
|
+
async listRfqQueue(merchantId) {
|
|
29
|
+
const data = await this.getJson(`/merchants/${encodeURIComponent(merchantId)}/conversations`);
|
|
30
|
+
const rfqs = Array.isArray(data)
|
|
31
|
+
? data
|
|
32
|
+
: ((data.conversations ?? data.results) ?? []);
|
|
33
|
+
return { rfqs };
|
|
34
|
+
}
|
|
35
|
+
/** human_required 会话(§7.6 处理人工审核)。 */
|
|
36
|
+
async listHumanReview(merchantId) {
|
|
37
|
+
const data = await this.getJson(`/merchants/${encodeURIComponent(merchantId)}/human-review`);
|
|
38
|
+
return { conversations: data.conversations ?? [] };
|
|
39
|
+
}
|
|
40
|
+
/** 处理 human_required:resolve 会话(商家运营决定)。 */
|
|
41
|
+
async resolveReview(merchantId, conversationId, decision) {
|
|
42
|
+
const res = await this.fetchImpl(`${this.baseUrl}/conversations/${encodeURIComponent(conversationId)}/human-review/resolve`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: { authorization: `Bearer ${this.merchantToken}`, "content-type": "application/json" },
|
|
45
|
+
body: JSON.stringify({ decision, merchant_id: merchantId }),
|
|
46
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
47
|
+
});
|
|
48
|
+
if (!res.ok)
|
|
49
|
+
throw new Error(`resolve review ${res.status}`);
|
|
50
|
+
const data = (await res.json());
|
|
51
|
+
return { ok: data.ok === true };
|
|
52
|
+
}
|
|
53
|
+
/** Analytics:RFQ 量、待办/完成、人工审核数(§7.6)。 */
|
|
54
|
+
async analytics(merchantId) {
|
|
55
|
+
const { rfqs } = await this.listRfqQueue(merchantId);
|
|
56
|
+
const { conversations } = await this.listHumanReview(merchantId);
|
|
57
|
+
return {
|
|
58
|
+
merchant_id: merchantId,
|
|
59
|
+
total_rfqs: rfqs.length,
|
|
60
|
+
pending_rfqs: rfqs.filter((r) => r.status === "waiting_merchant").length,
|
|
61
|
+
completed_rfqs: rfqs.filter((r) => r.status === "closed").length,
|
|
62
|
+
human_review_count: conversations.length,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async getJson(path) {
|
|
66
|
+
const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
67
|
+
headers: { authorization: `Bearer ${this.merchantToken}`, accept: "application/json" },
|
|
68
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
69
|
+
});
|
|
70
|
+
if (!res.ok)
|
|
71
|
+
throw new Error(`merchant ops ${path} ${res.status}`);
|
|
72
|
+
return (await res.json());
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ops.js","sourceRoot":"","sources":["../../src/merchant/ops.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
* Merchant UCP Profile 生成(战略 v2.5 §7.1 Protocol Publishing / §7.8 UCP-ready)。
|
|
18
|
+
*
|
|
19
|
+
* 商家在 `/.well-known/ucp` 发布 UCP Profile:声明 Catalog service(指向真实
|
|
20
|
+
* 商品 endpoint)与 KNP 磋商 capability。Kiwi 的 Buyer 端(UcpProfileResolver)
|
|
21
|
+
* 通过该 profile 发现商家能力;商品 truth 仍在 merchant/marketplace,不落 Kiwi。
|
|
22
|
+
*
|
|
23
|
+
* UCP 命名不变量(基线 §8.3):service 名 = {reverse-domain}.{service},
|
|
24
|
+
* reverse-domain 反转即 namespace authority host;spec/schema URL origin MUST
|
|
25
|
+
* 与 namespace authority 一致。
|
|
26
|
+
*/
|
|
27
|
+
import type { UcpProfile } from "../discovery/ucp/types.js";
|
|
28
|
+
export declare const UCP_VERSION = "2026-04-08";
|
|
29
|
+
export interface MerchantUcpProfileOptions {
|
|
30
|
+
/** 商家规范域名(如 xihu-digital.example.com)——决定 namespace authority。 */
|
|
31
|
+
domain: string;
|
|
32
|
+
/** 商家 id(marketplace 商家 id,如 merchant-hz-xihu)。 */
|
|
33
|
+
merchantId: string;
|
|
34
|
+
/** 真实商品 catalog endpoint(marketplace search,如 http://127.0.0.1:8765/search/products)。 */
|
|
35
|
+
catalogEndpoint: string;
|
|
36
|
+
}
|
|
37
|
+
/** 由域名派生 reverse-domain(UCP §8.3:反转 authority host 的 label)。 */
|
|
38
|
+
export declare function reverseDomain(domain: string): string;
|
|
39
|
+
export declare function buildMerchantUcpProfile(options: MerchantUcpProfileOptions): UcpProfile;
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
export const UCP_VERSION = "2026-04-08";
|
|
17
|
+
/** 由域名派生 reverse-domain(UCP §8.3:反转 authority host 的 label)。 */
|
|
18
|
+
export function reverseDomain(domain) {
|
|
19
|
+
return domain.split(".").reverse().join(".");
|
|
20
|
+
}
|
|
21
|
+
export function buildMerchantUcpProfile(options) {
|
|
22
|
+
const authority = options.domain;
|
|
23
|
+
const rd = reverseDomain(options.domain);
|
|
24
|
+
const catalogService = `${rd}.catalog`;
|
|
25
|
+
const negotiationCapability = "com.harrylabsj.kiwi.shopping.negotiation";
|
|
26
|
+
return {
|
|
27
|
+
ucp: {
|
|
28
|
+
version: UCP_VERSION,
|
|
29
|
+
services: {
|
|
30
|
+
[catalogService]: [
|
|
31
|
+
{
|
|
32
|
+
version: UCP_VERSION,
|
|
33
|
+
spec: `https://${authority}/.well-known/ucp#catalog`,
|
|
34
|
+
transport: "rest",
|
|
35
|
+
endpoint: options.catalogEndpoint,
|
|
36
|
+
schema: `https://${authority}/schemas/catalog.json`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
capabilities: {
|
|
41
|
+
[negotiationCapability]: [
|
|
42
|
+
{
|
|
43
|
+
version: "1.0",
|
|
44
|
+
spec: "https://kiwi.harrylabsj.com/spec/negotiation/1.0",
|
|
45
|
+
schema: "https://kiwi.harrylabsj.com/schemas/negotiation/1.0/schema.json",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
signing_keys: [],
|
|
51
|
+
// 商家元信息(非 UCP 标准字段,forward-compat)。
|
|
52
|
+
merchant_id: options.merchantId,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=ucp-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ucp-profile.js","sourceRoot":"","sources":["../../src/merchant/ucp-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AAWxC,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAkC;IACxE,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IACjC,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,cAAc,GAAG,GAAG,EAAE,UAAU,CAAC;IACvC,MAAM,qBAAqB,GAAG,0CAA0C,CAAC;IAEzE,OAAO;QACL,GAAG,EAAE;YACH,OAAO,EAAE,WAAW;YACpB,QAAQ,EAAE;gBACR,CAAC,cAAc,CAAC,EAAE;oBAChB;wBACE,OAAO,EAAE,WAAW;wBACpB,IAAI,EAAE,WAAW,SAAS,0BAA0B;wBACpD,SAAS,EAAE,MAAM;wBACjB,QAAQ,EAAE,OAAO,CAAC,eAAe;wBACjC,MAAM,EAAE,WAAW,SAAS,uBAAuB;qBACpD;iBACF;aACF;YACD,YAAY,EAAE;gBACZ,CAAC,qBAAqB,CAAC,EAAE;oBACvB;wBACE,OAAO,EAAE,KAAK;wBACd,IAAI,EAAE,kDAAkD;wBACxD,MAAM,EAAE,iEAAiE;qBAC1E;iBACF;aACF;SACF;QACD,YAAY,EAAE,EAAE;QAChB,oCAAoC;QACpC,WAAW,EAAE,OAAO,CAAC,UAAU;KAChC,CAAC;AACJ,CAAC"}
|
package/dist/product-cli.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare const DEFAULT_PROFILE_PATH: string;
|
|
|
42
42
|
*/
|
|
43
43
|
export declare function writeDefaultProfile(yaml: string, profilePath?: string): void;
|
|
44
44
|
/** 与 package.json / USAGE 同步的产品版本。 */
|
|
45
|
-
export declare const PRODUCT_VERSION = "0.7.
|
|
45
|
+
export declare const PRODUCT_VERSION = "0.7.11";
|
|
46
46
|
/**
|
|
47
47
|
* 缺省 kiwi-catalog 地址(单一来源,cli.ts 各处解析一致)。
|
|
48
48
|
*
|
package/dist/product-cli.js
CHANGED
|
@@ -51,7 +51,7 @@ export function writeDefaultProfile(yaml, profilePath) {
|
|
|
51
51
|
writeFileSync(target, yaml, { mode: 0o600 });
|
|
52
52
|
}
|
|
53
53
|
/** 与 package.json / USAGE 同步的产品版本。 */
|
|
54
|
-
export const PRODUCT_VERSION = "0.7.
|
|
54
|
+
export const PRODUCT_VERSION = "0.7.11";
|
|
55
55
|
/**
|
|
56
56
|
* 缺省 kiwi-catalog 地址(单一来源,cli.ts 各处解析一致)。
|
|
57
57
|
*
|