@alipay/alipay-aipay 1.5.1 → 1.5.2

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 (33) hide show
  1. package/package.json +3 -2
  2. package/skill/SKILL.md +1 -1
  3. package/skill/VERSION +1 -1
  4. package/skill/references/integration/flow.md +51 -73
  5. package/skill/references/integration/modules/checklist.md +3 -3
  6. package/skill/references/integration/modules/product-decision.md +1 -1
  7. package/skill/references/integration/modules/sandbox/a2m-sandbox-test.md +11 -22
  8. package/skill/references/integration/modules/sandbox/alipay-sandbox-tool.md +1 -1
  9. package/skill/references/integration/modules/sandbox/sandbox-setup-guide.md +4 -4
  10. package/skill/references/normal/alipay-cli-env.md +2 -2
  11. package/skill/references/normal/full-process-routing.md +17 -17
  12. package/skill/references/normal/json/customer-messages.json +13 -14
  13. package/skill/references/normal/rejection-guide.md +1 -1
  14. package/skill/references/normal/scripts/alipay_cli_env.mjs +8 -4
  15. package/skill/references/normal/scripts/flow_loader.mjs +2 -2
  16. package/skill/references/normal/scripts/integration_context_runner.mjs +3 -3
  17. package/skill/references/normal/scripts/integration_message_runner.mjs +3 -3
  18. package/skill/references/normal/scripts/interaction_reply.mjs +3 -3
  19. package/skill/references/normal/scripts/onboarding_discovery_runner.mjs +3 -3
  20. package/skill/references/normal/scripts/onboarding_message_runner.mjs +3 -3
  21. package/skill/references/normal/scripts/onboarding_recovery_runner.mjs +9 -7
  22. package/skill/references/normal/scripts/python_script_runner.mjs +10 -8
  23. package/skill/references/normal/scripts/render_customer_message.mjs +68 -59
  24. package/skill/references/normal/scripts/runtime.mjs +133 -0
  25. package/skill/references/normal/scripts/self_update.mjs +8 -6
  26. package/skill/references/normal/scripts/telemetry.mjs +27 -26
  27. package/skill/references/normal/self-update.md +2 -2
  28. package/skill/references/normal/telemetry.md +5 -5
  29. package/skill/references/onboarding/flow.md +67 -80
  30. package/skill/references/onboarding/modules/app-release.md +5 -5
  31. package/skill/references/onboarding/modules/authorization.md +1 -1
  32. package/skill/references/onboarding/modules/service-registration.md +4 -4
  33. package/skill/references/runtime-entry.md +30 -30
@@ -554,126 +554,126 @@ function validateVariantInputShape(messageId, variantName, variables) {
554
554
 
555
555
  const managedEntrypoints = {
556
556
  'product.unsupported': {
557
- command: 'node scripts/integration_message_runner.mjs product-unsupported --product-name <产品名>',
557
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message product-unsupported --product-name <产品名>',
558
558
  reason: '不支持产品拒绝引导必须由 runner 接收产品名后托管输出,禁止 jq/printf 管道直调 renderer。'
559
559
  },
560
560
  'product.clarify': {
561
- command: 'node ../normal/scripts/onboarding_message_runner.mjs product-clarify,或代码开发上下文执行 node ../normal/scripts/integration_message_runner.mjs product-clarify',
561
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message product-clarify,或代码开发上下文使用同一入口执行 integration-message product-clarify',
562
562
  reason: '产品澄清属于通用托管消息,禁止用 printf/renderer 管道直接输出。'
563
563
  },
564
564
  'onboarding.enterprise.unsupported': {
565
- command: 'node ../normal/scripts/onboarding_message_runner.mjs enterprise-unsupported --variant <ONBOARDING_ONLY|FULL_PROCESS>',
565
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message enterprise-unsupported --variant <ONBOARDING_ONLY|FULL_PROCESS>',
566
566
  reason: '企业账号停止分支必须由 Onboarding runner 输出,禁止空 JSON 管道直调 renderer。'
567
567
  },
568
568
  'onboarding.windows.redirect': {
569
- command: 'node ../normal/scripts/onboarding_message_runner.mjs platform-entry',
569
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message platform-entry',
570
570
  reason: 'Windows 产品开通入口必须由 runner 根据真实运行平台确定;非 Windows 不得输出该消息。'
571
571
  },
572
572
  'integration.start.confirm': {
573
- command: 'node ../normal/scripts/integration_message_runner.mjs start-confirm --product-type <aipay|webpay|apppay> --project-path <absPath> --project-selection <CURRENT_PROJECT|OTHER_PROJECT|PREPARED_NEW_PROJECT> --language <language> --framework <framework>',
574
- reason: '代码开发启动确认必须绑定 runner 同一次项目扫描的项目来源、代码状态和其他支付产品,禁止手写项目扫描事实。'
573
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message start-confirm --product-type <aipay|webpay|apppay> --project-path <absPath> --project-selection <CURRENT_PROJECT|OTHER_PROJECT|PREPARED_NEW_PROJECT> --language <language> --framework <framework>',
574
+ reason: '代码开发启动方案必须绑定 runner 同一次项目扫描的项目来源、代码状态和其他支付产品,禁止手写项目扫描事实。'
575
575
  },
576
576
  'integration.checklist.result': {
577
- command: 'node ../normal/scripts/integration_message_runner.mjs checklist-result --variant <AIPAY|WEBPAY|APPPAY> --project-path <absPath> --scope-receipt <receipt> --overall-result <result> --failed-items <items> --manual-items <items> ...',
577
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message checklist-result --variant <AIPAY|WEBPAY|APPPAY> --project-path <absPath> --scope-receipt <receipt> --overall-result <result> --failed-items <items> --manual-items <items> ...',
578
578
  reason: '最终 checklist 结论必须由 runner 统一接收最终证据并调用 renderer,同一次代码开发只输出一次。'
579
579
  },
580
580
  'integration.project_candidates.select': {
581
- command: 'node ../normal/scripts/integration_message_runner.mjs locate-projects --base-path <absDir> --search-input <current|relativeSubdir|absDir> --format message',
581
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message locate-projects --base-path <absDir> --search-input <current|relativeSubdir|absDir> --format message',
582
582
  reason: '项目候选目录必须由 runner 轻量定位项目标记后统一输出,禁止手写候选列表或扫描整个大目录。'
583
583
  },
584
584
  'onboarding.mcc.clarify': {
585
- command: 'node ../normal/scripts/onboarding_message_runner.mjs mcc-clarify --product-type <aipay|webpay|apppay>',
585
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message mcc-clarify --product-type <aipay|webpay|apppay>',
586
586
  reason: '产品已明确但用户尚未提供 MCC/经营描述时由 runner 绑定产品名称,禁止手写经营类目补问;检索无匹配时不得回到本消息。'
587
587
  },
588
588
  'onboarding.mcc.no_match': {
589
- command: 'node ../normal/scripts/onboarding_message_runner.mjs mcc-candidates --query <business-description> --product-type <aipay|webpay|apppay>',
589
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message mcc-candidates --query <business-description> --product-type <aipay|webpay|apppay>',
590
590
  reason: '用户已经提供经营描述但当前 MCC 表无确定匹配时,由 runner 输出独立恢复路径,禁止重新渲染初始 mcc-clarify 形成循环。'
591
591
  },
592
592
  'onboarding.mcc.candidates': {
593
- command: 'node ../normal/scripts/onboarding_message_runner.mjs mcc-candidates --query <business-description> --product-type <aipay|webpay|apppay>',
593
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message mcc-candidates --query <business-description> --product-type <aipay|webpay|apppay>',
594
594
  reason: '产品已明确且 MCC 存在多个相似候选时由 runner 绑定本地 MCC 表生成候选事实,禁止手写候选表。'
595
595
  },
596
596
  'onboarding.discovery.summary': {
597
- command: 'node ../normal/scripts/onboarding_discovery_runner.mjs run --product-type <aipay|webpay|apppay> --sales-code <SALES_CODE>',
597
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-discovery run --product-type <aipay|webpay|apppay> --sales-code <SALES_CODE>',
598
598
  reason: '该 runner 执行本轮适用查询、校验原始脚本事实、输出唯一 DISCOVERY_RESULT envelope,并在同一次 stdout 生成摘要;禁止单独重放摘要。'
599
599
  },
600
600
  'onboarding.plan.confirm': {
601
- command: 'node ../normal/scripts/onboarding_message_runner.mjs mcc-resolve-plan --query <经营描述|MCC编码|MCC名称> --product-type <aipay|webpay|apppay>,或已持有唯一 MCC 事实时执行 plan-confirm --product-type <...> --mcc-code <...> --mcc-name <...> --mcc-qualification <...>',
602
- reason: '方案确认必须由 runner 绑定本轮唯一 MCC 事实和产品变体生成,避免 Agent 用 jq/printf/管道手写 renderer 输入。'
601
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message mcc-resolve-plan --query <经营描述|MCC编码|MCC名称> --product-type <aipay|webpay|apppay>,或已持有唯一 MCC 事实时使用同一入口执行 onboarding-message plan-confirm --product-type <...> --mcc-code <...> --mcc-name <...> --mcc-qualification <...>',
602
+ reason: '方案展示必须由 runner 绑定本轮唯一 MCC 事实和产品变体生成,避免 Agent 用 jq/printf/管道手写 renderer 输入。'
603
603
  },
604
604
  'materials.category.collect': {
605
- command: 'node ../normal/scripts/onboarding_message_runner.mjs material-collect --category <signing|service|application> --state <INITIAL|PARTIAL|INVALID|APP_MOBILE_INITIAL> --product-type <aipay|webpay|apppay> [--missing-fields <字段列表>]',
605
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message material-collect --category <signing|service|application> --state <INITIAL|PARTIAL|INVALID|APP_MOBILE_INITIAL> --product-type <aipay|webpay|apppay> [--missing-fields <字段列表>]',
606
606
  reason: '运行时禁止手写 categoryName/currentStatus/missingFields;runner 会归一化“签约资料”“AI 网页应用收款签约截图”“mobilePlatform”等常见别名后再调用 renderer。'
607
607
  },
608
608
  'service.create.summary': {
609
- command: 'node ../normal/scripts/onboarding_message_runner.mjs service-create-summary --service-name <name> --service-description <desc> --service-url <url> --service-pricing <amount> --service-schema <json>',
609
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message service-create-summary --service-name <name> --service-description <desc> --service-url <url> --service-pricing <amount> --service-schema <json>',
610
610
  reason: '服务创建摘要必须由 runner 接收已校验五项资料后托管输出,禁止 jq/printf 管道直调 renderer。'
611
611
  },
612
612
  'onboarding.write.confirm': {
613
- command: 'node ../normal/scripts/onboarding_message_runner.mjs service-update-confirm --session-summary <summary> --subject-summary <summary> --service-id <id> --service-name <name> --service-description <desc> --service-url <url> --service-pricing <amount> --service-schema <json>',
613
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message service-update-confirm --session-summary <summary> --subject-summary <summary> --service-id <id> --service-name <name> --service-description <desc> --service-url <url> --service-pricing <amount> --service-schema <json>',
614
614
  reason: '服务修改确认必须由 runner 固定产品和动作,只绑定当前服务修改摘要,禁止 Agent 自行拼 actionTypes。'
615
615
  },
616
616
  'signing.operation.result': {
617
- command: 'node ../normal/scripts/onboarding_message_runner.mjs signing-result --actual-status <status> [--fee-display <fee>] --next-action <action>',
617
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message signing-result --actual-status <status> [--fee-display <fee>] --next-action <action>',
618
618
  reason: '签约分支结果必须由 Onboarding runner 输出,避免直调 renderer 或手写状态。'
619
619
  },
620
620
  'service.operation.result': {
621
- command: 'node ../normal/scripts/onboarding_message_runner.mjs service-result --service-id <id> --actual-result <result>',
621
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message service-result --service-id <id> --actual-result <result>',
622
622
  reason: '服务分支结果必须由 Onboarding runner 输出,避免直调 renderer 或手写状态。'
623
623
  },
624
624
  'application.harmony.manual_create': {
625
- command: 'node ../normal/scripts/onboarding_message_runner.mjs application-harmony-manual-create',
625
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message application-harmony-manual-create',
626
626
  reason: 'HarmonyOS 移动应用边界必须由 Onboarding runner 输出,禁止空 JSON 管道直调 renderer。'
627
627
  },
628
628
  'application.reuse.no_key': {
629
- command: 'node ../normal/scripts/onboarding_message_runner.mjs application-reuse-no-key --app-id <appId>',
629
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message application-reuse-no-key --app-id <appId>',
630
630
  reason: '复用应用缺公钥提示必须由 runner 绑定当前完整 appId 后输出。'
631
631
  },
632
632
  'application.operation.result': {
633
- command: 'node ../normal/scripts/onboarding_message_runner.mjs application-result --app-id <id> --actual-status <status> --next-action <action>',
633
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message application-result --app-id <id> --actual-status <status> --next-action <action>',
634
634
  reason: '应用分支结果必须由 Onboarding runner 输出,避免直调 renderer 或手写状态。'
635
635
  },
636
636
  'application.candidates.facts': {
637
- command: 'node ../normal/scripts/onboarding_message_runner.mjs application-candidates --application-type <WEBAPP|MOBILEAPP> --candidates-json <APP_CANDIDATES_JSON>',
637
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message application-candidates --application-type <WEBAPP|MOBILEAPP> --candidates-json <APP_CANDIDATES_JSON>',
638
638
  reason: '应用候选事实和适用选择提示必须由 runner 接收结构化候选数组并统一生成,禁止脚本或 Agent 拼接 Markdown 行。'
639
639
  },
640
640
  'application.candidate.select': {
641
- command: 'node ../normal/scripts/onboarding_message_runner.mjs application-candidates --application-type <WEBAPP|MOBILEAPP> --candidates-json <APP_CANDIDATES_JSON>',
641
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message application-candidates --application-type <WEBAPP|MOBILEAPP> --candidates-json <APP_CANDIDATES_JSON>',
642
642
  reason: '应用候选选择必须由 runner 绑定同一份结构化候选快照生成,禁止脚本或 Agent 单独调用。'
643
643
  },
644
644
  'service.candidates.facts': {
645
- command: 'node ../normal/scripts/onboarding_message_runner.mjs service-candidates --candidates-json <SERVICE_CANDIDATES_JSON>',
645
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message service-candidates --candidates-json <SERVICE_CANDIDATES_JSON>',
646
646
  reason: '服务候选事实和适用选择提示必须由 runner 接收结构化候选数组并统一生成,禁止脚本或 Agent 拼接 Markdown 行。'
647
647
  },
648
648
  'service.candidate.select': {
649
- command: 'node ../normal/scripts/onboarding_message_runner.mjs service-candidates --candidates-json <SERVICE_CANDIDATES_JSON>',
649
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message service-candidates --candidates-json <SERVICE_CANDIDATES_JSON>',
650
650
  reason: '服务候选选择必须由 runner 绑定同一份结构化候选快照生成,禁止脚本或 Agent 单独调用。'
651
651
  },
652
652
  'process.partial_result': {
653
- command: 'node ../normal/scripts/onboarding_message_runner.mjs closeout --product-name <name> --process-result-title <title> --integration-result <result> --signing-result <result> --service-result <result> --application-result <result> --remaining-actions <actions> --next-flow-reminder <reminder>',
653
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message closeout --product-name <name> --process-result-title <title> --integration-result <result> --signing-result <result> --service-result <result> --application-result <result> --remaining-actions <actions> --next-flow-reminder <reminder>',
654
654
  reason: '产品开通 Step 6 最终收口必须由 runner 统一合并代码开发状态、产品开通分支状态、最近应用分支结果和上线前关键点;禁止 Agent 手写、缩写或删减 AI 网页应用收款限制引导。'
655
655
  },
656
656
  'sandbox.android.client': {
657
- command: 'node ../normal/scripts/integration_message_runner.mjs sandbox-android-client',
657
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message sandbox-android-client',
658
658
  reason: '沙箱支付宝 Android 下载说明必须由 Integration runner 输出,避免 Agent 为静态消息直调 renderer 或追加 shell 过滤。'
659
659
  },
660
660
  'auth.page': {
661
- command: 'bash modules/scripts/auth.sh init --defer-open --scope <scope> --sales-code <code> --mcc-code <code> --product-name <name> --mcc-name <name>,页面 stdout 独立送达后执行 bash modules/scripts/auth.sh open ...',
661
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" auth init --defer-open --scope <scope> --sales-code <code> --mcc-code <code> --product-name <name> --mcc-name <name>,页面 stdout 独立送达后使用同一入口执行 auth open ...',
662
662
  reason: '授权页必须由 auth.sh 绑定本次 CLI deviceCode、固定产品/MCC 上下文和 URL 白名单;受控 opener 必须在页面消息独立送达后执行,后续 auth.pending 不得合并到同一条对客回复。',
663
663
  allowedCallers: ['auth.sh']
664
664
  },
665
665
  'auth.pending': {
666
- command: 'bash modules/scripts/auth.sh wait|confirm [--scope <scope> --sales-code <code> --mcc-code <code> --product-name <name> --mcc-name <name>]',
666
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" auth wait|confirm [--scope <scope> --sales-code <code> --mcc-code <code> --product-name <name> --mcc-name <name>]',
667
667
  reason: '授权待完成状态只能来自 auth.sh 有限轮询耗尽或登记肯定回复后的单次实际查询。',
668
668
  allowedCallers: ['auth.sh']
669
669
  },
670
670
  'auth.expired': {
671
- command: 'bash modules/scripts/auth.sh confirm [--scope <scope> --sales-code <code> --mcc-code <code> --product-name <name> --mcc-name <name>]',
671
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" auth confirm [--scope <scope> --sales-code <code> --mcc-code <code> --product-name <name> --mcc-name <name>]',
672
672
  reason: '授权过期状态只能来自本轮 auth.sh confirm 对 CLI 授权状态的实际查询。',
673
673
  allowedCallers: ['auth.sh']
674
674
  },
675
675
  'auth.mismatch': {
676
- command: 'bash modules/scripts/auth.sh mismatch --defer-open [--scope <scope> --sales-code <code> --mcc-code <code> --product-name <name> --mcc-name <name>],页面 stdout 独立送达后执行 bash modules/scripts/auth.sh open ...',
676
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" auth mismatch --defer-open [--scope <scope> --sales-code <code> --mcc-code <code> --product-name <name> --mcc-name <name>],页面 stdout 独立送达后使用同一入口执行 auth open ...',
677
677
  reason: 'scope 或 MCC 不匹配必须由 auth.sh 执行 logout 与重新授权链路,不能只输出提示。',
678
678
  allowedCallers: ['auth.sh']
679
679
  },
@@ -703,22 +703,22 @@ const managedEntrypoints = {
703
703
  allowedCallers: ['error_handler.sh']
704
704
  },
705
705
  'application.key.page': {
706
- command: 'bash modules/scripts/app.sh key --defer-open <appId> <publicKey>,页面 stdout 独立送达后执行 bash modules/scripts/app.sh open-key-page <appId>',
706
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" app key --defer-open <appId> <publicKey>,页面 stdout 独立送达后使用同一入口执行 app open-key-page <appId>',
707
707
  reason: '公钥确认页必须由 app.sh 调用 createKeyConfirmPage 后校验官方 URL;受控 opener 必须在页面消息独立送达后执行,后续 application.key.pending 不得合并到同一条对客回复。',
708
708
  allowedCallers: ['app.sh']
709
709
  },
710
710
  'application.key.pending': {
711
- command: 'bash modules/scripts/app.sh verify-key <appId> [publicKey] 或 verify-key-and-audit <appId> [publicKey]',
711
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" app verify-key <appId> [publicKey] 或使用同一入口执行 app verify-key-and-audit <appId> [publicKey]',
712
712
  reason: '公钥确认超时提示只能来自 app.sh 完成有限次数的实际公钥状态查询后。',
713
713
  allowedCallers: ['app.sh']
714
714
  },
715
715
  'key_tool.download.result': {
716
- command: 'bash modules/scripts/download_key_tool.sh [downloadDir]',
716
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" key-tool [downloadDir]',
717
717
  reason: '密钥工具下载成功消息必须来自下载脚本的实际下载路径、跳转校验和安装包校验结果。',
718
718
  allowedCallers: ['download_key_tool.sh']
719
719
  },
720
720
  'key_tool.download.fallback': {
721
- command: 'bash modules/scripts/download_key_tool.sh [downloadDir]',
721
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" key-tool [downloadDir]',
722
722
  reason: '密钥工具下载失败或不支持时必须由下载脚本统一给出官方手动下载兜底。',
723
723
  allowedCallers: ['download_key_tool.sh']
724
724
  }
@@ -837,6 +837,9 @@ export function validateCatalog() {
837
837
  if (typeof inputHints.confirmInputHint !== 'string' || !inputHints.confirmInputHint) {
838
838
  errors.push('缺少统一确认输入提示');
839
839
  }
840
+ if (typeof inputHints.planAdjustmentHint !== 'string' || !inputHints.planAdjustmentHint) {
841
+ errors.push('缺少统一方案调整提示');
842
+ }
840
843
  if (typeof inputHints.authorizationCompletionInputHint !== 'string' || !inputHints.authorizationCompletionInputHint) {
841
844
  errors.push('缺少统一授权完成输入提示');
842
845
  }
@@ -870,11 +873,11 @@ export function validateCatalog() {
870
873
  if (message.blocking && allowedReplies.length > 0 && !['confirm', 'authorizationComplete', 'publicKeyConfirmationComplete', 'complete'].includes(message.inputHintType)) {
871
874
  errors.push(`${message.messageId}: 阻塞回复消息必须声明合法 inputHintType`);
872
875
  }
873
- if (message.postRenderHintType !== undefined && message.postRenderHintType !== 'scanReturn') {
876
+ if (message.postRenderHintType !== undefined && !['planAdjustment', 'scanReturn'].includes(message.postRenderHintType)) {
874
877
  errors.push(`${message.messageId}: postRenderHintType 无效`);
875
878
  }
876
- if (message.postRenderHintType === 'scanReturn' && (message.blocking || JSON.stringify(message.allowedReplies || []) !== '[]')) {
877
- errors.push(`${message.messageId}: scanReturn 只允许用于非阻塞且无回复枚举的消息`);
879
+ if (message.postRenderHintType && (message.blocking || JSON.stringify(message.allowedReplies || []) !== '[]')) {
880
+ errors.push(`${message.messageId}: postRenderHintType 只允许用于非阻塞且无回复枚举的消息`);
878
881
  }
879
882
  for (const id of message.requiredPolicyIds || []) {
880
883
  if (!policyIds.has(id)) errors.push(`${message.messageId}: 未知 policyId ${id}`);
@@ -1247,9 +1250,15 @@ export function renderMessage(messageId, variantName, variables, telemetryContex
1247
1250
  if (!hint) throw new Error(`缺少 ${hintKey}`);
1248
1251
  rendered = `${rendered}\n\n${hint}`;
1249
1252
  }
1250
- if (message.postRenderHintType === 'scanReturn') {
1251
- const hint = catalog.interactionPrompts?.scanReturnHint;
1252
- if (!hint) throw new Error('缺少 scanReturnHint');
1253
+ if (message.postRenderHintType) {
1254
+ const hintKeys = {
1255
+ planAdjustment: 'planAdjustmentHint',
1256
+ scanReturn: 'scanReturnHint'
1257
+ };
1258
+ const hintKey = hintKeys[message.postRenderHintType];
1259
+ if (!hintKey) throw new Error(`${messageId} 的 postRenderHintType 非法`);
1260
+ const hint = catalog.interactionPrompts?.[hintKey];
1261
+ if (!hint) throw new Error(`缺少 ${hintKey}`);
1253
1262
  rendered = `${rendered}\n\n${hint}`;
1254
1263
  }
1255
1264
  emitMessageMetric(messageId, variantName, variables, telemetryContext);
@@ -1293,10 +1302,10 @@ async function readStdin() {
1293
1302
  return input.trim() ? JSON.parse(input) : {};
1294
1303
  }
1295
1304
 
1296
- function inputJsonArg() {
1297
- const index = process.argv.indexOf('--input-json');
1305
+ function inputJsonArg(argv = process.argv) {
1306
+ const index = argv.indexOf('--input-json');
1298
1307
  if (index < 0) return null;
1299
- const value = process.argv[index + 1];
1308
+ const value = argv[index + 1];
1300
1309
  if (value === undefined || value.startsWith('--')) throw new Error('参数 --input-json 缺少取值');
1301
1310
  try {
1302
1311
  return JSON.parse(value);
@@ -1305,42 +1314,42 @@ function inputJsonArg() {
1305
1314
  }
1306
1315
  }
1307
1316
 
1308
- async function main() {
1309
- if (process.argv.includes('--check-catalog')) {
1317
+ export async function runRenderCustomerMessageCli(argv = process.argv) {
1318
+ if (argv.includes('--check-catalog')) {
1310
1319
  const errors = validateCatalog();
1311
1320
  if (errors.length) throw new Error(errors.join('\n'));
1312
1321
  console.log('customer message catalog: OK');
1313
1322
  return;
1314
1323
  }
1315
- if (process.argv.includes('--validate-authorization-context')) {
1324
+ if (argv.includes('--validate-authorization-context')) {
1316
1325
  validateAuthorizationContext(await readStdin());
1317
1326
  console.log('authorization context: OK');
1318
1327
  return;
1319
1328
  }
1320
- if (process.argv.includes('--build-authorization-url')) {
1329
+ if (argv.includes('--build-authorization-url')) {
1321
1330
  console.log(buildAuthorizationUrl(await readStdin()));
1322
1331
  return;
1323
1332
  }
1324
- if (process.argv.includes('--schema')) {
1325
- const schemaIndex = process.argv.indexOf('--schema');
1326
- const messageId = process.argv[2] === '--schema' ? process.argv[schemaIndex + 1] : process.argv[2];
1327
- const variantIndex = process.argv.indexOf('--variant');
1328
- const variant = variantIndex >= 0 ? process.argv[variantIndex + 1] : 'DEFAULT';
1333
+ if (argv.includes('--schema')) {
1334
+ const schemaIndex = argv.indexOf('--schema');
1335
+ const messageId = argv[2] === '--schema' ? argv[schemaIndex + 1] : argv[2];
1336
+ const variantIndex = argv.indexOf('--variant');
1337
+ const variant = variantIndex >= 0 ? argv[variantIndex + 1] : 'DEFAULT';
1329
1338
  if (!messageId || messageId.startsWith('--')) throw new Error('用法: render_customer_message.mjs --schema <messageId> [--variant NAME]');
1330
1339
  console.log(JSON.stringify(getMessageSchema(messageId, variant), null, 2));
1331
1340
  return;
1332
1341
  }
1333
- const messageId = process.argv[2];
1334
- const variantIndex = process.argv.indexOf('--variant');
1335
- const variant = variantIndex >= 0 ? process.argv[variantIndex + 1] : 'DEFAULT';
1342
+ const messageId = argv[2];
1343
+ const variantIndex = argv.indexOf('--variant');
1344
+ const variant = variantIndex >= 0 ? argv[variantIndex + 1] : 'DEFAULT';
1336
1345
  if (!messageId) throw new Error('用法: render_customer_message.mjs <messageId> [--variant NAME]');
1337
1346
  assertManagedCliAllowed(messageId);
1338
- const variables = inputJsonArg() ?? await readStdin();
1347
+ const variables = inputJsonArg(argv) ?? await readStdin();
1339
1348
  console.log(renderMessageWithFallback(messageId, variant, variables));
1340
1349
  }
1341
1350
 
1342
1351
  if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
1343
- main().catch((error) => {
1352
+ runRenderCustomerMessageCli().catch((error) => {
1344
1353
  console.error('当前步骤的标准消息无法安全生成,已停止后续动作。请按 MESSAGE_RENDER_ERROR 的具体原因修正输入;若消息目录或脚本缺失,再检查 Skill 文件完整性。');
1345
1354
  console.error(`MESSAGE_RENDER_ERROR:${error.message}`);
1346
1355
  process.exitCode = 1;
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'node:child_process';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
9
+ const referencesDir = path.resolve(scriptDir, '../..');
10
+ const flowDirs = Object.freeze({
11
+ integration: path.join(referencesDir, 'integration'),
12
+ onboarding: path.join(referencesDir, 'onboarding')
13
+ });
14
+
15
+ const shellEntrypoints = Object.freeze({
16
+ sandbox: ['integration', 'sandbox_config.sh'],
17
+ auth: ['onboarding', 'auth.sh'],
18
+ 'key-tool': ['onboarding', 'download_key_tool.sh'],
19
+ screenshots: ['onboarding', 'upload_screenshots.sh'],
20
+ sign: ['onboarding', 'ar_sign_apply.sh'],
21
+ service: ['onboarding', 'service.sh'],
22
+ app: ['onboarding', 'app.sh']
23
+ });
24
+
25
+ const nodeEntrypoints = Object.freeze({
26
+ telemetry: ['telemetry.mjs', 'runTelemetryCli'],
27
+ flow: ['flow_loader.mjs', 'runFlowCli'],
28
+ 'onboarding-message': ['onboarding_message_runner.mjs', 'runOnboardingMessageCli'],
29
+ 'integration-message': ['integration_message_runner.mjs', 'runIntegrationMessageCli'],
30
+ render: ['render_customer_message.mjs', 'runRenderCustomerMessageCli'],
31
+ reply: ['interaction_reply.mjs', 'runInteractionReplyCli'],
32
+ 'integration-context': ['integration_context_runner.mjs', 'runIntegrationContextCli'],
33
+ 'onboarding-discovery': ['onboarding_discovery_runner.mjs', 'runOnboardingDiscoveryCli'],
34
+ 'onboarding-recovery': ['onboarding_recovery_runner.mjs', 'runOnboardingRecoveryCli']
35
+ });
36
+
37
+ export const runtimeDomains = Object.freeze([
38
+ 'self-update',
39
+ ...Object.keys(nodeEntrypoints),
40
+ 'env',
41
+ ...Object.keys(shellEntrypoints),
42
+ 'a2m'
43
+ ]);
44
+
45
+ function cliArgv(args) {
46
+ return [process.execPath, fileURLToPath(import.meta.url), ...args];
47
+ }
48
+
49
+ function runShell(script, cwd, args) {
50
+ const result = spawnSync('bash', [script, ...args], {
51
+ cwd,
52
+ env: process.env,
53
+ stdio: 'inherit',
54
+ shell: false
55
+ });
56
+ if (result.error) throw result.error;
57
+ process.exitCode = Number.isInteger(result.status) ? result.status : 1;
58
+ }
59
+
60
+ function shellPath(domain) {
61
+ const [flow, basename] = shellEntrypoints[domain];
62
+ const cwd = flowDirs[flow];
63
+ return [path.join(cwd, 'modules/scripts', basename), cwd];
64
+ }
65
+
66
+ function runtimeError(domain, error) {
67
+ const message = String(error?.message || error).replace(/[\r\n]/gu, ' ');
68
+ if (domain === 'self-update') return process.stderr.write('SELF_UPDATE:CHECK_FAILED\n');
69
+ if (domain === 'render') {
70
+ process.stderr.write('当前步骤的标准消息无法安全生成,已停止后续动作。请按 MESSAGE_RENDER_ERROR 的具体原因修正输入;若消息目录或脚本缺失,再检查 Skill 文件完整性。\n');
71
+ process.stderr.write(`MESSAGE_RENDER_ERROR:${message}\n`);
72
+ return;
73
+ }
74
+ const prefixes = {
75
+ flow: 'FLOW_LOADER_ERROR:',
76
+ 'onboarding-message': 'ONBOARDING_MESSAGE_ERROR:',
77
+ 'integration-message': 'INTEGRATION_MESSAGE_ERROR:',
78
+ reply: 'ALIPAY_AIPAY_INTERNAL:REPLY_VALIDATION_ERROR=',
79
+ 'integration-context': 'ALIPAY_AIPAY_INTERNAL:INTEGRATION_CONTEXT_ERROR=',
80
+ 'onboarding-discovery': 'ALIPAY_AIPAY_INTERNAL:DISCOVERY_RUNNER_ERROR=',
81
+ 'onboarding-recovery': 'ALIPAY_AIPAY_INTERNAL:RECOVERY_RUNNER_ERROR=',
82
+ a2m: 'PYTHON_RUNNER_ERROR:'
83
+ };
84
+ process.stderr.write(`${prefixes[domain] || 'ALIPAY_AIPAY_INTERNAL:RUNTIME_ERROR='}${message}\n`);
85
+ }
86
+
87
+ async function dispatch(domain, args) {
88
+ if (shellEntrypoints[domain]) {
89
+ const [script, cwd] = shellPath(domain);
90
+ return runShell(script, cwd, args);
91
+ }
92
+ if (nodeEntrypoints[domain]) {
93
+ const [moduleName, exportName] = nodeEntrypoints[domain];
94
+ const result = await (await import(`./${moduleName}`))[exportName](cliArgv(args));
95
+ if (domain === 'telemetry') process.exitCode = result;
96
+ return;
97
+ }
98
+ if (domain === 'env') {
99
+ const { isWindowsPlatform } = await import('./platform_compat.mjs');
100
+ if (!isWindowsPlatform()) {
101
+ return runShell(path.join(scriptDir, 'alipay_cli_env.sh'), path.dirname(scriptDir), args);
102
+ }
103
+ const { runAlipayCliEnvCli } = await import('./alipay_cli_env.mjs');
104
+ return runAlipayCliEnvCli(cliArgv(args));
105
+ }
106
+ if (domain === 'self-update') {
107
+ const { runSelfUpdateCli } = await import('./self_update.mjs');
108
+ const outcome = runSelfUpdateCli(cliArgv(args));
109
+ const version = outcome.version ? `=${outcome.version.raw}` : '';
110
+ process.stderr.write(`SELF_UPDATE:${outcome.kind}${version}\n`);
111
+ return;
112
+ }
113
+ if (domain === 'a2m') {
114
+ const { runPythonScriptCli } = await import('./python_script_runner.mjs');
115
+ const script = path.join(flowDirs.integration, 'modules/scripts/local_402_sandbox_pay.py');
116
+ return runPythonScriptCli(cliArgv([script, ...args]));
117
+ }
118
+ throw new Error(`用法: runtime.mjs <${runtimeDomains.join('|')}> ...`);
119
+ }
120
+
121
+ export async function runRuntimeCli(argv = process.argv) {
122
+ const domain = argv[2] || '';
123
+ try {
124
+ await dispatch(domain, argv.slice(3));
125
+ } catch (error) {
126
+ runtimeError(domain, error);
127
+ process.exitCode = domain === 'self-update' ? 0 : 1;
128
+ }
129
+ }
130
+
131
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
132
+ runRuntimeCli();
133
+ }
@@ -127,8 +127,8 @@ function reportOutcome(outcome) {
127
127
  process.stderr.write(`SELF_UPDATE:${outcome.kind}${version}\n`);
128
128
  }
129
129
 
130
- function main() {
131
- const skillDir = parseArgs(process.argv);
130
+ export function runSelfUpdateCli(argv = process.argv) {
131
+ const skillDir = parseArgs(argv);
132
132
  const local = readLocalVersion(skillDir);
133
133
 
134
134
  if (!local) {
@@ -153,8 +153,10 @@ function main() {
153
153
  return { kind: installed ? 'VERIFY_FAILED' : 'INSTALL_FAILED' };
154
154
  }
155
155
 
156
- try {
157
- reportOutcome(main());
158
- } catch {
159
- reportOutcome({ kind: 'CHECK_FAILED' });
156
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
157
+ try {
158
+ reportOutcome(runSelfUpdateCli());
159
+ } catch {
160
+ reportOutcome({ kind: 'CHECK_FAILED' });
161
+ }
160
162
  }
@@ -449,7 +449,7 @@ export function emitStageMetricAsync(stage, context = {}, env = process.env) {
449
449
  export function emitMessageMetric(messageId, variantName, variables = {}, context = {}) {
450
450
  try {
451
451
  const event = messageMetricEvent(messageId, variantName, variables, context);
452
- if (event?.stage === 'run.terminal' && telemetryEnabled()) updateExistingRunStateFromEvent(event);
452
+ if (event?.stage === 'run.terminal') updateExistingRunStateFromEvent(event);
453
453
  return emitMetricEvent(event, { stream: null });
454
454
  } catch {
455
455
  return false;
@@ -458,7 +458,6 @@ export function emitMessageMetric(messageId, variantName, variables = {}, contex
458
458
 
459
459
  export function emitTerminalMetric(context = {}) {
460
460
  try {
461
- if (!telemetryEnabled()) return false;
462
461
  const event = stageMetricEvent('run.terminal', context);
463
462
  if (!event) return false;
464
463
  updateExistingRunStateFromEvent(event);
@@ -1251,6 +1250,32 @@ function emitTelemetryError(error) {
1251
1250
  export async function runTelemetryCli(argv = process.argv) {
1252
1251
  try {
1253
1252
  const { command, args } = parseCliArgs(argv);
1253
+ if (command === 'start-run') {
1254
+ assertCliArgs(args, new Set(['root-intent', 'product-type', 'run-id']), 0);
1255
+ const state = createOrReuseRunState(args);
1256
+ if (!writeRunState(state) && telemetryEnabled(process.env)) {
1257
+ recordLocalTelemetryFailure('LOCAL_RUN_STATE_WRITE_FAILED', 'failed to write local telemetry run state');
1258
+ }
1259
+ if (telemetryEnabled(process.env)) {
1260
+ const event = stageMetricEvent('run.intent_detected', {
1261
+ runId: state.run_id,
1262
+ rootIntent: state.root_intent,
1263
+ productType: state.product_type
1264
+ });
1265
+ emitMetricEvent(event);
1266
+ }
1267
+ return 0;
1268
+ }
1269
+ if (command === 'current-run-id') {
1270
+ assertCliArgs(args, new Set(), 0);
1271
+ const state = canReadImplicitRunState(process.env) ? readRunState(process.env) : null;
1272
+ if (state?.run_id) {
1273
+ process.stderr.write(`${internalPrefix}RUN_ID=${state.run_id}\n`);
1274
+ } else {
1275
+ process.stderr.write(`${internalPrefix}RUN_ID_MISSING=true\n`);
1276
+ }
1277
+ return 0;
1278
+ }
1254
1279
  if (command === 'upload-status' && args._.length === 0) {
1255
1280
  assertCliArgs(args, new Set(), 0);
1256
1281
  const status = readUploadStatus(process.env) || { schema_ver: 1, status: 'unknown' };
@@ -1280,30 +1305,6 @@ export async function runTelemetryCli(argv = process.argv) {
1280
1305
  return 0;
1281
1306
  }
1282
1307
  if (!telemetryEnabled(process.env)) return 0;
1283
- if (command === 'start-run') {
1284
- assertCliArgs(args, new Set(['root-intent', 'product-type', 'run-id']), 0);
1285
- const state = createOrReuseRunState(args);
1286
- if (!writeRunState(state)) {
1287
- recordLocalTelemetryFailure('LOCAL_RUN_STATE_WRITE_FAILED', 'failed to write local telemetry run state');
1288
- }
1289
- const event = stageMetricEvent('run.intent_detected', {
1290
- runId: state.run_id,
1291
- rootIntent: state.root_intent,
1292
- productType: state.product_type
1293
- });
1294
- emitMetricEvent(event);
1295
- return 0;
1296
- }
1297
- if (command === 'current-run-id') {
1298
- assertCliArgs(args, new Set(), 0);
1299
- const state = canReadImplicitRunState(process.env) ? readRunState(process.env) : null;
1300
- if (state?.run_id) {
1301
- process.stderr.write(`${internalPrefix}RUN_ID=${state.run_id}\n`);
1302
- } else {
1303
- process.stderr.write(`${internalPrefix}RUN_ID_MISSING=true\n`);
1304
- }
1305
- return 0;
1306
- }
1307
1308
  if (command !== 'stage' || args._.length !== 1) {
1308
1309
  emitTelemetryError(new Error('invalid telemetry arguments'));
1309
1310
  return 0;
@@ -3,10 +3,10 @@
3
3
  根 `SKILL.md` 是跨 Agent 的最小启动器。本次 Agent 对话首次使用 `alipay-aipay` 时,必须在读取 `references/runtime-entry.md`、flow、模块、消息目录或业务脚本前只检查一次版本。执行前把根 `SKILL.md` 所在目录解析为规范化绝对路径 `<SKILL_DIR>`,并在当前 Agent 上下文记录一次性事实 `ALIPAY_AIPAY_SELF_UPDATE_CHECKED=true`;不写文件、不跨对话保存,也不是业务进度。同一对话内后续根流程、确认、补材料、候选选择、错误恢复、中断后继续、重复读取本 Skill 和 Full Process 子流程衔接均不重查。`<SKILL_DIR>` 是执行时替换的路径占位符,固定命令为:
4
4
 
5
5
  ```text
6
- node "<SKILL_DIR>/references/normal/scripts/self_update.mjs" check --skill-dir "<SKILL_DIR>"
6
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" self-update check --skill-dir "<SKILL_DIR>"
7
7
  ```
8
8
 
9
- `<SKILL_DIR>` 必须是刚触发的本份 `SKILL.md` 所在目录,且该目录包含 `SKILL.md`、`VERSION` 和 `references/runtime-entry.md`;不得换成 Agent 启动目录、用户项目目录、用户 Home、npm 包源码根或另一份 Skill 副本。禁止把固定命令改写成 shell 变量赋值、命令替换或依赖 Bash、PowerShell、CMD 的专属语法。若执行器支持设置 workdir,也可以从本文所在目录 `references/normal/` 执行 `node scripts/self_update.mjs check`;两种写法必须绑定同一份当前 Skill。
9
+ `<SKILL_DIR>` 必须是刚触发的本份 `SKILL.md` 所在目录,且该目录包含 `SKILL.md`、`VERSION` 和 `references/runtime-entry.md`;不得换成 Agent 启动目录、用户项目目录、用户 Home、npm 包源码根或另一份 Skill 副本。禁止把固定命令改写成 shell 变量赋值、命令替换、相对路径或依赖 Bash、PowerShell、CMD 的专属语法;只允许执行上方统一 runtime 单命令。
10
10
 
11
11
  结果与规则:
12
12
 
@@ -5,21 +5,21 @@
5
5
  ## 运行契约
6
6
 
7
7
  - 固定 stage 枚举、Yuyan 字段和低敏维度以 `scripts/telemetry.mjs` 的 `approvedStages`、事件构造器和测试为准。
8
- - `start-run` 创建或复用 `run_id`;动作 runner/script 使用异步子进程记录 stage。缺少 run、遥测关闭、落盘失败、网络失败和非 2xx 都不得改变业务 stdout、退出语义或 flow 转移。
8
+ - `start-run` 始终创建或复用当前流程 `run_id`;该私有 run context 是 alipay-cli `PLATFORM_ID` 和终态生命周期的运行事实,不依赖事件采集开关。动作 runner/script 在遥测开启时使用异步子进程记录 stage。缺少 run、遥测关闭、落盘失败、网络失败和非 2xx 都不得改变业务 stdout、退出语义或 flow 转移。
9
9
  - 前台只写用户系统临时目录下带 `alipay-aipay` 标识的私有 outbox,并启动后台补传;collector 返回 HTTP 2xx 后才删除记录。维护/测试进程不复用默认 run state,默认不远端上传。
10
- - `PLATFORM_ID` 必须等于当前 `run_id`;模型维度只读 `AIPAY_MODEL`,禁止读取用户项目通用 `MODEL` 推断。
10
+ - `PLATFORM_ID` 必须等于当前流程 `run_id`,遥测开关不得令其缺失或改变;模型维度只读 `AIPAY_MODEL`,禁止读取用户项目通用 `MODEL` 推断。
11
11
  - 遥测内部行统一使用 `ALIPAY_AIPAY_INTERNAL:` 前缀,禁止对客。
12
12
 
13
13
  ## Run 生命周期
14
14
 
15
15
  - 根入口用 `root_intent + product_type` 判定复用或新建;组合不变时不按经过时长失效,`UNKNOWN` 补全为明确产品仍复用同一 `run_id`。只有明确换产品、切换根意图或已有 run 终态才重建;用户只说“重新开始/重新走流程/作废本轮流程”但组合没有变化时继续复用。
16
- - 若宿主提供 `COZE_PROJECT_ID`、`MEOO_PROJECT_ID`,或 `MIAODA` 环境下的 `VITE_APP_ID`/`PLATFORM_ID`,新建 run 直接使用该项目 ID。当前宿主项目 ID 与已有状态不兼容时静默跳过不兼容事件且不改写 run state。
16
+ - 若宿主提供 `COZE_PROJECT_ID`、`MEOO_PROJECT_ID`,或 `MIAODA` 环境下的 `VITE_APP_ID`/`PLATFORM_ID`,新建 run 优先使用该项目 ID,不得被显式 run ID 或本地生成值覆盖。当前宿主项目 ID 与已有状态不兼容时静默跳过不兼容事件且不改写 run state。
17
17
  - `integration_only` 由 checklist 最终消息终止;`full_process` 的 Integration 收口不终止,和 `onboarding_only` 一样由 Onboarding closeout 终止。
18
- - 查看当前主键只使用 `current-run-id`,不得为查看而新建 run。业务脚本继续继承 `PLATFORM_ID=<run_id>`;Agent 不手写环境变量前缀或改写 MCP payload。
18
+ - 查看当前主键只使用 `current-run-id`,不得为查看而新建 run。关闭遥测只停止 `METRIC_EVENT`、outbox、上传和 flush,不停止 run context 的创建、读取与终态记账;业务脚本继续继承 `PLATFORM_ID=<run_id>`。Agent 不手写环境变量前缀或改写 MCP payload。
19
19
 
20
20
  ## 诊断与补传
21
21
 
22
- `node scripts/telemetry.mjs upload-status` 只读取内部诊断状态。网络类失败且 outbox 有积压时,先执行 `node scripts/telemetry.mjs permissioned-flush-needed`;只有内部事实为 `PERMISSIONED_FLUSH_NEEDED=true` 时才申请一次可复用联网权限运行 `node scripts/telemetry.mjs flush-outbox`。同一 Agent 会话只请求一次;已取得权限后可在后续阶段旁路触发同一异步命令。`flush-outbox` 只启动后台补传并立即返回;用户拒绝、后台失败或非 2xx 都保留 outbox 并继续业务。collector HTTP 结果、失败或待重试结果不得作为业务转移或完成条件。
22
+ `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" telemetry upload-status` 只读取内部诊断状态。网络类失败且 outbox 有积压时,先执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" telemetry permissioned-flush-needed`;只有内部事实为 `PERMISSIONED_FLUSH_NEEDED=true` 时才申请一次可复用联网权限运行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" telemetry flush-outbox`。同一 Agent 会话只请求一次;已取得权限后可在后续阶段旁路触发同一异步命令。`flush-outbox` 只启动后台补传并立即返回;用户拒绝、后台失败或非 2xx 都保留 outbox 并继续业务。collector HTTP 结果、失败或待重试结果不得作为业务转移或完成条件。
23
23
 
24
24
  ## 修改要求
25
25