@itpay/cli 2.0.11 → 2.0.12
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/README.md +1 -1
- package/dist/src/commands/guidance.js +14 -1
- package/dist/src/commands/services.js +30 -9
- package/dist/src/main.js +14 -2
- package/dist/src/state/config.js +3 -3
- package/docs/agent/buyer/install-and-setup.json +1 -1
- package/docs/cli-reference/commands/install.md +1 -1
- package/docs/cli-reference/commands/services/invoke.md +19 -0
- package/docs/cli-reference/commands/services/next.md +28 -1
- package/package.json +1 -1
- package/skills/itpay-buyer/SKILL.md +3 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ itpay --agent-type codex-desktop readyz --json
|
|
|
11
11
|
# follow next.command: typed skill show, then catalog list
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
The default API is
|
|
14
|
+
The default API is `https://app.itpay.ai`. Set `ITPAY_BACKEND_URL` only for an intentional environment override.
|
|
15
15
|
|
|
16
16
|
## Output Contract
|
|
17
17
|
|
|
@@ -404,7 +404,7 @@ function buildServiceGuidance(input) {
|
|
|
404
404
|
command: `itpay services checkout ${execution.service_execution_id} --capability ${capabilityID}${emailRequired ? " --email <email>" : ""} --json`,
|
|
405
405
|
requires_human: true,
|
|
406
406
|
reason: emailRequired
|
|
407
|
-
?
|
|
407
|
+
? deliveryEmailGuidance(checkoutCapability?.delivery_email_purpose)
|
|
408
408
|
: "This capability returns an agent-visible result after payment and does not require a delivery email.",
|
|
409
409
|
});
|
|
410
410
|
}
|
|
@@ -487,6 +487,7 @@ function buildServiceGuidance(input) {
|
|
|
487
487
|
requires_payment: capability.requires_payment,
|
|
488
488
|
vault_required: capability.vault_required,
|
|
489
489
|
delivery_email_required: capability.delivery_email_required,
|
|
490
|
+
delivery_email_purpose: capability.delivery_email_purpose,
|
|
490
491
|
price_amount_minor: capability.price_amount_minor,
|
|
491
492
|
price_currency: capability.price_currency,
|
|
492
493
|
free_quota_limit: capability.free_quota_limit,
|
|
@@ -506,6 +507,18 @@ function buildServiceGuidance(input) {
|
|
|
506
507
|
: {}),
|
|
507
508
|
};
|
|
508
509
|
}
|
|
510
|
+
function deliveryEmailGuidance(purpose) {
|
|
511
|
+
switch (purpose) {
|
|
512
|
+
case "receipt":
|
|
513
|
+
return "Ask the human for their email. It is used to send the order receipt; never invent or substitute an address.";
|
|
514
|
+
case "claim":
|
|
515
|
+
return "Ask the human for their email. It is used to send the protected result claim link; never invent or substitute an address.";
|
|
516
|
+
case "receipt_and_claim":
|
|
517
|
+
return "Ask the human for their email. It is used to send the order receipt and protected result claim link; never invent or substitute an address.";
|
|
518
|
+
default:
|
|
519
|
+
return "Ask the human for the required email and state only the Backend-declared purpose; never invent or substitute an address.";
|
|
520
|
+
}
|
|
521
|
+
}
|
|
509
522
|
function firstPrePurchaseCapability(capabilities) {
|
|
510
523
|
return capabilities.find((capability) => capability.agent_visible && !capability.requires_payment)
|
|
511
524
|
?? capabilities.find((capability) => capability.agent_visible);
|
|
@@ -128,7 +128,7 @@ function invokedEnvelope(response, requestedCapability, capabilities, input) {
|
|
|
128
128
|
delivery_email_required: checkoutCapability.delivery_email_required,
|
|
129
129
|
};
|
|
130
130
|
const price = capabilityPrice(checkoutCapability);
|
|
131
|
-
instruction = purchaseConfirmationInstruction("quota_exhausted", price, checkoutCapability.delivery_email_required);
|
|
131
|
+
instruction = purchaseConfirmationInstruction("quota_exhausted", price, checkoutCapability.delivery_email_required, checkoutCapability.delivery_email_purpose);
|
|
132
132
|
next = {
|
|
133
133
|
command: checkoutCommand(response.execution.service_execution_id, checkoutCapability, input),
|
|
134
134
|
reason: `仅在用户明确同意支付 ${price} 后执行;否则停止`,
|
|
@@ -198,17 +198,30 @@ function capabilityPrice(capability) {
|
|
|
198
198
|
? formatMoney(capability.price_amount_minor, capability.price_currency)
|
|
199
199
|
: "当前发布价格";
|
|
200
200
|
}
|
|
201
|
-
function purchaseConfirmationInstruction(context, price, deliveryEmailRequired, candidateTitle = "") {
|
|
201
|
+
function purchaseConfirmationInstruction(context, price, deliveryEmailRequired, deliveryEmailPurpose, candidateTitle = "") {
|
|
202
|
+
const emailPurpose = deliveryEmailPurposeText(deliveryEmailPurpose);
|
|
202
203
|
if (context === "quota_exhausted") {
|
|
203
204
|
return deliveryEmailRequired
|
|
204
|
-
? `免费额度已用完,本次没有调用 Provider,也尚未创建 Quote 或 Checkout。现在只向用户说明:继续当前请求需要支付 ${price}
|
|
205
|
+
? `免费额度已用完,本次没有调用 Provider,也尚未创建 Quote 或 Checkout。现在只向用户说明:继续当前请求需要支付 ${price},并提供${emailPurpose};请确认是否购买并提供邮箱。然后停止并等待。用户明确同意并提供真实邮箱前,不要执行 next.command,不要新建 Execution,不要尝试其他 capability、quote、cart、buy、checkout 或 pay 命令。`
|
|
205
206
|
: `免费额度已用完,本次没有调用 Provider,也尚未创建 Quote 或 Checkout。现在只向用户说明:“继续当前请求需要支付 ${price},是否购买?”然后停止并等待用户明确回复。用户明确同意前,不要执行 next.command,不要新建 Execution,不要尝试其他 capability、quote、cart、buy、checkout 或 pay 命令。`;
|
|
206
207
|
}
|
|
207
208
|
const selected = candidateTitle ? `已选择 ${candidateTitle}。` : "当前候选已经确认。";
|
|
208
209
|
return deliveryEmailRequired
|
|
209
|
-
? `${selected}候选已绑定到当前 Execution,但尚未购买后续服务。现在只向用户说明:继续购买后续服务需要支付 ${price}
|
|
210
|
+
? `${selected}候选已绑定到当前 Execution,但尚未购买后续服务。现在只向用户说明:继续购买后续服务需要支付 ${price},并提供${emailPurpose};请确认是否购买并提供邮箱。然后停止。用户明确同意并提供真实邮箱前,不要执行 next.command,不要创建新 Execution 或 Checkout。`
|
|
210
211
|
: `${selected}候选已绑定到当前 Execution,但尚未购买后续服务。现在只向用户说明:“继续购买后续服务需要支付 ${price},是否购买?”然后停止。用户明确同意前,不要执行 next.command,不要创建新 Execution 或 Checkout。`;
|
|
211
212
|
}
|
|
213
|
+
function deliveryEmailPurposeText(purpose) {
|
|
214
|
+
switch (purpose) {
|
|
215
|
+
case "receipt":
|
|
216
|
+
return "用于发送订单收据的真实邮箱";
|
|
217
|
+
case "claim":
|
|
218
|
+
return "用于发送交付认领链接的真实邮箱";
|
|
219
|
+
case "receipt_and_claim":
|
|
220
|
+
return "用于发送订单收据和交付认领链接的真实邮箱";
|
|
221
|
+
default:
|
|
222
|
+
return "服务端声明用途的真实邮箱";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
212
225
|
function paidContinuation(model, action, input) {
|
|
213
226
|
if (!action.capability_id)
|
|
214
227
|
return null;
|
|
@@ -226,6 +239,7 @@ function paidContinuation(model, action, input) {
|
|
|
226
239
|
price: { amount_minor: capability.price_amount_minor, currency: capability.price_currency },
|
|
227
240
|
} : {}),
|
|
228
241
|
delivery_email_required: capability.delivery_email_required,
|
|
242
|
+
...(capability.delivery_email_purpose ? { delivery_email_purpose: capability.delivery_email_purpose } : {}),
|
|
229
243
|
},
|
|
230
244
|
next: {
|
|
231
245
|
command: checkoutCommand(model.execution.service_execution_id, capability, input, !stateBacked),
|
|
@@ -288,7 +302,7 @@ export async function runServicesAction(backend, serviceExecutionID, actionType,
|
|
|
288
302
|
...(continuation ? { checkout: continuation.checkout } : {}),
|
|
289
303
|
},
|
|
290
304
|
instruction: continuation
|
|
291
|
-
? purchaseConfirmationInstruction("candidate_selected", continuation.price, continuation.capability.delivery_email_required, selection.title)
|
|
305
|
+
? purchaseConfirmationInstruction("candidate_selected", continuation.price, continuation.capability.delivery_email_required, continuation.capability.delivery_email_purpose, selection.title)
|
|
292
306
|
: "候选已绑定到来源 Execution;后续动作必须继续使用该 Execution。",
|
|
293
307
|
next,
|
|
294
308
|
recovery: [{
|
|
@@ -692,19 +706,26 @@ function servicesNextEnvelope(model) {
|
|
|
692
706
|
if (deliveryMode === "vault_artifact") {
|
|
693
707
|
const grantStatus = normalizeGrantStatus(delivery?.grant_status);
|
|
694
708
|
const grantActive = grantStatus === "active";
|
|
709
|
+
const grantPending = grantStatus === "pending";
|
|
695
710
|
return {
|
|
696
|
-
status: grantActive ? "grant_active" : "human_authorization_required",
|
|
711
|
+
status: grantActive ? "grant_active" : grantPending ? "result_preparing" : "human_authorization_required",
|
|
697
712
|
result: {
|
|
698
713
|
service_execution_id: execution.service_execution_id,
|
|
699
714
|
...(delivery?.capability_id ? { capability_id: delivery.capability_id } : {}),
|
|
700
715
|
delivery_mode: deliveryMode,
|
|
701
716
|
grant_status: grantStatus,
|
|
717
|
+
...(delivery?.preparation ? { preparation: delivery.preparation } : {}),
|
|
702
718
|
...(grantActive && delivery?.grant_expires_at ? { grant_expires_at: delivery.grant_expires_at } : {}),
|
|
703
719
|
},
|
|
704
720
|
instruction: grantActive
|
|
705
721
|
? "这是当前 Graph 步骤对应的交付;用户授权有效,立即读取并遵守字段范围与到期时间。"
|
|
706
|
-
:
|
|
707
|
-
|
|
722
|
+
: grantPending
|
|
723
|
+
? "用户已经完成授权,服务端正在按已发布执行图准备交付内容。不要再次付款、再次授权、新建 Execution 或调用 read-result;只执行 next.command 查询同一 Execution。"
|
|
724
|
+
: "这是当前 Graph 步骤对应的交付;请用户在订单页面授权,未授权前不要读取或猜测内容。",
|
|
725
|
+
next: grantPending ? {
|
|
726
|
+
command: `itpay services next ${execution.service_execution_id} --json`,
|
|
727
|
+
reason: "等待同一 Execution 的交付准备完成",
|
|
728
|
+
} : {
|
|
708
729
|
command: `itpay services read-result ${execution.service_execution_id} --json`,
|
|
709
730
|
reason: grantActive ? "读取当前有效 grant 的结果" : "仅在用户确认授权后执行",
|
|
710
731
|
},
|
|
@@ -724,7 +745,7 @@ function servicesNextEnvelope(model) {
|
|
|
724
745
|
phase: execution.phase,
|
|
725
746
|
checkout: continuation.checkout,
|
|
726
747
|
},
|
|
727
|
-
instruction: purchaseConfirmationInstruction(execution.status === "quota_exhausted" ? "quota_exhausted" : "candidate_selected", continuation.price, continuation.capability.delivery_email_required),
|
|
748
|
+
instruction: purchaseConfirmationInstruction(execution.status === "quota_exhausted" ? "quota_exhausted" : "candidate_selected", continuation.price, continuation.capability.delivery_email_required, continuation.capability.delivery_email_purpose),
|
|
728
749
|
next: continuation.next,
|
|
729
750
|
recovery: [],
|
|
730
751
|
};
|
package/dist/src/main.js
CHANGED
|
@@ -96,6 +96,10 @@ function reportCLIError(error, contract) {
|
|
|
96
96
|
error.code === "platform_release_unavailable" ||
|
|
97
97
|
(error.status === 404 && error.code === "unknown_error"));
|
|
98
98
|
const backendInternal = error instanceof HttpError && error.status === 500 && error.code === "internal_error";
|
|
99
|
+
const providerConnectionUnavailable = error instanceof HttpError && error.code === "provider_connection_unavailable";
|
|
100
|
+
const providerTemporary = error instanceof HttpError && error.code === "provider_temporarily_unavailable";
|
|
101
|
+
const providerRejected = error instanceof HttpError && error.code === "provider_rejected";
|
|
102
|
+
const capabilityInputInvalid = error instanceof HttpError && error.code === "capability_input_invalid";
|
|
99
103
|
const deviceRecovery = deviceError ? [{
|
|
100
104
|
command: "itpay skill show itpay-buyer --json",
|
|
101
105
|
reason: "读取身份边界;该错误需要用户或运营恢复 Backend 登记,不能通过换类型或删除本地身份绕过",
|
|
@@ -124,9 +128,17 @@ function reportCLIError(error, contract) {
|
|
|
124
128
|
? "立即向用户报告 error.message 并结束本次任务。不要运行任何其他 itpay、npm、which、device、docs、cart、orders 或 services 命令;不要寻找、安装或切换其他 CLI。只有运营明确提供兼容 CLI 后,才能在新的任务中重新开始。"
|
|
125
129
|
: backendInternal
|
|
126
130
|
? "Backend 内部故障;立即停止并向用户报告。不要重试、检查或删除 Device 身份、创建替代 Execution、切换 Backend,或尝试 quote、checkout、cart、buy、pay 等付费路径。"
|
|
127
|
-
:
|
|
131
|
+
: providerConnectionUnavailable
|
|
132
|
+
? "Provider 请求未发出,预留免费额度已释放;当前 Execution 已失败。立即向用户报告 error.message 并停止,不要自动重试、不要继续同一 Execution,也不要进入任何付费路径。只有运营确认连接恢复且用户明确要求重新查询后,才启动新的 Service Execution。"
|
|
133
|
+
: providerTemporary
|
|
134
|
+
? "上游服务暂时不可用;向用户逐字报告 error.message 并停止,不要自动重试。请求是否可能计费以 Backend 的消费事实为准;只有用户明确要求再次查询后,才可按 next/recovery 重新开始。"
|
|
135
|
+
: providerRejected
|
|
136
|
+
? "上游拒绝了本次输入;向用户逐字报告 error.message 并停止,不要重试同一 Execution。Provider 已收到响应,本次请求可能计费;只有用户提供修正后的输入后,才可按 next/recovery 继续。"
|
|
137
|
+
: capabilityInputInvalid
|
|
138
|
+
? "输入未通过本地校验,上游尚未被调用且用户额度未变化。向用户逐字报告 error.message 并停止,不要原样重试或运行其他恢复命令。用户提供修正后的输入后,继续使用当前未结束的 Execution。"
|
|
139
|
+
: commandError?.instruction ?? authorizationInstruction ?? contract?.instruction ?? "检查命令参数后重试。",
|
|
128
140
|
next: null,
|
|
129
|
-
recovery: incompatible || backendInternal ? [] : commandError?.recovery ?? (stateError ? stateRecovery : deviceError ? deviceRecovery : identityRecovery ? httpRecovery : contract?.recovery ?? []),
|
|
141
|
+
recovery: incompatible || backendInternal || providerConnectionUnavailable || providerTemporary || providerRejected || capabilityInputInvalid ? [] : commandError?.recovery ?? (stateError ? stateRecovery : deviceError ? deviceRecovery : identityRecovery ? httpRecovery : contract?.recovery ?? []),
|
|
130
142
|
}, {
|
|
131
143
|
...(contract?.jsonOutput !== undefined ? { jsonOutput: contract.jsonOutput } : {}),
|
|
132
144
|
output: (text) => { process.stderr.write(text); },
|
package/dist/src/state/config.js
CHANGED
|
@@ -9,9 +9,9 @@ import { BackendClient } from "../client/backend.js";
|
|
|
9
9
|
import { declaredAgentType } from "./agent_type.js";
|
|
10
10
|
import { DeviceAuthority } from "./device_authority.js";
|
|
11
11
|
import { OperationJournal } from "./operation_journal.js";
|
|
12
|
-
export const DEFAULT_BASE_URL = "https://
|
|
13
|
-
export const CLI_VERSION = "2.0.
|
|
14
|
-
export const API_CONTRACT_REVISION = "sha256:
|
|
12
|
+
export const DEFAULT_BASE_URL = "https://app.itpay.ai";
|
|
13
|
+
export const CLI_VERSION = "2.0.12";
|
|
14
|
+
export const API_CONTRACT_REVISION = "sha256:ea7d716f444a01e9d0109f12a53b56ce16d5ab813795a18a7a63f24a8c752073";
|
|
15
15
|
const CART_SESSION_DEFAULT_DIR = ".itpay-v3";
|
|
16
16
|
const CART_SESSION_FILENAME = "cart.json";
|
|
17
17
|
const OPERATION_JOURNAL_FILENAME = "operations.json";
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
],
|
|
33
33
|
"agent_rules": [
|
|
34
34
|
"Install with npm install -g @itpay/cli.",
|
|
35
|
-
"Use the default https://
|
|
35
|
+
"Use the default https://app.itpay.ai API unless an environment override is deliberate.",
|
|
36
36
|
"Use one exact type: codex-desktop, codex-cli, claude-code-desktop, claude-code-cli, or workbuddy.",
|
|
37
37
|
"One local private key is reused, but each exact Backend API base URL has its own server registration and quota lineage.",
|
|
38
38
|
"Within one Backend registration, each Agent Type has one Agent Instance; all windows and chats of the same type reuse it.",
|
|
@@ -29,7 +29,7 @@ itpay install [target] [--json]
|
|
|
29
29
|
"result": {
|
|
30
30
|
"agent_type": "codex-desktop",
|
|
31
31
|
"default_host": "codex",
|
|
32
|
-
"default_api": "https://
|
|
32
|
+
"default_api": "https://app.itpay.ai",
|
|
33
33
|
"install_command": "npm install -g @itpay/cli"
|
|
34
34
|
},
|
|
35
35
|
"instruction": "在 Codex Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
|
|
@@ -62,6 +62,25 @@ itpay services invoke <service_execution_id> --capability <capability_id>
|
|
|
62
62
|
|
|
63
63
|
缺少 required input 时返回 `capability_input_invalid`,recovery 给出带占位符的同一 invoke 命令;CLI 和 Backend 都必须在 Provider 调用前拒绝,Backend 还必须在 execution/event/quota/invocation 写入前拒绝。错误调用付费 capability 时不得给出购买旁路,只能回到同一 Execution 的 `services next`;execution 状态、event、ProviderCalled 均保持不变。
|
|
64
64
|
|
|
65
|
+
## Provider 请求前连接失败
|
|
66
|
+
|
|
67
|
+
如果 Backend 能确认请求未发出,返回固定的终态错误,不暴露 DNS、IP、Provider URL 或凭证诊断:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"status": "error",
|
|
72
|
+
"error": {
|
|
73
|
+
"code": "provider_connection_unavailable",
|
|
74
|
+
"message": "provider request was not sent; reserved quota was released"
|
|
75
|
+
},
|
|
76
|
+
"instruction": "Provider 请求未发出,预留免费额度已释放;当前 Execution 已失败。立即向用户报告 error.message 并停止,不要自动重试、不要继续同一 Execution,也不要进入任何付费路径。只有运营确认连接恢复且用户明确要求重新查询后,才启动新的 Service Execution。",
|
|
77
|
+
"next": null,
|
|
78
|
+
"recovery": []
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
该终态不允许 CLI 猜测网络修复、重复 invoke 或转入购买。连接恢复后也不能复用失败 Execution;必须同时满足“运营已确认恢复”和“用户明确要求再次查询”,才创建新 Execution。
|
|
83
|
+
|
|
65
84
|
## Agent Type / Host
|
|
66
85
|
|
|
67
86
|
`codex-desktop`、`codex-cli`、`claude-code-desktop`、`claude-code-cli`、`workbuddy` 五种 Agent Type 的 safe result 一致。instruction 可以适配对话表述,但不得隐藏 quota、价格或 schema 错误。
|
|
@@ -65,7 +65,7 @@ itpay services next <service_execution_id> [--json]
|
|
|
65
65
|
}
|
|
66
66
|
]
|
|
67
67
|
},
|
|
68
|
-
"instruction": "
|
|
68
|
+
"instruction": "付费 Agent-visible 搜索已完成。现在把 items 中的编号、title 和 safe_payload 展示给用户,然后停止。本结果是 agent-visible,不要调用 read-result。若用户的目标只是候选搜索,任务已经完成;只有用户之后明确选择某个候选并要求继续时,才执行 next.command。不要自动购买后续报告。",
|
|
69
69
|
"next": {
|
|
70
70
|
"command": "itpay services action <id> --action select_candidate --actor-type human --status approved --candidate <rank> --json",
|
|
71
71
|
"reason": "仅在用户明确选择候选并要求继续时执行"
|
|
@@ -98,6 +98,33 @@ itpay services next <service_execution_id> [--json]
|
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
+
用户已经授权、但服务端仍在按已发布执行图准备 Vault 交付时,必须只轮询同一 Execution:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"status": "result_preparing",
|
|
106
|
+
"result": {
|
|
107
|
+
"service_execution_id": "<id>",
|
|
108
|
+
"capability_id": "<capability_id>",
|
|
109
|
+
"delivery_mode": "vault_artifact",
|
|
110
|
+
"grant_status": "pending",
|
|
111
|
+
"preparation": {
|
|
112
|
+
"status": "running",
|
|
113
|
+
"total_nodes": 4,
|
|
114
|
+
"completed_nodes": 2,
|
|
115
|
+
"succeeded_nodes": 2,
|
|
116
|
+
"failed_nodes": 0
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"instruction": "用户已经完成授权,服务端正在按已发布执行图准备交付内容。不要再次付款、再次授权、新建 Execution 或调用 read-result;只执行 next.command 查询同一 Execution。",
|
|
120
|
+
"next": {
|
|
121
|
+
"command": "itpay services next <id> --json",
|
|
122
|
+
"reason": "等待同一 Execution 的交付准备完成"
|
|
123
|
+
},
|
|
124
|
+
"recovery": []
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
101
128
|
有效 grant 存在时:
|
|
102
129
|
|
|
103
130
|
```json
|
package/package.json
CHANGED
|
@@ -70,6 +70,7 @@ Run `next.command` only after the human says they acted or asks for status. QR r
|
|
|
70
70
|
|
|
71
71
|
- Agent-visible results come from `services next`; do not use `read-result` for them.
|
|
72
72
|
- Protected results require a current 15-minute human grant scoped to one delivery, approved fields, and frozen Agent audience.
|
|
73
|
+
- If `services next` returns `result_preparing`, authorization is already complete. Run only its same-Execution `next.command`; do not pay, authorize, start, or call `read-result` again.
|
|
73
74
|
- An Execution may have delivery history; follow `services next` for the Backend-selected current delivery.
|
|
74
75
|
- A pending refund locks delivery and revokes active grants. Follow the returned refund command and state.
|
|
75
76
|
|
|
@@ -88,6 +89,8 @@ itpay --agent-type <agent_type> refund get <refund_request_id> --json
|
|
|
88
89
|
|
|
89
90
|
Reuse the same Execution and Checkout. Never start another Execution, create another Checkout, change payment route, or replay a capability to bypass quota, selection, payment, delivery, grant, or refund state.
|
|
90
91
|
|
|
92
|
+
`provider_connection_unavailable` is a terminal exception: Backend confirms no Provider request was sent and releases the reservation, then fails that Execution. Stop with no recovery command. Only after an operator confirms connectivity is restored and the human explicitly asks to query again may you start a new Execution.
|
|
93
|
+
|
|
91
94
|
## Safety
|
|
92
95
|
|
|
93
96
|
- Never invent service, capability, item, Checkout, Order, grant, or refund IDs.
|