@itpay/cli 2.0.30 → 2.0.32

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 (48) hide show
  1. package/README.md +20 -9
  2. package/dist/src/client/backend.js +3 -1
  3. package/dist/src/commands/checkout.js +1 -1
  4. package/dist/src/commands/guidance.js +11 -412
  5. package/dist/src/commands/install.js +1 -1
  6. package/dist/src/commands/order.js +13 -3
  7. package/dist/src/commands/orders.js +66 -17
  8. package/dist/src/commands/pay.js +1 -1
  9. package/dist/src/commands/readyz.js +2 -2
  10. package/dist/src/commands/refund.js +12 -12
  11. package/dist/src/commands/services.js +48 -28
  12. package/dist/src/commands/skill.js +3 -3
  13. package/dist/src/commands/vault.js +63 -17
  14. package/dist/src/commands/vault_handoff.js +71 -0
  15. package/dist/src/main.js +56 -17
  16. package/dist/src/render/ide.js +1 -1
  17. package/dist/src/state/config.js +2 -2
  18. package/docs/agent/buyer/catalog-list.json +11 -8
  19. package/docs/agent/buyer/install-and-setup.json +15 -13
  20. package/docs/agent/buyer/orders-refunds.json +34 -7
  21. package/docs/agent/buyer/payment-flow.json +9 -4
  22. package/docs/agent/buyer/purchased-content.json +58 -0
  23. package/docs/agent/buyer/quickstart.json +22 -40
  24. package/docs/agent/buyer/render-hosts.json +7 -4
  25. package/docs/cli-reference/agent-types.md +23 -5
  26. package/docs/cli-reference/commands/checkout.md +3 -1
  27. package/docs/cli-reference/commands/install.md +3 -1
  28. package/docs/cli-reference/commands/order.md +2 -2
  29. package/docs/cli-reference/commands/orders.md +43 -55
  30. package/docs/cli-reference/commands/pay.md +2 -0
  31. package/docs/cli-reference/commands/readyz.md +3 -3
  32. package/docs/cli-reference/commands/refund/create.md +2 -2
  33. package/docs/cli-reference/commands/refund/get.md +7 -7
  34. package/docs/cli-reference/commands/refund/index.md +8 -0
  35. package/docs/cli-reference/commands/refund/watch.md +2 -2
  36. package/docs/cli-reference/commands/services/action.md +1 -1
  37. package/docs/cli-reference/commands/services/invoke.md +5 -5
  38. package/docs/cli-reference/commands/services/list.md +3 -3
  39. package/docs/cli-reference/commands/services/next.md +7 -5
  40. package/docs/cli-reference/commands/skill.md +28 -11
  41. package/docs/cli-reference/commands/vault/access.md +37 -9
  42. package/docs/cli-reference/commands/vault/index.md +12 -5
  43. package/docs/cli-reference/commands/vault/list.md +26 -9
  44. package/docs/cli-reference/commands/vault/read.md +18 -5
  45. package/docs/cli-reference/conventions.md +27 -0
  46. package/docs/cli-reference/index.md +2 -2
  47. package/package.json +2 -2
  48. package/skills/itpay/SKILL.md +74 -136
package/README.md CHANGED
@@ -1,20 +1,30 @@
1
1
  # ItPay CLI
2
2
 
3
- The official V3 CLI and the single ItPay entry point for Agent-driven commerce.
3
+ The official V3 CLI and the single ItPay entry point for services, purchases, account history, and human-authorized content reads.
4
4
 
5
- ## One Entry Point, Two Actions
5
+ ## One Entry Point, Several Human Intents
6
6
 
7
- `itpay` is the only public CLI entry point, and `$itpay` is its user-facing Skill invocation. Under that one product entry point, the two top-level commerce actions are `buy` and `sell`: Buyer workflows are available now, while Seller workflows will use the same entry point and are not implemented yet. Do not create separate Buyer or Seller product entry points.
7
+ `itpay` is the only public CLI entry point, and `$itpay` is its user-facing Skill invocation. The Agent first distinguishes a new service request, previously purchased content, order history, or a refund. Seller workflows will use the same entry point later and are not implemented yet.
8
8
 
9
9
  ```bash
10
10
  npm install -g @itpay/cli
11
- itpay readyz --json
12
- itpay skill show itpay --json
13
11
  itpay install --json
14
12
  itpay --agent-type codex-desktop readyz --json
15
- # follow next.command: typed skill show, then catalog list
13
+ # execute the returned next.command until the typed Skill is loaded
16
14
  ```
17
15
 
16
+ Official Agent install prompt(可直接复制给 Agent):
17
+
18
+ > 安装或更新官方 `@itpay/cli`。从 `itpay install --json` 开始,选择你真实
19
+ > 所在的平台,完整执行 iTPay 返回的每个 `next.command` 并读取内置 Skill。
20
+ > 初始化后,根据我的自然语言需求自行使用 iTPay,不要让我运行命令,也不要向
21
+ > 我讲解命令、内部 ID 或技术步骤。只有需要我选择、授权、付款、提供联系方式
22
+ > 或确认退款时,才用通俗语言询问我;展示官方操作入口后停止等待。不要猜命令、
23
+ > 切换身份或环境、绕过授权或重复创建资源。准备完成后只回复“iTPay 已就绪”。
24
+
25
+ Use a separate test prompt to set the exact official Dev Backend. Never put a
26
+ Dev override in the public install prompt.
27
+
18
28
  The CLI defaults to the production Backend `https://app.itpay.ai`. Explicit tests may set `ITPAY_BACKEND_URL=https://dev.itpay.ai`; every other Backend URL is rejected before network or local state access.
19
29
 
20
30
  ## Output Contract
@@ -58,6 +68,7 @@ The local installation keeps one Ed25519 private key and a separate registration
58
68
  - `checkout`: authoritative payment and fulfillment recovery.
59
69
  - `services read-result`: read one human-granted protected result.
60
70
  - `order`, `orders`: exact order and account order views.
71
+ - `vault list/access/read`: find and read previously purchased content after time-limited human authorization.
61
72
  - `refund create/list/get/watch/cancel`: Refund Owner flow.
62
73
  - `services get/events`: redacted support diagnostics; normal flows should use `services next`.
63
74
  - `install`, `skill show`, `docs list/show/search`: offline packaged guidance.
@@ -84,12 +95,12 @@ The local `~/.itpay-v3` directory stores one owner-only signing key, Backend-sco
84
95
 
85
96
  - `ITPAY_AGENT_TYPE`: stable alternative to global `--agent-type`.
86
97
  - `ITPAY_BACKEND_URL`: optional test override; only the exact official URL `https://dev.itpay.ai` is accepted. Unset it for production.
87
- - `ITPAY_BEARER_TOKEN`: account-scoped Buyer session for account-only commands such as `orders`.
98
+ - `ITPAY_BEARER_TOKEN`: optional account-scoped browser session for legacy account reads. Local Agents normally use signed Device Authority plus the time-limited human authorization returned by `vault access`.
88
99
  - `ITPAY_CART_SESSION_PATH`: local recovery-state path override.
89
100
  - `ITPAY_CURRENCY`: ordinary Cart currency, default `CNY`.
90
101
  - `ITPAY_IDEMPOTENCY_KEY`: explicit operation key for deterministic testing; normal use persists operation IDs automatically.
91
- - `ITPAY_IDE_IMAGE_ATTACH=0`: disable local Checkout image download when the runtime filesystem is read-only.
92
- - `ITPAY_IDE_IMAGE_DIR_OVERRIDE`: override the local Checkout image directory.
102
+ - `ITPAY_IDE_IMAGE_ATTACH=0`: disable local Checkout and authorization image download when the runtime filesystem is read-only.
103
+ - `ITPAY_IDE_IMAGE_DIR_OVERRIDE`: override the local handoff image directory.
93
104
 
94
105
  Provider credentials, Buyer identity, payment provider choice, amount, refund policy, quota, grant scope, and delivery access are never client-owned environment settings.
95
106
 
@@ -58,11 +58,13 @@ export class BackendClient {
58
58
  getOrderDeliveryAccess(orderID) {
59
59
  return this.http.get(`/v1/orders/${encodeURIComponent(orderID)}/delivery-access`);
60
60
  }
61
- listAccountOrders(limit, status, bearer) {
61
+ listAccountOrders(limit, status, bearer, cursor) {
62
62
  const qs = new URLSearchParams({ limit: String(limit) });
63
63
  if (status) {
64
64
  qs.set("status", status);
65
65
  }
66
+ if (cursor)
67
+ qs.set("cursor", cursor);
66
68
  return this.http.get(`/v1/me/orders?${qs}`, bearer ? { bearer } : {});
67
69
  }
68
70
  getVaultAccountStatus() {
@@ -110,7 +110,7 @@ function terminalCheckoutEnvelope(presentation) {
110
110
  const recovery = [];
111
111
  if (payment === "verified") {
112
112
  status = "completed";
113
- instruction = "Backend 已确认这笔付款。不要再次展示付款入口,不要调用 pay,不要创建新 Checkout 或 Execution。现在只执行 next.command,读取同一 Execution 的履约结果。";
113
+ instruction = "告诉用户:付款已经确认,订单已经记录,不需要再次付款;结果会在同一订单下继续准备,如果最终无法交付,可以从原订单申请退款,处理方式由内容是否已使用决定。然后只执行 next.command 读取同一笔服务;Agent 不再展示付款入口或创建新订单,也不承诺退款结果。";
114
114
  next = serviceExecutionIDs.length === 1
115
115
  ? { command: `itpay services next ${serviceExecutionIDs[0]} --json`, reason: "读取同一笔已付款 Service Execution" }
116
116
  : presentation.completed_order_id
@@ -57,49 +57,6 @@ export function writeCommandEnvelope(value, options = {}) {
57
57
  }
58
58
  }
59
59
  }
60
- export function attachAgentGuidance(payload, guidance) {
61
- return {
62
- ...payload,
63
- agent_guidance: guidance,
64
- };
65
- }
66
- export function printAgentGuidance(guidance, output) {
67
- const out = resolveOutput(output);
68
- const agentType = declaredAgentType();
69
- out(`${guidance.summary}\n`);
70
- if (guidance.visible_results?.length) {
71
- out("results:\n");
72
- for (const item of guidance.visible_results) {
73
- out(` ${item.rank}. ${item.title}\n`);
74
- for (const [key, value] of Object.entries(item.safe_payload)) {
75
- if (key === "name" || key === "company_name" || value === "" || value === undefined || value === null)
76
- continue;
77
- out(` ${key}: ${String(value)}\n`);
78
- }
79
- }
80
- }
81
- if (guidance.next_actions.length === 0) {
82
- out("next actions: none\n");
83
- }
84
- else {
85
- out("next actions:\n");
86
- for (const action of guidance.next_actions) {
87
- out(` - ${action.label}\n`);
88
- out(` ${qualifyBackendCommand(qualifyItPayCommand(action.command, agentType))}\n`);
89
- if (action.requires_human)
90
- out(" requires human confirmation\n");
91
- if (action.reason)
92
- out(` reason: ${action.reason}\n`);
93
- }
94
- }
95
- if (guidance.recovery.length > 0) {
96
- out("recovery:\n");
97
- for (const action of guidance.recovery) {
98
- out(` - ${action.label}\n`);
99
- out(` ${qualifyBackendCommand(qualifyItPayCommand(action.command, agentType))}\n`);
100
- }
101
- }
102
- }
103
60
  function qualifyEnvelope(value, agentType) {
104
61
  return {
105
62
  ...value,
@@ -110,181 +67,31 @@ function qualifyEnvelope(value, agentType) {
110
67
  })),
111
68
  };
112
69
  }
113
- export function buildCartGuidance(cart, serviceModel) {
114
- const serviceLine = latestServiceLine(cart);
115
- if (serviceLine?.service_execution_id) {
116
- const serviceGuidance = serviceModel
117
- ? buildServiceReadModelGuidance(serviceModel)
118
- : buildServiceHandleGuidance(serviceLine.service_execution_id, serviceLine.service_capability_id);
119
- return {
120
- ...serviceGuidance,
121
- kind: "cart_service_execution",
122
- summary: `cart ${cart.cart_id}: service-backed line ${lineID(serviceLine)} is ready for Service Execution`,
123
- state: {
124
- ...serviceGuidance.state,
125
- cart_id: cart.cart_id,
126
- cart_item_id: lineID(serviceLine),
127
- service_capability_id: serviceLine.service_capability_id,
128
- },
129
- };
130
- }
131
- if (cart.items.length === 0) {
132
- return {
133
- kind: "empty_cart",
134
- summary: `cart ${cart.cart_id}: empty`,
135
- state: { cart_id: cart.cart_id, status: cart.status },
136
- next_actions: [
137
- {
138
- id: "browse_catalog",
139
- label: "Browse services",
140
- command: "itpay catalog list",
141
- },
142
- ],
143
- recovery: [],
144
- };
145
- }
146
- return {
147
- kind: "cart_checkout_ready",
148
- summary: `cart ${cart.cart_id}: ready for checkout`,
149
- state: { cart_id: cart.cart_id, status: cart.status, amount_minor: cart.amount_minor, currency: cart.currency },
150
- next_actions: [
151
- {
152
- id: "checkout_cart",
153
- label: "Create ItPay checkout",
154
- command: `itpay buy --cart ${cart.cart_id} --host <client> --contact-email <email>`,
155
- requires_human: true,
156
- reason: "The human must review and pay on the ItPay checkout page.",
157
- },
158
- ],
159
- recovery: [
160
- {
161
- id: "show_cart",
162
- label: "Inspect current server cart",
163
- command: "itpay cart show",
164
- },
165
- ],
166
- };
167
- }
168
- export function buildServiceStartedGuidance(response) {
169
- return buildServiceGuidance({
170
- execution: response.execution,
171
- capabilities: response.capabilities,
172
- });
173
- }
174
- export function buildServiceReadModelGuidance(model) {
175
- return buildServiceGuidance({
176
- execution: model.execution,
177
- capabilities: model.capabilities,
178
- resultItems: model.result_items,
179
- checkoutBindings: model.checkout_bindings,
180
- deliveryBindings: model.delivery_bindings,
181
- });
182
- }
183
- export function buildServiceInvokedGuidance(response, capabilities = []) {
184
- return buildServiceGuidance({
185
- execution: response.execution,
186
- capabilities,
187
- resultItems: response.result_items,
188
- ...(response.next_actions ? { backendNextActions: response.next_actions } : {}),
189
- ...(response.effective_quota ? { effectiveQuota: response.effective_quota } : {}),
190
- providerCalled: response.provider_called,
191
- });
192
- }
193
- export function buildServiceActionGuidance(action) {
194
- const serviceExecutionID = action.service_execution_id;
195
- return {
196
- kind: "service_action_recorded",
197
- summary: `service execution ${serviceExecutionID}: action ${action.action_type} recorded`,
198
- state: {
199
- service_execution_id: serviceExecutionID,
200
- action_type: action.action_type,
201
- status: action.status,
202
- result_item_id: action.result_item_id,
203
- },
204
- next_actions: [
205
- {
206
- id: "inspect_service_execution",
207
- label: "Read updated Service Execution state",
208
- command: `itpay services next ${serviceExecutionID} --json`,
209
- },
210
- ],
211
- recovery: [
212
- {
213
- id: "timeline",
214
- label: "Inspect full timeline",
215
- command: `itpay services get ${serviceExecutionID}`,
216
- },
217
- ],
218
- };
219
- }
220
- export function buildServiceHandleGuidance(serviceExecutionID, checkoutCapabilityID) {
221
- const actions = [
222
- {
223
- id: "inspect_service_execution",
224
- label: "Read Service Execution state and capabilities",
225
- command: `itpay services next ${serviceExecutionID} --json`,
226
- },
227
- ];
228
- if (checkoutCapabilityID) {
229
- actions.push({
230
- id: "checkout_service",
231
- label: "Create checkout after human confirmation",
232
- command: `itpay services checkout ${serviceExecutionID} --capability ${checkoutCapabilityID} --json`,
233
- requires_human: true,
234
- reason: "Inspect the Service Execution first; the CLI will request delivery contact only when the selected capability requires it.",
235
- });
236
- }
237
- return {
238
- kind: "service_execution_handle",
239
- summary: `service execution ${serviceExecutionID}: inspect before invoking`,
240
- state: { service_execution_id: serviceExecutionID },
241
- next_actions: actions,
242
- recovery: [],
243
- };
244
- }
245
70
  export function errorRecoveryActions(error) {
246
71
  if (!(error instanceof HttpError))
247
72
  return [];
248
73
  if (error.code === "agent_identity_required") {
249
- return [
250
- {
251
- id: "inspect_agent_setup",
252
- label: "Inspect supported Agent Type setup",
253
- command: "itpay install --json",
254
- },
255
- ];
74
+ return [{ id: "inspect_agent_setup", label: "Inspect supported Agent Type setup", command: "itpay install --json" }];
256
75
  }
257
76
  if (error.code === "agent_device_session_required") {
258
- return [
259
- {
77
+ return [{
260
78
  id: "read_agent_session_rules",
261
79
  label: "Read ItPay identity and session recovery rules",
262
80
  command: "itpay skill show itpay --json",
263
81
  reason: "The CLI already attempted one automatic session renewal; do not rotate identity or loop retries.",
264
- },
265
- ];
82
+ }];
266
83
  }
267
84
  if (error.code === "quota_exhausted" || error.code === "checkout_required") {
268
- return [
269
- {
85
+ return [{
270
86
  id: "inspect_service_execution",
271
87
  label: "Inspect Service Execution before checkout",
272
88
  command: "itpay services next <service_execution_id> --json",
273
- },
274
- ];
89
+ }];
275
90
  }
276
91
  if (error.code === "cart_item_locked" || error.status === 409) {
277
92
  return [
278
- {
279
- id: "show_cart",
280
- label: "Inspect the canonical server cart",
281
- command: "itpay cart show",
282
- },
283
- {
284
- id: "continue_checkout",
285
- label: "Continue the last locally remembered checkout",
286
- command: "itpay checkout",
287
- },
93
+ { id: "show_cart", label: "Inspect the canonical server cart", command: "itpay cart show" },
94
+ { id: "continue_checkout", label: "Continue the last locally remembered checkout", command: "itpay checkout" },
288
95
  {
289
96
  id: "recover_service_execution",
290
97
  label: "List recoverable Service Executions if the local handoff is missing",
@@ -293,22 +100,18 @@ export function errorRecoveryActions(error) {
293
100
  ];
294
101
  }
295
102
  if (error.status === 404) {
296
- return [
297
- {
103
+ return [{
298
104
  id: "recover_service_executions",
299
105
  label: "List visible Service Executions and follow their next instruction",
300
106
  command: "itpay services list",
301
- },
302
- ];
107
+ }];
303
108
  }
304
109
  if (error.status === 502 || error.status === 503 || error.status === 504) {
305
- return [
306
- {
110
+ return [{
307
111
  id: "retry_after_backend_recovers",
308
112
  label: "Retry after the selected official Backend is reachable",
309
113
  command: "itpay readyz",
310
- },
311
- ];
114
+ }];
312
115
  }
313
116
  return [];
314
117
  }
@@ -323,207 +126,3 @@ export function printErrorRecovery(error, output) {
323
126
  out(` ${action.command}\n`);
324
127
  }
325
128
  }
326
- function buildServiceGuidance(input) {
327
- const execution = input.execution;
328
- const capabilities = input.capabilities ?? [];
329
- const prePurchase = firstPrePurchaseCapability(capabilities);
330
- const paid = firstPaidCapability(capabilities);
331
- const resultItem = input.resultItems?.[0];
332
- const checkoutID = input.checkoutBindings?.at(-1)?.checkout_id;
333
- const delivery = input.deliveryBindings?.[0];
334
- const deliveryMode = String(delivery?.redacted_summary?.delivery_mode ?? (delivery?.vault_artifact_id ? "vault_artifact" : ""));
335
- const backendCheckout = input.backendNextActions?.find((action) => action.kind === "create_checkout");
336
- const nextActions = [];
337
- const recovery = [
338
- {
339
- id: "timeline",
340
- label: "Inspect full timeline",
341
- command: `itpay services get ${execution.service_execution_id}`,
342
- },
343
- ];
344
- if (isTerminalServiceExecutionStatus(execution.status)) {
345
- // Terminal executions are inspectable, but no command may advance or replay them.
346
- }
347
- else if (deliveryMode === "agent_visible_result" && (input.resultItems?.length ?? 0) > 0) {
348
- nextActions.push({
349
- id: "use_agent_visible_result",
350
- label: "Use the safe candidate list shown above",
351
- command: `itpay services next ${execution.service_execution_id} --json`,
352
- reason: "This result is already visible to the agent. Do not call services read-result; that command is only for Vault deliveries authorized by a human.",
353
- });
354
- }
355
- else if (execution.status === "completed" || execution.next_action === "completed") {
356
- nextActions.push({
357
- id: "inspect_order_or_grant",
358
- label: "Inspect order, claim, or grant from the checkout/order owner",
359
- command: "itpay orders --limit 20",
360
- requires_human: true,
361
- reason: "Delivery visibility is controlled by the human account and grant flow.",
362
- });
363
- }
364
- else if (execution.phase === "delivery" || execution.next_action === "view_delivery") {
365
- const grantStatus = String(delivery?.grant_status ?? "");
366
- if (grantStatus === "active") {
367
- nextActions.push({
368
- id: "read_granted_result",
369
- label: "Read the human-granted service result",
370
- command: `itpay services read-result ${execution.service_execution_id}`,
371
- reason: "The human granted temporary agent access to this delivery.",
372
- });
373
- }
374
- else {
375
- nextActions.push({
376
- id: grantStatus === "expired" ? "ask_human_to_reauthorize" : "wait_for_human_agent_grant",
377
- label: grantStatus === "expired" ? "Ask the human to authorize AI access again" : "Wait for human AI-access authorization",
378
- command: `itpay services get ${execution.service_execution_id}`,
379
- requires_human: true,
380
- reason: grantStatus === "expired"
381
- ? "The 15 minute human grant expired."
382
- : "The human must approve AI access on the Credential page.",
383
- });
384
- }
385
- }
386
- else if (execution.next_action === "pay_checkout" || execution.status === "checkout_pending") {
387
- nextActions.push({
388
- id: "open_existing_checkout",
389
- label: "Open the existing ItPay checkout handoff",
390
- command: `itpay services checkout ${execution.service_execution_id} --resume --json`,
391
- requires_human: true,
392
- reason: checkoutID
393
- ? `Checkout ${checkoutID} already exists; reissue a short-lived handoff without creating another checkout.`
394
- : "Recover the existing checkout handoff from Service Execution owner facts.",
395
- });
396
- }
397
- else if (execution.checkout_required || execution.next_action === "create_checkout") {
398
- const capabilityID = backendCheckout?.capability_id ?? paid?.capability_id ?? input.checkoutCapabilityID ?? execution.current_capability_id;
399
- if (capabilityID) {
400
- const checkoutCapability = capabilities.find((capability) => capability.capability_id === capabilityID);
401
- const emailRequired = checkoutCapability?.delivery_email_required === true;
402
- nextActions.push({
403
- id: "checkout_service",
404
- label: "Create ItPay checkout for the paid service capability",
405
- command: `itpay services checkout ${execution.service_execution_id} --capability ${capabilityID}${emailRequired ? " --email <email>" : ""} --json`,
406
- requires_human: true,
407
- reason: emailRequired
408
- ? deliveryEmailGuidance(checkoutCapability?.delivery_email_purpose)
409
- : "This capability returns an agent-visible result after payment and does not require a delivery email.",
410
- });
411
- }
412
- else {
413
- nextActions.push({
414
- id: "inspect_capabilities",
415
- label: "Inspect capabilities before checkout",
416
- command: `itpay services get ${execution.service_execution_id}`,
417
- });
418
- }
419
- }
420
- else if ((input.providerCalled || execution.next_action === "select_candidate") && (input.resultItems?.length ?? 0) === 0) {
421
- // Terminal empty result: a later explicit human request starts a new flow.
422
- }
423
- else if (needsHumanSelection(execution, resultItem)) {
424
- nextActions.push({
425
- id: "select_result_item",
426
- label: "Ask the human to select a result item, then submit the selection",
427
- command: `itpay services action ${execution.service_execution_id} --action select_candidate --actor-type human --status approved --candidate <rank>`,
428
- requires_human: true,
429
- reason: "Do not choose a candidate without explicit human confirmation.",
430
- });
431
- }
432
- else if (prePurchase) {
433
- const action = {
434
- id: "invoke_capability",
435
- label: `Invoke ${prePurchase.capability_id}`,
436
- command: `itpay services invoke ${execution.service_execution_id} --capability ${prePurchase.capability_id} --input key=value --json`,
437
- };
438
- if (prePurchase.free_quota_limit) {
439
- action.reason = `One keyword per execution. Free quota limit: ${prePurchase.free_quota_limit} per ${prePurchase.quota_subject || "subject"}.`;
440
- }
441
- nextActions.push(action);
442
- }
443
- else {
444
- nextActions.push({
445
- id: "inspect_service_execution",
446
- label: "Read Service Execution state",
447
- command: `itpay services next ${execution.service_execution_id} --json`,
448
- });
449
- }
450
- return {
451
- kind: "service_execution",
452
- summary: `service execution ${execution.service_execution_id}: ${execution.status}/${execution.phase}${input.effectiveQuota ? `, quota ${input.effectiveQuota.remaining}/${input.effectiveQuota.limit}` : ""}`,
453
- state: {
454
- service_execution_id: execution.service_execution_id,
455
- service_id: execution.service_id,
456
- status: execution.status,
457
- phase: execution.phase,
458
- current_capability_id: execution.current_capability_id,
459
- checkout_required: execution.checkout_required,
460
- next_action: execution.next_action,
461
- delivery: delivery
462
- ? {
463
- status: delivery.status,
464
- delivery_mode: deliveryMode || undefined,
465
- vault_artifact_id: delivery.vault_artifact_id,
466
- vault_status: delivery.vault_status,
467
- vault_payload_state: delivery.vault_payload_state,
468
- reveal_status: delivery.reveal_status,
469
- grant_status: delivery.grant_status ?? "missing",
470
- grant_expires_at: delivery.grant_expires_at,
471
- }
472
- : undefined,
473
- capabilities: capabilities.map((capability) => ({
474
- capability_id: capability.capability_id,
475
- agent_visible: capability.agent_visible,
476
- requires_payment: capability.requires_payment,
477
- vault_required: capability.vault_required,
478
- delivery_email_required: capability.delivery_email_required,
479
- delivery_email_purpose: capability.delivery_email_purpose,
480
- price_amount_minor: capability.price_amount_minor,
481
- price_currency: capability.price_currency,
482
- free_quota_limit: capability.free_quota_limit,
483
- })),
484
- result_items: (input.resultItems ?? []).map((item) => ({
485
- result_item_id: item.service_capability_result_item_id,
486
- rank: item.rank,
487
- display_title: item.display_title,
488
- safe_payload: item.safe_payload,
489
- })),
490
- effective_quota: input.effectiveQuota,
491
- },
492
- next_actions: nextActions,
493
- recovery,
494
- ...(input.resultItems?.length
495
- ? { visible_results: input.resultItems.map((item) => ({ rank: item.rank, title: item.display_title, safe_payload: item.safe_payload })) }
496
- : {}),
497
- };
498
- }
499
- function deliveryEmailGuidance(purpose) {
500
- switch (purpose) {
501
- case "receipt":
502
- return "Ask the human for their email. It is used to send the order receipt; never invent or substitute an address.";
503
- case "claim":
504
- return "Ask the human for their email. It is used to send the protected result claim link; never invent or substitute an address.";
505
- case "receipt_and_claim":
506
- 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.";
507
- default:
508
- return "Ask the human for the required email and state only the Backend-declared purpose; never invent or substitute an address.";
509
- }
510
- }
511
- function firstPrePurchaseCapability(capabilities) {
512
- return capabilities.find((capability) => capability.agent_visible && !capability.requires_payment)
513
- ?? capabilities.find((capability) => capability.agent_visible);
514
- }
515
- function firstPaidCapability(capabilities) {
516
- return (capabilities.find((capability) => capability.requires_payment && capability.vault_required) ??
517
- capabilities.find((capability) => capability.requires_payment));
518
- }
519
- function needsHumanSelection(execution, resultItem) {
520
- if (execution.phase !== "pre_purchase")
521
- return false;
522
- return execution.next_action === "select_candidate" || execution.next_action === "human_action_required" || resultItem !== undefined;
523
- }
524
- function latestServiceLine(cart) {
525
- return [...cart.items].reverse().find((item) => item.service_execution_id);
526
- }
527
- function lineID(line) {
528
- return line.cart_item_id ?? line.line_item_id ?? line.checkout_item_id ?? "<unknown_line>";
529
- }
@@ -25,7 +25,7 @@ export function runInstall(target, options = {}) {
25
25
  writeCommandEnvelope({
26
26
  status: "install_targets",
27
27
  result: { agent_types: agentTypes },
28
- instruction: "选择当前真实运行环境;同一 Agent 不要临时更换 Agent Type。",
28
+ instruction: "识别当前真实运行平台,从 result.agent_types 选择对应 agent_type,并由 Agent 自行运行 itpay install <agent_type> --json;不要让用户运行命令,也不要临时更换 Agent Type。",
29
29
  next: null,
30
30
  recovery: [{ command: "itpay docs show install-and-setup", reason: "查看安装与环境说明" }],
31
31
  }, {
@@ -19,17 +19,27 @@ function orderEnvelope(order, delivery, lockedRefund) {
19
19
  let instruction = "订单状态已读取;当前没有可用交付入口。";
20
20
  let next = null;
21
21
  if (lockedRefund) {
22
- instruction = "退款访问锁已生效;不要 reveal、创建 grant 或读取交付结果。";
22
+ instruction = "告诉用户退款处理中,原交付已按政策冻结。然后读取同一退款的权威状态;Agent 不读取交付、不创建授权或重复申请退款。";
23
23
  if (!refundTerminal) {
24
24
  next = { command: `itpay refund get ${lockedRefund.refund_request_id} --json`, reason: "读取退款的服务器状态" };
25
25
  }
26
26
  }
27
27
  else if (delivery?.service_execution_id) {
28
- instruction = "根据 delivery_mode 使用对应读取入口;不要从订单摘要猜测受保护内容。";
28
+ instruction = "告诉用户订单已经找到并说明当前交付状态。然后使用返回的读取入口;Agent 不向用户提及 delivery_mode,也不从订单摘要猜测受保护内容。";
29
29
  next = { command: `itpay services next ${delivery.service_execution_id} --json`, reason: "读取交付状态" };
30
30
  }
31
+ else if (order.status === "failed") {
32
+ instruction = "先告诉用户这笔订单没有正常交付,不需要重复付款或重新下单;先检查原订单是否已有退款,再由用户决定是否申请。";
33
+ next = { command: `itpay refund list --order ${order.order_id} --json`, reason: "检查同一订单的退款状态" };
34
+ }
35
+ else if (order.status === "refunded") {
36
+ instruction = "先告诉用户这笔订单已经退款,原交付不可继续读取;不要再次付款或尝试恢复旧授权。";
37
+ }
38
+ else if (order.status === "cancelled") {
39
+ instruction = "先告诉用户这笔订单已经取消,没有可继续的付款或交付;不要创建替代订单,除非用户另行提出新的购买。";
40
+ }
31
41
  else if (!["delivered", "refunded", "failed", "cancelled"].includes(order.status)) {
32
- instruction = "订单尚未进入交付终态;稍后查询同一订单,不要创建替代订单。";
42
+ instruction = "先告诉用户订单仍在处理,已记录的付款和订单不需要重复创建;稍后查询同一订单,不要创建替代订单。";
33
43
  next = { command: `itpay order ${order.order_id} --json`, reason: "刷新订单状态" };
34
44
  }
35
45
  return {