@alipay/alipay-aipay 1.6.0-beta → 1.6.0-beta.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 (21) hide show
  1. package/package.json +1 -1
  2. package/skill/VERSION +1 -1
  3. package/skill/references/integration/flow.md +22 -11
  4. package/skill/references/integration/modules/code-examples/csharp/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.cs +12 -8
  5. package/skill/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +3 -2
  6. package/skill/references/integration/modules/code-examples/nodejs/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.js +15 -6
  7. package/skill/references/integration/modules/code-examples/php/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.php +36 -19
  8. package/skill/references/integration/modules/code-examples/python/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.py +9 -9
  9. package/skill/references/integration/modules/sandbox/alipay-sandbox-tool.md +4 -1
  10. package/skill/references/integration/modules/scripts/sandbox_config.sh +3 -2
  11. package/skill/references/normal/alipay-cli-env.md +2 -2
  12. package/skill/references/normal/full-process-routing.md +11 -5
  13. package/skill/references/normal/json/customer-messages.json +1 -1
  14. package/skill/references/normal/scripts/common.sh +21 -0
  15. package/skill/references/normal/scripts/integration_message_runner.mjs +65 -23
  16. package/skill/references/normal/scripts/onboarding_message_runner.mjs +4 -0
  17. package/skill/references/onboarding/flow.md +5 -5
  18. package/skill/references/onboarding/modules/scripts/auth.sh +1 -1
  19. package/skill/references/onboarding/modules/scripts/upload_screenshots.sh +4 -4
  20. package/skill/references/onboarding/modules/service-registration.md +1 -1
  21. package/skill/references/runtime-entry.md +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alipay/alipay-aipay",
3
- "version": "1.6.0-beta",
3
+ "version": "1.6.0-beta.2",
4
4
  "description": "支付宝 AI 付 Skill 安装包 - 优先安装 Agent Skill,并在 macOS/Linux 后台准备用户级 CLI 工具",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/VERSION CHANGED
@@ -1 +1 @@
1
- 1.6.0-beta
1
+ 1.6.0-beta.2
@@ -85,15 +85,23 @@
85
85
 
86
86
  ### 目标项目执行上下文
87
87
 
88
- 进入步骤 1 前先确定唯一的规范化目标项目根目录和服务端语言,后续所有用户项目检测、依赖安装、代码与配置写入、服务启动和项目校验命令都必须以该目录为工作目录。`PROJECT_INPUT` 必须保留用户选择项目时的原始表达,不得先改写成“当前项目/当前目录”。用户不需要手写绝对路径;可以选择新建项目,或复用已有项目。新建项目可以说明尚不存在或为空的新目录;若用户明确要新建但不想指定位置,runner 使用用户目录下的默认安全目录并返回规范化地址。复用已有项目必须说明具体项目文件夹,或先让 Agent 在当前目录或用户给出的子目录下定位候选项目。“当前项目/当前目录”只有在 runner 确认当前目录本身是可识别项目根时才接受;否则不得把大目录交给代码扫描。Agent 必须通过统一 runtime 的 `integration-message resolve-project` 或 `locate-projects` 取得规范化事实,再继续检查器和方案展示链路。Skill 内置 renderer、脚本和参考文件按上方规则从当前 Skill 目录定位,不受目标项目 cwd 影响。禁止因为 Agent 的原始工作目录更方便而改用其他项目,也禁止把临时目录中的产物误报为目标项目产物。
88
+ 步骤 1 前先确定唯一项目根和服务端语言,所有项目检测、安装、写入、启动和校验都以该目录为 workdir。保留原始 `PROJECT_INPUT`;新项目含名称或位置时由 Agent 解析 `TARGET_PROJECT_PATH`,runner 只校验,不解析原话或把整句当目录;纯默认新建才使用安全默认目录。复用项目未知时先定位候选,“当前项目/当前目录”仅在 runner 识别为项目根时接受。所有路径经统一 runtime 的 `resolve-project`/`locate-projects` 取得规范化事实;Skill 自有文件仍从当前 Skill 目录读取。禁止改用方便的其他项目或把临时产物算作目标项目产物。
89
89
 
90
90
  - `full_process`:步骤 1 前置准备先继承 `../normal/full-process-routing.md` 已选定的项目来源、规范化项目路径和服务端语言,此时代码开发启动方案尚未展示,不得要求目标产品或代码状态;产品确定、项目扫描并完成唯一方案展示后,进入步骤 2 时继承同一摘要中的目标产品、项目选择分类、目标产品集成状态、其他支付产品、语言和框架。两个阶段都不得重新猜测或静默改选项目。
91
91
  - `integration_only`:优先从当前可访问业务项目确定根目录;当前目录不是可识别项目根、存在多个候选或用户要求其他/新项目时,先让用户选定并确认规范化路径,不得自行选择或扫描上层大目录。当前目录内已选定的现有项目分类为 `CURRENT_PROJECT`,用户指定的其他现有路径分类为 `OTHER_PROJECT`;本轮按下条规则初始化成功的新项目在完成步骤 1 后分类为 `PREPARED_NEW_PROJECT`。该分类只用于本轮检查器输入,不持久化。
92
92
  - 已有项目:进入任何写操作前确认目标路径可读、在当前 Agent 权限范围内可写、包含可识别项目根标记,且仍对应用户选定的业务项目;需要环境文件权限时按当前权限机制申请,无法取得权限、路径失效或实际项目不符时停止,要求用户提供可访问路径或重新选择项目。完整保留项目结构、已有文件和其他支付能力,只对当前目标产品需要的位置做可审查的局部修改,禁止先删除文件再重建。
93
- - 新项目:用户可以说明“在当前目录新建 pay-demo”、当前目录下的新子目录名、绝对路径,或只说“新建/新建项目/帮我新建默认项目”。目标路径必须先从本 flow 所在目录执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message resolve-project --project-input "$PROJECT_INPUT" --base-path "$USER_WORKSPACE_ROOT" --intent new`;该 runner 内部调用固定检查器 `prepare-new`。用户明确要新建但未指定地址时,runner 默认选择用户目录下的 `alipay-aipay-projects/pay-demo`;若已被非空目录占用,则自动选择安全后缀,不清空或覆盖。只有退出码 0 且唯一 JSON 同时满足 `projectSelection=NEW_PROJECT`、`projectOrigin=NEW_PROJECT`、`projectOriginLabel=本轮新建项目`、`preparationStatus=READY`,才能继续确认目标路径或其最近已有父目录在当前 Agent 权限范围内可写。路径非空、预检失败或无法取得写权限时停止,保留其中全部内容,让用户改选明确的空子目录、空同级目录、默认项目或其他尚不存在或为空的目录,禁止清空、覆盖或把该目录强行当作新项目。步骤 1 前只要求创建项目必需的服务端语言;用户未指定框架时使用下方 runner 固定默认,不再追问。创建命令只能来自用户指定方式、可访问模板/项目中的既有命令或本轮已读取的官方文档,且不得使用 `--force` 等覆盖既有文件的选项;禁止凭记忆编造脚手架、依赖或默认工程结构。初始化并完成步骤 1 后,当前会话分类单向转为 `PREPARED_NEW_PROJECT`;重新进入时只按现有项目重新分类,不读取历史状态。
93
+ - 新项目:含名称或位置时,Agent 解析绝对 `TARGET_PROJECT_PATH`,`PROJECT_INPUT` 仍传原话并执行第一条;仅“新建项目”或“帮我新建默认项目”执行第二条。runner 只调用 `prepare-new`;其他自然语言缺 `--target-path` 时保持未解析,禁止原话目录或默认回退。默认目录为用户目录下 `alipay-aipay-projects/pay-demo`,非空占用则加安全后缀。仅退出码 0 且唯一 JSON 满足 `projectSelection/projectOrigin=NEW_PROJECT`、`projectOriginLabel=本轮新建项目`、`preparationStatus=READY` 才继续,并确认目标或最近已有父目录可写。路径非空、预检/权限失败时保留内容并改选空或不存在目录,禁止清空或覆盖。服务端语言缺失才补问;框架使用下方 runner。创建命令只来自用户指定方式、已有模板/项目或本轮已读官方文档,禁用覆盖选项和臆造脚手架。初始化并完成步骤 1 后本轮转为 `PREPARED_NEW_PROJECT`,重入按现有项目重扫。
94
+
95
+ ```bash
96
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message resolve-project --project-input "$PROJECT_INPUT" --target-path "$TARGET_PROJECT_PATH" --base-path "$USER_WORKSPACE_ROOT" --intent new
97
+ ```
98
+
99
+ ```bash
100
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message resolve-project --project-input "$PROJECT_INPUT" --base-path "$USER_WORKSPACE_ROOT" --intent new
101
+ ```
94
102
  - 项目准备失败时停留在步骤 1 前置准备,输出实际错误和待补条件;不得进入沙箱准备。目标项目准备成功后记录实际根目录,步骤 1、步骤 3 的沙箱配置和后续代码必须落入该项目;沙箱配置不得降级写入系统临时目录、Agent 原始工作目录或其他项目。
95
103
 
96
- **项目选择保护规则**:当前目录已包含项目时,只能让用户选择“保留并在该项目新增目标支付能力”“保留并在指定的新子目录创建独立项目”或“保留并使用指定的其他路径”,不得展示清空或删除选项。用户明确要求“新建/创建”项目、网站、商城、应用或同类新产物时,必须保留完整原始表达并执行 `resolve-project --intent new`;当前目录中恰好已有项目不能把该意图改写为 `--intent existing`。用户未指定地址时走用户目录默认项目规则并返回规范化地址,不得反复要求用户补绝对路径。对 `full_process`,此时必须留在或返回 `../normal/full-process-routing.md` 主编排,取得 `NEW_PROJECT` 的规范化路径、技术栈和初始化结果前不得进入子流程;对 `integration_only`,取得可安全创建的新项目路径前不得进入沙箱准备或代码开发。取得并确认尚不存在或为空的精确路径前不得执行文件操作。
104
+ **项目选择保护规则**:当前目录已有项目时只允许保留并新增能力、在新子目录创建或使用其他路径,不展示清空/删除。明确新建产物必须保留原话并使用 `--intent new`,不得因当前目录有项目而改为复用。`full_process` 在取得 `NEW_PROJECT` 路径、技术栈和初始化结果前留在主编排;`integration_only` 在路径可安全创建前不准备沙箱或开发。确认精确空/不存在路径前不得写文件。
97
105
 
98
106
  **启动前上下文补问**:缺少项目位置或服务端语言时,必须从本 flow 所在目录执行下列标准消息,一次补齐;只缺少新项目精确位置时仍使用上方 `integration.project_path.required`。框架缺失本身不触发补问。禁止使用 AskUserQuestion、request_user_input、宿主超时默认值、Agent 自创选项或自由话术替代;用户未明确补齐前不得创建目录、初始化项目、安装依赖或修改文件。
99
107
 
@@ -162,7 +170,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message win
162
170
  资料校验通过后必须建立适用的版本控制忽略和访问保护。该例外只适用于当前沙箱资料;生产环境应用私钥和密码禁止提供给 Agent。Agent 不得在回复、摘要或普通日志中复述收到的值。
163
171
 
164
172
  5. Unix/macOS/Linux 的 `ensure` 只依据同一目标项目的 `.alipay-sandbox.json` 当前事实分流:文件不存在时执行既有 `create`;文件存在或为符号链接时执行既有 `verify` 安全路径。已存在配置不得再次调用 `createAnonymousSandbox`、不得覆盖,也不得因为中断重来要求用户删除配置;无效、不安全或被 Git 跟踪时不得使用,脚本按待配置终态统一输出后继续代码开发。
165
- 6. 只有 Unix/macOS/Linux 创建分支会调用冻结的 `alipay-anonymous-sandbox.createAnonymousSandbox`、`{"request":{"appType":"PUBLICAPP"}}`、`PLATFORM` 环境变量和 `result.content[0].text -> success -> data` 解包逻辑;禁止传入 `PRODUCT`。Windows 不调用该方法或 `sandbox_config.sh`。
173
+ 6. 只有 Unix/macOS/Linux 创建分支会调用冻结的 `alipay-anonymous-sandbox.createAnonymousSandbox`、`{"request":{"appType":"PUBLICAPP"}}`、`PLATFORM`、`PLATFORM_ID`、`ALIPAYCLI_BIZ_INFO` 环境变量和 `result.content[0].text -> success -> data` 解包逻辑;`ALIPAYCLI_BIZ_INFO` 固定标识当前调用方 Skill 及当前生效版本,禁止传入 `PRODUCT`。Windows 不调用该方法或 `sandbox_config.sh`。
166
174
  7. Unix/macOS/Linux 只接受以下互斥最终结果和一个临时恢复结果;待配置不算成功:
167
175
  - `READY`:退出码为 0;唯一带 `ALIPAY_AIPAY_INTERNAL:` 前缀的 `SANDBOX_ENSURE_ACTION=CREATED|VERIFIED`、`SANDBOX_CONFIG_PATH=<同一目标项目绝对路径>/.alipay-sandbox.json` 和 `FLOW:SANDBOX_CONFIG_READY` 内部事实。
168
176
  - `CREATE_PENDING`:退出码为 0;脚本 stdout 已渲染唯一 `sandbox.configuration.pending/CREATE` 正文;唯一带 `ALIPAY_AIPAY_INTERNAL:` 前缀的 `SANDBOX_PENDING_PATH=<同一目标项目绝对路径>/.alipay-sandbox.json` 和 `FLOW:SANDBOX_CONFIG_PENDING_CREATE` 内部事实;不得出现 READY 标记。
@@ -198,7 +206,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message win
198
206
  node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message start-confirm --product-type "$PRODUCT_TYPE" --product-name "$PRODUCT_NAME" --project-path "$PROJECT_PATH" --project-selection "$PROJECT_SELECTION" --language "$SERVER_LANGUAGE" --framework "$FRAMEWORK" --framework-source "$FRAMEWORK_SOURCE"
199
207
  ```
200
208
 
201
- runner 失败时不得展示手写方案或进入步骤 3。成功时必须同时取得唯一 `ALIPAY_AIPAY_INTERNAL:PROJECT_CONFIRM_RECEIPT=<receipt>` 和 `ALIPAY_AIPAY_INTERNAL:CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm`;缺失、重复或不可解析都视为失败。stdout 是步骤 2 唯一对客消息;Agent 必须把 stdout 原文作为非阻塞消息发给用户,且这是命令返回后的下一个动作;必须作为独立可见 assistant 消息送达,在此之前禁止继续调用工具。命令面板、折叠输出、内部思考或“已渲染/已展示”说明都不能替代交付;禁止添加 `<...>` 标签、“已输出完整方案”“服务声明已打印”、重复摘要或任何前后缀。发送后不停止、不等待回复,立即执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" flow integration development --product-type "$PRODUCT_TYPE" --project-confirm-receipt "$PROJECT_CONFIRM_RECEIPT"`。loader 返回 `PROJECT_CONFIRMATION=VALID` 才能进入步骤 3;返回 `STALE` 时 stdout 已重新渲染当前标准方案并给出新 receipt,必须先原文可见送达,再使用新 receipt 立即重试一次;连续变化说明项目事实不稳定,停在步骤 2 核对,不要求用户输入确认口令。
209
+ runner 失败时不得展示手写方案或进入步骤 3。成功时必须同时取得唯一 `ALIPAY_AIPAY_INTERNAL:PROJECT_CONFIRM_RECEIPT=<receipt>` 和 `ALIPAY_AIPAY_INTERNAL:CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm`;缺失、重复或不可解析都视为失败。stdout 是步骤 2 唯一对客消息;命令后的下一个动作必须把 stdout 作为可见进行中消息原文送达,不得作为最终答复结束当前回合,送达前禁止调用工具。命令面板、折叠输出、内部思考或“已渲染/已展示”说明都不能替代交付;禁止添加 `<...>` 标签、“已输出完整方案”“服务声明已打印”、重复摘要或任何前后缀。送达后同一回合立即执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" flow integration development --product-type "$PRODUCT_TYPE" --project-confirm-receipt "$PROJECT_CONFIRM_RECEIPT"`,不得总结、询问或等待回复。loader 返回 `PROJECT_CONFIRMATION=VALID` 才能进入步骤 3;返回 `STALE` 时 stdout 已重新渲染当前标准方案并给出新 receipt,必须先原文可见送达,再使用新 receipt 立即重试一次;连续变化说明项目事实不稳定,停在步骤 2 核对,不要求用户输入确认口令。
202
210
 
203
211
  - `full_process`:`../normal/full-process-routing.md` 已使用同一个 `integration.start.confirm`,在同一方案中绑定检查器产生的项目来源、代码状态和其他产品,以及产品、项目路径、语言/框架和服务声明;消息送达后已经直接通过 receipt 进入本步骤,禁止再次渲染或要求确认。该消息不展示步骤清单,也不缩减执行范围;后续仍必须按当前产品完成本 flow 的全部适用接口、完成条件和 checklist。任一方案字段变化时必须返回完整接入 router 重新扫描、渲染并自动校验新的单条方案。该方案仍禁止提前展示 MCC、授权范围、产品开通材料或 onboarding 待办。
204
212
 
@@ -548,7 +556,14 @@ stdout 必须为空,stderr 只接受唯一可解析的 `ALIPAY_AIPAY_INTERNAL:
548
556
 
549
557
  仅在已通过、只剩人工待验证或无法继续修复时渲染一次最终结论。
550
558
 
551
- 对客结论统一使用 `integration.checklist.result`,并严格按当前产品选择 `AIPAY`、`WEBPAY` 或 `APPPAY` 变体;整体结论、失败/部分通过项和人工待验证项必须来自最终一轮 checklist 实际证据。必须从本 flow 所在目录执行当前产品对应单行 runner;runner stdout 是唯一对客正文,必须把 stdout 原文作为当前回复正文发给用户:
559
+ 对客结论统一使用 `integration.checklist.result`;整体结论、失败/部分通过项和人工待验证项必须来自最终一轮 checklist 实际证据。首次调用前先按以下完整契约确定参数,禁止自由改写枚举:
560
+
561
+ - `variant` 固定映射 `aipay=AIPAY`、`webpay=WEBPAY`、`apppay=APPPAY`;`overallResult` 只允许“通过、部分通过、未通过”。
562
+ - `failedItems` 与 `manualItems` 是包含确定下一步的受控单行值,没有对应项目时传“无”。`sandboxConfigState` 直接复用本轮脚本终态,只允许 `READY|CREATE_PENDING|VERIFY_PENDING`;`blockingDefectState` 中,除沙箱配置及其直接依赖外存在代码、安全或必要依据缺口时传 `PRESENT`,否则传 `NONE`。待配置时 `failedItems` 必须列出沙箱配置及依赖动作未完成;恢复口令只由步骤 1/3 已发送的 `sandbox.configuration.pending` 正文提供。
563
+ - `executionMode` 在 `integration_only` 固定为“仅代码开发”,此时 `nextFlowReminder` 固定为“正式上线前还需完成支付产品-产品开通。”。`full_process` 固定为“完整接入”:`READY` 且整体“通过”时提醒“代码开发校验通过后将自动进入支付产品-产品开通;未通过时停留在代码开发。”;沙箱待配置、整体“部分通过”且 `blockingDefectState=NONE` 时提醒“沙箱配置尚未完成,但不阻塞本轮继续支付产品-产品开通;完成配置后再进行沙箱测试。”;`READY`、整体“部分通过”、`failedItems=无` 且仅有人工待验证项时提醒“代码开发自动校验已完成,仍有人工待验证项;本轮继续产品开通。”,并把 `manualItems` 原样带入 Onboarding Step 6。其他阻断结果停在 Integration。
564
+ - 只有 `AIPAY` 传 `sandboxResult`:步骤 6 实际端到端联调通过时固定为“AI 按量付费沙箱服务端联调通过”,未运行、待配置或未通过时固定为“AI 按量付费沙箱服务端联调未通过”。只有 `WEBPAY` 传 `webpayExperienceResult`:按步骤 6 实际证据从“已提供浏览器付款入口和操作指引,实际付款及结果验证仍以用户完成情况为准”“未提供浏览器付款入口或完整操作指引,需先补齐”“已提供浏览器付款入口和操作指引;用户已完成一笔沙箱付款,结果只按实际核验证据记录”中选择。`APPPAY` 不传这两个参数。
565
+
566
+ 必须从本 flow 所在目录执行当前产品对应单行 runner;runner stdout 是唯一对客正文,必须把 stdout 原文作为当前回复正文发给用户:
552
567
 
553
568
  <!-- ALIPAY_AIPAY_PRODUCT:aipay:START -->
554
569
  ```bash
@@ -566,13 +581,9 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message che
566
581
  ```
567
582
  <!-- ALIPAY_AIPAY_PRODUCT:END -->
568
583
 
569
- `failedItems` 与 `manualItems` 必须在各自受控单行值中包含对应的确定下一步;没有对应项目时明确传入“无”。`sandboxConfigState` 必须直接复用本轮脚本终态,只允许 `READY|CREATE_PENDING|VERIFY_PENDING`。`blockingDefectState` 来自同一最终 checklist:除沙箱配置及其直接依赖动作外,存在任一代码、安全或必要依据缺口时必须为 `PRESENT`,否则为 `NONE`。待配置时 `failedItems` 必须包含沙箱配置及其依赖动作未完成;恢复口令已由步骤 1/3 选定并发送的唯一 `sandbox.configuration.pending` 正文输出,checklist runner 不生成也不重复该消息。
570
-
571
584
  同一次代码开发流程只渲染一次最终 checklist 结果;renderer stdout 是本次默认收口的唯一对客正文,禁止在前后追加风险说明、通过项摘要、执行说明、衔接提醒或第二份待办。
572
585
 
573
- `integration_only` 的 `executionMode` 固定为“仅代码开发”,`nextFlowReminder` 固定为“正式上线前还需完成支付产品-产品开通。”。`full_process` 使用“完整接入”:`READY` 且整体结果为“通过”时使用“代码开发校验通过后将自动进入支付产品-产品开通;未通过时停留在代码开发。”;沙箱待配置、整体结果为“部分通过”且 `blockingDefectState=NONE` 时,使用“沙箱配置尚未完成,但不阻塞本轮继续支付产品-产品开通;完成配置后再进行沙箱测试。”;沙箱为 `READY`、整体结果为“部分通过”、`failedItems=无` 且仅剩人工待验证项时,使用“代码开发自动校验已完成,仍有人工待验证项;本轮继续产品开通。”,并把本次 `manualItems` 原样作为后续 onboarding Step 6 的 `MANUAL_VERIFICATION_ITEMS`;`blockingDefectState=PRESENT` 或整体结果为“未通过”时仍停在代码开发。renderer 会校验执行模式、沙箱状态、阻塞缺口、整体结果和衔接文案一致。
574
-
575
- 只有 `AIPAY` 传入 `sandboxResult`,且只能复用步骤 6 的实际服务端联调证据;`WEBPAY` 必须按步骤 6 的实际证据传入消息目录登记的 `webpayExperienceResult` 枚举,区分已提供、未提供和用户已完成一笔体验,不得自由改写。这些字段只用于 renderer 校验最终结论,不在 checklist 对客结果中重复展示。APP 支付未来沙箱测试对客引导的维护 TODO 只保留在消息目录对应变体,不得渲染给用户;补充时必须同步本 flow、`modules/checklist.md` 和测试。
586
+ 这些产品专属字段只用于 renderer 校验最终结论,不在 checklist 对客结果中重复展示。APP 支付未来沙箱测试对客引导的维护 TODO 只保留在消息目录对应变体,不得渲染给用户;补充时必须同步本 flow、`modules/checklist.md` 和测试。
576
587
 
577
588
  **完成语义**:只有步骤 1-8 的适用完成条件均满足且 `sandboxConfigState=READY` 后,才可声明支付产品-代码开发完成。沙箱待配置但代码实现与其余检查通过时只能输出“部分通过”,不得改写为完成;存在其他未通过项时输出“未通过”;若配置已就绪且仅剩无法自动验证项,输出“部分通过”并登记人工待验证项,不得改写为完成或生产就绪。
578
589
 
@@ -13,7 +13,6 @@
13
13
  *
14
14
  * 依赖安装:
15
15
  * dotnet add package AlipaySDKNet.Standard
16
- * dotnet add package Microsoft.AspNetCore.App
17
16
  */
18
17
 
19
18
  using System;
@@ -351,9 +350,10 @@ namespace A2MPaymentDemo
351
350
  tradeNo = protocol.GetProperty("trade_no").GetString();
352
351
  }
353
352
 
354
- if (root.TryGetProperty("method", out var method))
353
+ if (root.TryGetProperty("method", out var method)
354
+ && method.TryGetProperty("client_session", out var clientSessionValue))
355
355
  {
356
- clientSession = method.GetProperty("client_session").GetString();
356
+ clientSession = clientSessionValue.GetString();
357
357
  }
358
358
  }
359
359
 
@@ -379,12 +379,16 @@ namespace A2MPaymentDemo
379
379
 
380
380
  // 2. 调用支付宝 API 验证支付凭证
381
381
  var request = new AlipayAipayAgentPaymentVerifyRequest();
382
- request.BizContent = JsonSerializer.Serialize(new
382
+ var verifyBizContent = new Dictionary<string, string>
383
383
  {
384
- payment_proof = paymentProofValue,
385
- trade_no = tradeNo,
386
- client_session = clientSession
387
- }, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
384
+ ["payment_proof"] = paymentProofValue,
385
+ ["trade_no"] = tradeNo
386
+ };
387
+ if (!string.IsNullOrWhiteSpace(clientSession))
388
+ {
389
+ verifyBizContent["client_session"] = clientSession;
390
+ }
391
+ request.BizContent = JsonSerializer.Serialize(verifyBizContent);
388
392
 
389
393
  var verifyResponse = AlipayClient.Execute(request);
390
394
 
@@ -29,7 +29,8 @@ public class AlipayTradeQuery {
29
29
 
30
30
  if (response.isSuccess()) {
31
31
  String businessState;
32
- switch (response.getTradeStatus()) {
32
+ String tradeStatus = response.getTradeStatus();
33
+ switch (tradeStatus == null ? "" : tradeStatus) {
33
34
  case "TRADE_SUCCESS":
34
35
  case "TRADE_FINISHED":
35
36
  businessState = "PAID";
@@ -44,7 +45,7 @@ public class AlipayTradeQuery {
44
45
  businessState = "UNKNOWN";
45
46
  }
46
47
  System.out.println("业务状态: " + businessState + ", 支付宝状态: "
47
- + response.getTradeStatus() + ", 订单金额: " + response.getTotalAmount());
48
+ + tradeStatus + ", 订单金额: " + response.getTotalAmount());
48
49
  } else {
49
50
  System.out.println("调用失败: " + response.getSubCode() + " - " + response.getSubMsg());
50
51
  }
@@ -161,6 +161,12 @@ function normalizeAmount(value) {
161
161
  return `${BigInt(match[1]).toString()}.${(match[2] || '').padEnd(2, '0')}`;
162
162
  }
163
163
 
164
+ function amountsEqual(left, right) {
165
+ const leftAmount = normalizeAmount(left);
166
+ const rightAmount = normalizeAmount(right);
167
+ return leftAmount !== null && rightAmount !== null && leftAmount === rightAmount;
168
+ }
169
+
164
170
  function isExactSandboxMode() {
165
171
  return CONFIG.alipay.gateway === 'https://openapi-sandbox.dl.alipaydev.com/gateway.do'
166
172
  && CONFIG.alipay.serviceId === 'api_mock_service_id';
@@ -301,12 +307,15 @@ async function verifyPaymentAndDeliverResource(req, res, paymentProof) {
301
307
  return createPaymentRequiredResponse(req, res);
302
308
  }
303
309
 
310
+ const verifyBizContent = {
311
+ payment_proof: paymentProofValue,
312
+ trade_no: tradeNo
313
+ };
314
+ if (clientSession) {
315
+ verifyBizContent.client_session = clientSession;
316
+ }
304
317
  const verifyResponse = await alipaySdk.exec('alipay.aipay.agent.payment.verify', {
305
- bizContent: {
306
- payment_proof: paymentProofValue,
307
- trade_no: tradeNo,
308
- client_session: clientSession
309
- }
318
+ bizContent: verifyBizContent
310
319
  });
311
320
 
312
321
  // SDK 可能返回嵌套响应,也可能直接返回业务字段。
@@ -351,7 +360,7 @@ async function verifyPaymentAndDeliverResource(req, res, paymentProof) {
351
360
  return createPaymentRequiredResponse(req, res);
352
361
  }
353
362
 
354
- const amountMatches = order && normalizeAmount(order.amount) === normalizeAmount(verifyAmount);
363
+ const amountMatches = order && amountsEqual(order.amount, verifyAmount);
355
364
  const resourceMatches = order && order.resourceId === resourceIdVerified && resourceIdVerified === CONFIG.resource.path;
356
365
  const fulfillmentInProgress = order && ['PENDING_CONFIRM', 'FULFILLED'].includes(order.fulfillStatus);
357
366
  const orderUsable = order
@@ -97,42 +97,51 @@ function loadAlipayConfig() {
97
97
  $privateKey = firstNonBlank(
98
98
  getenv('ALIPAY_AIPAY_PRIVATE_KEY'),
99
99
  $sandboxApp['appPrivatePkcsKey'] ?? null,
100
- $sandbox['appPrivatePkcsKey'] ?? null,
101
- '<APP_PRIVATE_KEY>'
100
+ $sandbox['appPrivatePkcsKey'] ?? null
102
101
  );
103
102
 
104
- return [
103
+ $gateway = firstNonBlank(
104
+ getenv('ALIPAY_AIPAY_GATEWAY'),
105
+ getenv('ALIPAY_GATEWAY'),
106
+ $sandbox['gateway'] ?? null,
107
+ 'https://openapi-sandbox.dl.alipaydev.com/gateway.do'
108
+ );
109
+ $serviceId = firstNonBlank(
110
+ getenv('ALIPAY_AIPAY_SERVICE_ID'),
111
+ $gateway === 'https://openapi-sandbox.dl.alipaydev.com/gateway.do' ? 'api_mock_service_id' : null
112
+ );
113
+
114
+ $config = [
105
115
  'appId' => firstNonBlank(
106
116
  getenv('ALIPAY_AIPAY_APP_ID'),
107
117
  $sandboxApp['appId'] ?? null,
108
- $sandbox['appId'] ?? null,
109
- '<APP_ID>'
118
+ $sandbox['appId'] ?? null
110
119
  ),
111
120
  'privateKey' => $privateKey, // PHP 使用 PKCS#1 格式应用私钥
112
121
  'alipayPublicKey' => firstNonBlank(
113
122
  getenv('ALIPAY_AIPAY_PUBLIC_KEY'),
114
123
  $sandboxApp['alipayPublicKey'] ?? null,
115
- $sandbox['alipayPublicKey'] ?? null,
116
- '<ALIPAY_PUBLIC_KEY>'
124
+ $sandbox['alipayPublicKey'] ?? null
117
125
  ),
118
126
  // 默认用于本 Skill 的快速沙箱联调;生产部署时设置 ALIPAY_AIPAY_GATEWAY=https://openapi.alipay.com/gateway.do
119
- 'gateway' => firstNonBlank(
120
- getenv('ALIPAY_AIPAY_GATEWAY'),
121
- getenv('ALIPAY_GATEWAY'),
122
- $sandbox['gateway'] ?? null,
123
- 'https://openapi-sandbox.dl.alipaydev.com/gateway.do'
124
- ),
127
+ 'gateway' => $gateway,
125
128
  'sellerId' => firstNonBlank(
126
129
  getenv('ALIPAY_AIPAY_SELLER_ID'),
127
130
  $sandboxApp['pid'] ?? null,
128
131
  $sandboxApp['uid'] ?? null,
129
- $sandbox['sellerId'] ?? null,
130
- '<SELLER_ID_2088>'
132
+ $sandbox['sellerId'] ?? null
131
133
  ), // 商户 PID(2088 格式)
132
134
  'sellerName' => firstNonBlank(getenv('ALIPAY_AIPAY_SELLER_NAME'), '测试商户'),
133
- 'serviceId' => firstNonBlank(getenv('ALIPAY_AIPAY_SERVICE_ID'), 'api_mock_service_id'), // 仅用于沙箱联调;上线前替换为服务市场真实 serviceId
135
+ 'serviceId' => $serviceId, // 仅固定沙箱网关可默认 api_mock_service_id;上线前替换为真实 serviceId
134
136
  'merchantPrivateKey' => $privateKey // 用于商家账单签名
135
137
  ];
138
+
139
+ foreach (['appId', 'privateKey', 'alipayPublicKey', 'sellerId', 'serviceId'] as $requiredField) {
140
+ if ($config[$requiredField] === '') {
141
+ throw new RuntimeException("支付宝配置缺失:{$requiredField}");
142
+ }
143
+ }
144
+ return $config;
136
145
  }
137
146
 
138
147
  define('ALIPAY_CONFIG', loadAlipayConfig());
@@ -254,6 +263,12 @@ function normalizeAmount($value) {
254
263
  return ($integer === '' ? '0' : $integer) . '.' . str_pad($fraction, 2, '0');
255
264
  }
256
265
 
266
+ function amountsEqual($left, $right) {
267
+ $leftAmount = normalizeAmount($left);
268
+ $rightAmount = normalizeAmount($right);
269
+ return $leftAmount !== null && $rightAmount !== null && $leftAmount === $rightAmount;
270
+ }
271
+
257
272
  function isExactSandboxMode() {
258
273
  return ALIPAY_CONFIG['gateway'] === 'https://openapi-sandbox.dl.alipaydev.com/gateway.do'
259
274
  && ALIPAY_CONFIG['serviceId'] === 'api_mock_service_id';
@@ -427,9 +442,11 @@ function verifyPaymentAndDeliverResource($paymentProof) {
427
442
 
428
443
  $model = [
429
444
  'payment_proof' => $paymentProofValue,
430
- 'trade_no' => $tradeNo,
431
- 'client_session' => $clientSession
445
+ 'trade_no' => $tradeNo
432
446
  ];
447
+ if ($clientSession !== null && trim((string)$clientSession) !== '') {
448
+ $model['client_session'] = $clientSession;
449
+ }
433
450
 
434
451
  $request->setBizContent(json_encode($model, JSON_UNESCAPED_UNICODE));
435
452
  $responseResult = $alipayClient->execute($request);
@@ -465,7 +482,7 @@ function verifyPaymentAndDeliverResource($paymentProof) {
465
482
  createPaymentRequiredResponse();
466
483
  }
467
484
 
468
- $amountMatches = $order && normalizeAmount($order['amount']) === normalizeAmount($verifyAmount);
485
+ $amountMatches = $order && amountsEqual($order['amount'], $verifyAmount);
469
486
  $resourceMatches = $order
470
487
  && $order['resourceId'] === $resourceIdVerified
471
488
  && $resourceIdVerified === RESOURCE_CONFIG['path'];
@@ -168,6 +168,12 @@ def normalize_amount(value):
168
168
  return f'{Decimal(text):.2f}'
169
169
 
170
170
 
171
+ def amounts_equal(left, right):
172
+ left_amount = normalize_amount(left)
173
+ right_amount = normalize_amount(right)
174
+ return left_amount is not None and right_amount is not None and left_amount == right_amount
175
+
176
+
171
177
  def is_future(value):
172
178
  try:
173
179
  return datetime.fromisoformat(value).timestamp() > time.time()
@@ -344,16 +350,10 @@ def verify_payment_and_deliver_resource(payment_proof):
344
350
  model = AlipayAipayAgentPaymentVerifyModel()
345
351
  model.payment_proof = payment_proof_value
346
352
  model.trade_no = trade_no
347
- model.client_session = client_session
353
+ if client_session:
354
+ model.client_session = client_session
348
355
  verify_request.biz_model = model
349
356
 
350
- # 如果 SDK 版本支持直接使用 dict,也可以用 biz_content(备选方案)
351
- # verify_request.biz_content = json.dumps({
352
- # 'payment_proof': payment_proof_value,
353
- # 'trade_no': trade_no,
354
- # 'client_session': client_session
355
- # }, ensure_ascii=False)
356
-
357
357
  verify_response_content = alipay_client.execute(verify_request)
358
358
  verify_response = json.loads(verify_response_content)
359
359
 
@@ -392,7 +392,7 @@ def verify_payment_and_deliver_resource(payment_proof):
392
392
  print(f"支付凭证无效或已过期:outTradeNo={verify_out_trade_no}")
393
393
  return create_payment_required_response()
394
394
 
395
- amount_matches = order and normalize_amount(order.get('amount')) == normalize_amount(verify_amount)
395
+ amount_matches = order and amounts_equal(order.get('amount'), verify_amount)
396
396
  resource_matches = (
397
397
  order
398
398
  and order.get('resource_id') == resource_id_verified
@@ -18,11 +18,12 @@ which alipay-cli 2>/dev/null && alipay-cli version
18
18
 
19
19
  **重要:工具返回的 `data` 只是候选沙箱配置;必须先按 `sandbox-setup-guide.md` 完成字段完整性校验,校验通过后再写入正式本地配置文件。创建阶段不对客输出字段核对表、独立校验结论或沙箱环境摘要表;代码开发完成并通过配置后置校验后,才按该指南输出一张沙箱环境摘要表。该结果不代表沙箱支付测试通过。privateKey、publicKey 等密钥字段不得展开。**
20
20
 
21
- `createAnonymousSandbox` 的 MCP 请求体只有业务入参 `request.appType`,当前固定为 `PUBLICAPP`。`PLATFORM` 和 `PLATFORM_ID` 是 alipay-cli 上下文环境变量,禁止写入 `--data` 的 JSON。匿名沙箱创建不依赖本轮最终选择的支付产品,因此不得为了创建沙箱先编造或强行确认 `PRODUCT`。
21
+ `createAnonymousSandbox` 的 MCP 请求体只有业务入参 `request.appType`,当前固定为 `PUBLICAPP`。`PLATFORM`、`PLATFORM_ID` 和 `ALIPAYCLI_BIZ_INFO` 是 alipay-cli 上下文环境变量,禁止写入 `--data` 的 JSON。匿名沙箱创建不依赖本轮最终选择的支付产品,因此不得为了创建沙箱先编造或强行确认 `PRODUCT`。
22
22
 
23
23
  ```bash
24
24
  PLATFORM="<PLATFORM>" \
25
25
  PLATFORM_ID="<run_id>" \
26
+ ALIPAYCLI_BIZ_INFO='{"skill":"alipay-aipay","skill_version":"v<当前 VERSION>"}' \
26
27
  alipay-cli mcp call alipay-anonymous-sandbox.createAnonymousSandbox \
27
28
  --data '{"request":{"appType":"PUBLICAPP"}}' \
28
29
  --json
@@ -40,6 +41,7 @@ alipay-cli mcp call alipay-anonymous-sandbox.createAnonymousSandbox \
40
41
  |------|------|----------|
41
42
  | `PLATFORM` | AI 编程工具识别结果 | 参考通用脚本 `../../../normal/scripts/detect_dev_tool.sh` 识别当前工具后使用其输出值;缺失时使用 `unknown`。例如 Codex 环境检测结果为 `codex` |
42
43
  | `PLATFORM_ID` | 当前流程 `run_id` | 由 `sandbox_config.sh` 通过 `common.sh` 从当前流程 run context 读取,遥测开启或关闭都不影响该值;若宿主提供 `COZE_PROJECT_ID`、`MEOO_PROJECT_ID`,或 `MIAODA` 环境下的 `VITE_APP_ID`/`PLATFORM_ID`,新建 run 时优先以该项目 ID 为 `run_id`;缺失时不阻断沙箱创建 |
44
+ | `ALIPAYCLI_BIZ_INFO` | 当前生效 Skill 的 `VERSION` | 由 `common.sh` 动态生成 JSON:`{"skill":"alipay-aipay","skill_version":"v<当前 VERSION>"}`;`skill` 固定,版本统一带 `v` 前缀,禁止由 Agent 手写或沿用外部同名值 |
43
45
  | `PRODUCT` | 不再传入 | 该 MCP 方法当前不需要产品上下文;不得把产品名写入 `--data` 或 CLI 环境变量 |
44
46
 
45
47
  **Codex 环境示例:**
@@ -47,6 +49,7 @@ alipay-cli mcp call alipay-anonymous-sandbox.createAnonymousSandbox \
47
49
  ```bash
48
50
  PLATFORM=codex \
49
51
  PLATFORM_ID=<run_id> \
52
+ ALIPAYCLI_BIZ_INFO='{"skill":"alipay-aipay","skill_version":"v<当前 VERSION>"}' \
50
53
  alipay-cli mcp call alipay-anonymous-sandbox.createAnonymousSandbox \
51
54
  --data '{"request":{"appType":"PUBLICAPP"}}' \
52
55
  --json
@@ -242,6 +242,7 @@ if [ "$MODE" = "create" ]; then
242
242
  ensure_gitignore_protection || { echo "SANDBOX_ERROR:无法为沙箱配置建立 Git 忽略保护" >&2; exit 1; }
243
243
  PLATFORM=$(bash "$DETECTOR") || PLATFORM="unknown"
244
244
  init_platform_id
245
+ init_alipay_cli_biz_info || { echo "SANDBOX_ERROR:无法初始化 alipay-cli 业务上报信息" >&2; exit 1; }
245
246
  if ! require_alipay_cli_local_state_access; then
246
247
  emit_control "SANDBOX_LOCAL_FS_PERMISSION_MODE=CREATE"
247
248
  emit_control "CLI_FLOW:RETRY_WITH_LOCAL_FS_PERMISSION"
@@ -249,7 +250,7 @@ if [ "$MODE" = "create" ]; then
249
250
  fi
250
251
  attempt=1
251
252
  while true; do
252
- raw=$(env -u PRODUCT PLATFORM="$PLATFORM" PLATFORM_ID="${PLATFORM_ID:-}" alipay-cli mcp call alipay-anonymous-sandbox.createAnonymousSandbox --data '{"request":{"appType":"PUBLICAPP"}}' --json 2>&1)
253
+ raw=$(env -u PRODUCT PLATFORM="$PLATFORM" PLATFORM_ID="${PLATFORM_ID:-}" ALIPAYCLI_BIZ_INFO="$ALIPAYCLI_BIZ_INFO" alipay-cli mcp call alipay-anonymous-sandbox.createAnonymousSandbox --data '{"request":{"appType":"PUBLICAPP"}}' --json 2>&1)
253
254
  rc=$?
254
255
  if alipay_cli_has_local_state_permission_error "$raw"; then
255
256
  echo "SANDBOX_ERROR:快速沙箱创建结果不明,禁止自动重复创建" >&2
@@ -274,7 +275,7 @@ if [ "$MODE" = "create" ]; then
274
275
  fi
275
276
  if [ "$(echo "$business" | jq -r '.success // false')" != "true" ] && echo "$business" | jq -r '.msg // .resultMsg // ""' | grep -q '证书密钥'; then
276
277
  sleep 2
277
- raw=$(env -u PRODUCT PLATFORM="$PLATFORM" PLATFORM_ID="${PLATFORM_ID:-}" alipay-cli mcp call alipay-anonymous-sandbox.createAnonymousSandbox --data '{"request":{"appType":"PUBLICAPP"}}' --json 2>&1)
278
+ raw=$(env -u PRODUCT PLATFORM="$PLATFORM" PLATFORM_ID="${PLATFORM_ID:-}" ALIPAYCLI_BIZ_INFO="$ALIPAYCLI_BIZ_INFO" alipay-cli mcp call alipay-anonymous-sandbox.createAnonymousSandbox --data '{"request":{"appType":"PUBLICAPP"}}' --json 2>&1)
278
279
  rc=$?
279
280
  if alipay_cli_has_local_state_permission_error "$raw"; then
280
281
  echo "SANDBOX_ERROR:快速沙箱创建结果不明,禁止自动重复创建" >&2
@@ -130,12 +130,12 @@ source ~/.bashrc # Linux/bash
130
130
 
131
131
  > ⚠️ **强制规范:检测结果仅供内部使用,禁止向用户输出!**
132
132
 
133
- AI 工具由 `scripts/detect_dev_tool.sh` 检测;`init_alipay_cli_context` 设置 `DEV_TOOL_NAME`、`PLATFORM`,并把当前 `run_id` 作为 `PLATFORM_ID`(不受遥测开关影响)。产品确认后,Onboarding 还严格映射 `aipay|webpay|apppay` 为 `PRODUCT=AIPAY|WEBPAY|APPPAY`,其环境检查、登录、查询、写入和上传均携带该值,但不写入 MCP JSON;Integration 产品确定前的环境检查和快速沙箱不传 `PRODUCT`。新建 run 优先采用宿主 `COZE_PROJECT_ID`、`MEOO_PROJECT_ID` 或 MIAODA 的 `VITE_APP_ID`/`PLATFORM_ID`,并复用为 `PLATFORM_ID`。
133
+ AI 工具由 `scripts/detect_dev_tool.sh` 检测;`init_alipay_cli_context` 设置 `DEV_TOOL_NAME`、`PLATFORM`,把当前 `run_id` 作为 `PLATFORM_ID`(不受遥测开关影响),并从当前生效 Skill 的 `VERSION` 动态生成 `ALIPAYCLI_BIZ_INFO={"skill":"alipay-aipay","skill_version":"v<当前 VERSION>"}`。所有携带 `PLATFORM` 的 Skill 业务 CLI 调用均携带该 JSON,外部同名值会被覆盖;`alipay-cli version` 等不携带 `PLATFORM` 的安装和环境探测不注入该变量。产品确认后,Onboarding 还严格映射 `aipay|webpay|apppay` 为 `PRODUCT=AIPAY|WEBPAY|APPPAY`,其环境检查、登录、查询、写入和上传均携带该值,但 `PRODUCT` 和 `ALIPAYCLI_BIZ_INFO` 都不写入 MCP JSON;Integration 产品确定前的环境检查和快速沙箱不传 `PRODUCT`。新建 run 优先采用宿主 `COZE_PROJECT_ID`、`MEOO_PROJECT_ID` 或 MIAODA 的 `VITE_APP_ID`/`PLATFORM_ID`,并复用为 `PLATFORM_ID`。
134
134
 
135
135
  ```
136
136
  ❌ 禁止:向用户输出"环境检查完成,检测到您正在使用 Claude Code 环境"等检测结果
137
137
  ❌ 禁止:向用户透出任何关于 AI 编程工具检测的信息
138
- ✅ 正确:检测结果仅供内部使用,只用于脚本设置 `PLATFORM`;`PLATFORM_ID` 由脚本从当前 `run_id` 派生,Onboarding 的 `PRODUCT` 由已确认产品映射
138
+ ✅ 正确:检测结果仅供内部使用,只用于脚本设置 `PLATFORM`;`PLATFORM_ID` 由脚本从当前 `run_id` 派生,`ALIPAYCLI_BIZ_INFO` 由当前 Skill 版本派生,Onboarding 的 `PRODUCT` 由已确认产品映射
139
139
  ✅ 正确:静默完成检测,直接进入下一步流程
140
140
  ```
141
141
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  目标项目和技术栈确定后执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" flow integration entry`,只加载当前入口阶段并按步骤 1/2 准备及展示方案;代码开发满足衔接条件后执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" flow onboarding entry`。禁止整读两个 flow。所有 Skill 自有运行时动作只用当前 Skill 绝对路径下的统一入口单命令执行,不前置 `cd`、不拼接管道;用户项目命令仍以已确认项目根为 workdir。
6
6
 
7
- 其他 Skill 相对命令以 `references/normal/` 为 workdir。`PROJECT_INPUT` 保留用户原话;明确新建项目、网站、商城、应用等产物时固定 `--intent new`,不得因当前目录已有项目改成 `--intent existing`。新建使用空路径或 runner 安全默认目录;复用须选定项目根,未知位置先定位候选。“当前目录”仅在 runner 识别为项目根时接受。扫描和后续脚本只用解析后的规范化绝对路径。
7
+ 其他 Skill 相对命令以 `references/normal/` 为 workdir。保留原始 `PROJECT_INPUT`;明确新建产物用 `--intent new`,名称/位置由 Agent 解析为 `TARGET_PROJECT_PATH`,runner 只校验;登记默认表达才省略。复用须选定项目根,未知位置先定位候选;“当前目录”仅在 runner 识别为项目根时接受。后续只用解析后的绝对路径。
8
8
 
9
9
  ## 边界
10
10
 
@@ -16,17 +16,23 @@
16
16
 
17
17
  项目来源互斥:`CURRENT_PROJECT` 为当前目录内项目根,`OTHER_PROJECT` 为用户以绝对路径提供的目录外项目;未选定/不可访问/不可识别则为 `PROJECT_UNRESOLVED`,禁止凭描述生成状态。
18
18
 
19
- `NEW_PROJECT` 只用于初始化前:
19
+ `NEW_PROJECT` 只用于初始化前。含名称或位置时执行:
20
+
21
+ ```bash
22
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message resolve-project --project-input "$PROJECT_INPUT" --target-path "$TARGET_PROJECT_PATH" --base-path "$USER_WORKSPACE_ROOT" --intent new
23
+ ```
24
+
25
+ 登记默认表达时执行:
20
26
 
21
27
  ```bash
22
28
  node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message resolve-project --project-input "$PROJECT_INPUT" --base-path "$USER_WORKSPACE_ROOT" --intent new
23
29
  ```
24
30
 
25
- 未指定位置时使用用户目录下 `alipay-aipay-projects/pay-demo`,非空占用则加安全后缀。只有 `preparationStatus=READY` 才继续;服务端语言已知且用户未指定框架时,先执行 Integration flow 登记的 `framework-resolve`,按 Java / Spring Boot、PHP / Laravel、Python / FastAPI、C# / ASP.NET Core、Node.js / Express 的固定默认初始化,用户明确框架时优先使用其值;其他结果不得清空/覆盖。初始化且沙箱终态确定后单向成为当前会话的 `PREPARED_NEW_PROJECT`;不持久化,重入重扫。`NEW_PROJECT` 不扫描代码,`PREPARED_NEW_PROJECT` 不恢复为空目录状态。
31
+ 第二条只接受“新建项目”或“帮我新建默认项目”,使用用户目录下 `alipay-aipay-projects/pay-demo`,非空占用则加安全后缀;其他自然语言缺 `--target-path` 时保持未解析,禁止原话目录或默认回退。仅 `preparationStatus=READY` 才继续;框架由 Integration 的 `framework-resolve` 按语言默认或用户明确值初始化,其他结果不得清空/覆盖。初始化且沙箱终态确定后单向成为本轮 `PREPARED_NEW_PROJECT`;不持久化,重入重扫。`NEW_PROJECT` 不扫描代码。
26
32
 
27
33
  ### 固定检查器与 runner 终态协议
28
34
 
29
- - `resolve-project --intent new` 只接受退出码 0、唯一 JSON、规范化绝对 `projectPath`、`projectSelection/projectOrigin=NEW_PROJECT`、`projectOriginLabel=本轮新建项目` 和 `preparationStatus=READY`;否则保持 `PROJECT_UNRESOLVED`。
35
+ - `resolve-project --intent new` 只接受退出码 0、唯一 JSON、绝对 `projectPath`、`projectSelection/projectOrigin=NEW_PROJECT`、`projectOriginLabel=本轮新建项目` 和 `preparationStatus=READY`;否则保持 `PROJECT_UNRESOLVED`。
30
36
  - `resolve-project --intent existing` 只接受退出码 0、唯一 JSON、规范化绝对路径、`CURRENT_PROJECT|OTHER_PROJECT` 和 `EXISTING_PROJECT/现有项目`。相对路径限 `--base-path` 内,目录外由用户提供绝对路径;失败不得以描述/cwd 代替。
31
37
  - `locate-projects` 用于未知位置/多候选,缺省搜索词为 `当前目录`。stdout 是唯一候选消息;选择后仍执行 `resolve-project --intent existing`。
32
38
  - `start-confirm` 内部只执行一次项目扫描,校验后将同一对象交给 renderer;失败为 `STATUS_UNKNOWN`。禁止改写命令、另跑扫描、抽取代码,或追加 `find`、`ls`、`stat`、额外 `jq` 和临时脚本复核同一结果。
@@ -60,7 +66,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message con
60
66
  node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message locate-projects --base-path "$USER_WORKSPACE_ROOT" --search-input "$SEARCH_INPUT" --format message
61
67
  ```
62
68
 
63
- 字段齐备后执行唯一 runner;禁止拆开执行项目扫描、`jq` 或 renderer,也不得附加解析。runner stdout 是本动作唯一对客消息;只有同时取得唯一 `CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm` 时,Agent 必须把 stdout 原文作为非阻塞消息发给用户,且这是命令返回后的下一个动作;必须作为独立可见 assistant 消息送达,在此之前禁止继续调用工具。发送后不停止、不等待回复,立即使用同一次 runner 输出的 receipt 进入 development loader。方案事实变化时重新扫描并输出,不使用确认口令;静态扫描只决定进入验证,不证明完成。
69
+ 字段齐备后执行唯一 runner;禁止拆开执行项目扫描、`jq` 或 renderer,也不得附加解析。runner stdout 是本动作唯一对客消息;只有取得唯一 `CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm` 才能继续。命令后的下一个动作必须把 stdout 作为可见进行中消息原文送达,不得作为最终答复结束当前回合,送达前禁止调用工具;送达后同一回合立即使用同一次 runner receipt 进入 development loader,不得总结、询问或等待回复。方案事实变化时重新扫描并输出,不使用确认口令;静态扫描只决定进入验证,不证明完成。
64
70
 
65
71
  ```bash
66
72
  node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message start-confirm --product-type "$PRODUCT_TYPE" --product-name "$PRODUCT_NAME" --project-path "$PROJECT_PATH" --project-selection "$PROJECT_SELECTION" --language "$SERVER_LANGUAGE" --framework "$FRAMEWORK" --framework-source "$FRAMEWORK_SOURCE"