@alipay/alipay-aipay 1.6.1-beta.2 → 1.6.1-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alipay/alipay-aipay",
3
- "version": "1.6.1-beta.2",
3
+ "version": "1.6.1-beta.4",
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.1-beta.2
1
+ 1.6.1-beta.4
@@ -539,32 +539,32 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message saf
539
539
 
540
540
  先汇总 checklist 证据;本轮可修复时先修复、重检,不渲染中间结果。项目快照变化会使旧 `CHECKLIST_SCOPE_RECEIPT` 失效,必须重新加载本阶段并重跑受影响检查。
541
541
 
542
- 项目不再变化且准备生成最终结论时,按已确认产品固定映射 `aipay=AIPAY`、`webpay=WEBPAY`、`apppay=APPPAY` 得到 `PRODUCT_VARIANT`。下方唯一 `checklist-result` runner 会在同一次调用中重新计算一次项目快照、校验 scope,并基于该快照执行一次固定 implementation audit;审计只读非测试生产源码并检查核心接口标记和登记的高置信度危险模式,不替代完整 checklist,也不接受 Agent 提供的审计 receipt。
542
+ 项目不再变化且准备生成最终结论时,按 `aipay=AIPAY`、`webpay=WEBPAY`、`apppay=APPPAY` 得到 `PRODUCT_VARIANT`。下方唯一 runner 会重算项目快照、校验 scope,并执行固定 implementation audit;审计只读非测试生产源码,检查核心接口标记和登记的高置信度危险模式,不替代完整 checklist,也不接受 Agent 审计 receipt。
543
543
 
544
- 若审计发现问题且命令未传 `--finalize-audit-findings true`,stdout 固定为空,stderr 只接受唯一 `ALIPAY_AIPAY_INTERNAL:CHECKLIST_AUDIT={state:"REPAIR_REQUIRED",findings:[...]}`。本轮可修复时先修复,重新加载 checklist 阶段取得新 scope,再用更新后的实际证据重跑同一结论命令;不得渲染中间结果。确认无法在本轮修复时,向同一结论命令追加 `--finalize-audit-findings true`,由 runner 强制把结构化 findings 合入“未通过”并收口;该内部重跑不新增用户操作点。`full_process` 必须停在 Integration。
544
+ 若审计发现问题且命令未传 `--finalize-audit-findings true`,stdout 为空,stderr 只接受唯一 `ALIPAY_AIPAY_INTERNAL:CHECKLIST_AUDIT={state:"REPAIR_REQUIRED",findings:[...]}`。本轮可修复时先修复,重新加载 checklist 阶段取得新 scope,再用更新后的实际证据重跑同一结论命令,禁止渲染中间结果。无法修复时向同一命令追加 `--finalize-audit-findings true`,runner 将 findings 合入“未通过”并收口;该内部重跑不新增用户操作点。`full_process` 必须停在 Integration。
545
545
 
546
- 对客结论统一使用 `integration.checklist.result`;整体结论、失败/部分通过项和人工待验证项必须来自最终一轮 checklist 实际证据。首次调用前先按以下完整契约确定参数,禁止自由改写枚举:
546
+ 对客结论统一使用 `integration.checklist.result`;整体结论、失败/部分通过项和人工待验证项必须来自最终一轮 checklist 实际证据。首次调用前先按以下完整契约确定参数,禁止改枚举:
547
547
 
548
- - `variant` 固定映射 `aipay=AIPAY`、`webpay=WEBPAY`、`apppay=APPPAY`;`overallResult` 只允许“通过、部分通过、未通过”。
549
- - `failedItems` 与 `manualItems` 是包含确定下一步的受控单行值,没有对应项目时传“无”。`sandboxConfigState` 直接复用本轮脚本终态,只允许 `READY|CREATE_PENDING|VERIFY_PENDING`;`blockingDefectState` 中,除沙箱配置及其直接依赖外存在代码、安全或必要依据缺口时传 `PRESENT`,否则传 `NONE`。待配置时 `failedItems` 必须列出沙箱配置及依赖动作未完成;恢复口令只由步骤 1/3 已发送的 `sandbox.configuration.pending` 正文提供。
550
- - `executionMode` 在 `integration_only` 固定为“仅代码开发”,此时 `nextFlowReminder` 固定为“正式上线前还需完成支付产品-产品开通。”。`full_process` 固定为“完整接入”:`READY` 且整体“通过”时提醒“代码开发校验通过后将自动进入支付产品-产品开通;未通过时停留在代码开发。”;沙箱待配置、整体“部分通过”且 `blockingDefectState=NONE` 时提醒“沙箱配置尚未完成,但不阻塞本轮继续支付产品-产品开通;完成配置后再进行沙箱测试。”;`READY`、整体“部分通过”、`failedItems=无` 且仅有人工待验证项时提醒“代码开发自动校验已完成,仍有人工待验证项;本轮继续产品开通。”,并把 `manualItems` 原样带入 Onboarding Step 6。其他阻断结果停在 Integration。
551
- - 只有 `AIPAY` 传 `sandboxResult`:步骤 6 实际端到端联调通过时固定为“AI 按量付费沙箱服务端联调通过”,未运行、待配置或未通过时固定为“AI 按量付费沙箱服务端联调未通过”。只有 `WEBPAY` 传 `webpayExperienceResult`:按步骤 6 实际证据从“已提供浏览器付款入口和操作指引,实际付款及结果验证仍以用户完成情况为准”“未提供浏览器付款入口或完整操作指引,需先补齐”“已提供浏览器付款入口和操作指引;用户已完成一笔沙箱付款,结果只按实际核验证据记录”中选择。`APPPAY` 不传这两个参数。
548
+ - `variant` 映射 `aipay=AIPAY`、`webpay=WEBPAY`、`apppay=APPPAY`;`overallResult` 只允许“通过、部分通过、未通过”。
549
+ - `failedItems`/`manualItems` 必须是含下一步的受控单行值,无项目传“无”。`sandboxConfigState` 只允许 `READY|CREATE_PENDING|VERIFY_PENDING`;除沙箱配置及其直接依赖外存在代码、安全或必要依据缺口时 `blockingDefectState=PRESENT`,否则 `NONE`。待配置时 `failedItems` 必须列出沙箱配置及依赖动作未完成;恢复口令只由步骤 1/3 已发送的 `sandbox.configuration.pending` 正文提供。
550
+ - `executionMode`:`integration_only` 固定“仅代码开发”,`nextFlowReminder` 固定“正式上线前还需完成支付产品-产品开通。”。`full_process` 固定“完整接入”:`READY` 且“通过”时提醒“代码开发校验通过后将自动进入支付产品-产品开通;未通过时停留在代码开发。”;沙箱待配置且 `blockingDefectState=NONE` 时提醒“沙箱配置尚未完成,但不阻塞本轮继续支付产品-产品开通;完成配置后再进行沙箱测试。”;`READY`、`failedItems=无` 且仅有人工待验证项时提醒“代码开发自动校验已完成,仍有人工待验证项;本轮继续产品开通。”,并把 `manualItems` 原样带入 Onboarding Step 6。其他阻断结果停在 Integration。
551
+ - 仅 `AIPAY` 传 `sandboxResult`:步骤 6 端到端联调通过为“AI 按量付费沙箱服务端联调通过”,否则为“AI 按量付费沙箱服务端联调未通过”。仅 `WEBPAY` 传 `webpayExperienceResult`,按步骤 6 证据从三个登记值中选择:已提供入口指引且待用户实际付款、未提供完整入口指引、或用户已完成一笔沙箱付款且结果有核验证据。`APPPAY` 不传这两个参数。
552
552
 
553
553
  必须从本 flow 所在目录执行当前产品对应单行 runner;runner stdout 是唯一对客正文,必须把 stdout 原文作为当前回复正文发给用户:
554
554
 
555
555
  <!-- ALIPAY_AIPAY_PRODUCT:aipay:START -->
556
556
  ```bash
557
- node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message checklist-result --variant AIPAY --project-path "$PROJECT_PATH" --scope-receipt "$CHECKLIST_SCOPE_RECEIPT" --overall-result "$OVERALL_RESULT" --failed-items "$FAILED_ITEMS" --manual-items "$MANUAL_ITEMS" --sandbox-result "$SANDBOX_RESULT" --sandbox-config-state "$SANDBOX_CONFIG_STATE" --blocking-defect-state "$BLOCKING_DEFECT_STATE" --execution-mode "$EXECUTION_MODE" --next-flow-reminder "$NEXT_FLOW_REMINDER"
557
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message checklist-result --variant AIPAY --project-path "$PROJECT_PATH" --scope-receipt "$CHECKLIST_SCOPE_RECEIPT" --overall-result "$OVERALL_RESULT" --failed-items "$FAILED_ITEMS" --manual-items "$MANUAL_ITEMS" --sandbox-result "$SANDBOX_RESULT" --sandbox-config-state "$SANDBOX_CONFIG_STATE" --blocking-defect-state "$BLOCKING_DEFECT_STATE" --execution-mode "$EXECUTION_MODE" --next-flow-reminder "$NEXT_FLOW_REMINDER" --user-input-count-total <total> --user-input-count-after-flow-start <afterFlowStart> --flow-start-observed <true|false>
558
558
  ```
559
559
  <!-- ALIPAY_AIPAY_PRODUCT:END -->
560
560
  <!-- ALIPAY_AIPAY_PRODUCT:webpay:START -->
561
561
  ```bash
562
- node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message checklist-result --variant WEBPAY --project-path "$PROJECT_PATH" --scope-receipt "$CHECKLIST_SCOPE_RECEIPT" --overall-result "$OVERALL_RESULT" --failed-items "$FAILED_ITEMS" --manual-items "$MANUAL_ITEMS" --webpay-experience-result "$WEBPAY_EXPERIENCE_RESULT" --sandbox-config-state "$SANDBOX_CONFIG_STATE" --blocking-defect-state "$BLOCKING_DEFECT_STATE" --execution-mode "$EXECUTION_MODE" --next-flow-reminder "$NEXT_FLOW_REMINDER"
562
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message checklist-result --variant WEBPAY --project-path "$PROJECT_PATH" --scope-receipt "$CHECKLIST_SCOPE_RECEIPT" --overall-result "$OVERALL_RESULT" --failed-items "$FAILED_ITEMS" --manual-items "$MANUAL_ITEMS" --webpay-experience-result "$WEBPAY_EXPERIENCE_RESULT" --sandbox-config-state "$SANDBOX_CONFIG_STATE" --blocking-defect-state "$BLOCKING_DEFECT_STATE" --execution-mode "$EXECUTION_MODE" --next-flow-reminder "$NEXT_FLOW_REMINDER" --user-input-count-total <total> --user-input-count-after-flow-start <afterFlowStart> --flow-start-observed <true|false>
563
563
  ```
564
564
  <!-- ALIPAY_AIPAY_PRODUCT:END -->
565
565
  <!-- ALIPAY_AIPAY_PRODUCT:apppay:START -->
566
566
  ```bash
567
- node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message checklist-result --variant APPPAY --project-path "$PROJECT_PATH" --scope-receipt "$CHECKLIST_SCOPE_RECEIPT" --overall-result "$OVERALL_RESULT" --failed-items "$FAILED_ITEMS" --manual-items "$MANUAL_ITEMS" --sandbox-config-state "$SANDBOX_CONFIG_STATE" --blocking-defect-state "$BLOCKING_DEFECT_STATE" --execution-mode "$EXECUTION_MODE" --next-flow-reminder "$NEXT_FLOW_REMINDER"
567
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message checklist-result --variant APPPAY --project-path "$PROJECT_PATH" --scope-receipt "$CHECKLIST_SCOPE_RECEIPT" --overall-result "$OVERALL_RESULT" --failed-items "$FAILED_ITEMS" --manual-items "$MANUAL_ITEMS" --sandbox-config-state "$SANDBOX_CONFIG_STATE" --blocking-defect-state "$BLOCKING_DEFECT_STATE" --execution-mode "$EXECUTION_MODE" --next-flow-reminder "$NEXT_FLOW_REMINDER" --user-input-count-total <total> --user-input-count-after-flow-start <afterFlowStart> --flow-start-observed <true|false>
568
568
  ```
569
569
  <!-- ALIPAY_AIPAY_PRODUCT:END -->
570
570
 
@@ -10,7 +10,7 @@ import { fileURLToPath } from 'node:url';
10
10
 
11
11
  import { isWindowsPlatform } from './platform_compat.mjs';
12
12
  import { renderMessage } from './render_customer_message.mjs';
13
- import { emitTerminalMetric } from './telemetry.mjs';
13
+ import { emitTerminalMetric, terminalMetricContextFromArgs } from './telemetry.mjs';
14
14
 
15
15
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
16
16
  const inspectorPath = path.join(scriptDir, 'project_route_inspector.sh');
@@ -1231,7 +1231,7 @@ function runProductClarify() {
1231
1231
  function runProductUnsupported(args) {
1232
1232
  return renderMessage('product.unsupported', 'DEFAULT', {
1233
1233
  productName: required(args, 'product-name')
1234
- });
1234
+ }, terminalMetricContextFromArgs(args));
1235
1235
  }
1236
1236
 
1237
1237
  function runWindowsSandboxSetup() {
@@ -1357,7 +1357,7 @@ async function runChecklistResult(args) {
1357
1357
  }
1358
1358
  const rendered = renderMessage('integration.checklist.result', variant, input);
1359
1359
  if (input.executionMode === '仅代码开发') {
1360
- emitTerminalMetric({ rootIntent: 'integration_only', productType: variant });
1360
+ emitTerminalMetric({ rootIntent: 'integration_only', productType: variant, ...terminalMetricContextFromArgs(args) });
1361
1361
  }
1362
1362
  return rendered;
1363
1363
  }
@@ -6,7 +6,12 @@ import process from 'node:process';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { isWindowsPlatform } from './platform_compat.mjs';
8
8
  import { buildWindowsOnboardingRedirectUrl, loadMccRecords, renderMessage } from './render_customer_message.mjs';
9
- import { currentRunContext, emitStageMetricAsync, emitTerminalMetric } from './telemetry.mjs';
9
+ import {
10
+ currentRunContext,
11
+ emitStageMetricAsync,
12
+ emitTerminalMetric,
13
+ terminalMetricContextFromArgs
14
+ } from './telemetry.mjs';
10
15
 
11
16
  const internalPrefix = 'ALIPAY_AIPAY_INTERNAL:';
12
17
  const productTypes = {
@@ -120,6 +125,10 @@ function required(args, name) {
120
125
  return value;
121
126
  }
122
127
 
128
+ function optional(args, name) {
129
+ return typeof args[name] === 'string' && args[name].length > 0 ? args[name] : undefined;
130
+ }
131
+
123
132
  function productFor(args) {
124
133
  const productType = required(args, 'product-type');
125
134
  const mapping = productTypes[productType];
@@ -140,7 +149,7 @@ async function readStdin() {
140
149
  return input.trim() ? JSON.parse(input) : {};
141
150
  }
142
151
 
143
- function runPlatformEntry() {
152
+ function runPlatformEntry(args = {}) {
144
153
  if (!isWindowsPlatform()) return '';
145
154
  const runContext = currentRunContext();
146
155
  if (!runContext || runContext.terminal || !['onboarding_only', 'full_process'].includes(runContext.root_intent)) {
@@ -148,12 +157,7 @@ function runPlatformEntry() {
148
157
  }
149
158
  return renderMessage('onboarding.windows.redirect', 'DEFAULT', {
150
159
  officialUrl: buildWindowsOnboardingRedirectUrl(runContext.run_id)
151
- });
152
- }
153
-
154
- function optional(args, name) {
155
- const value = args[name];
156
- return typeof value === 'string' && value.length > 0 ? value : undefined;
160
+ }, terminalMetricContextFromArgs(args));
157
161
  }
158
162
 
159
163
  function parseJsonArg(args, name, label) {
@@ -174,7 +178,7 @@ function runEnterpriseUnsupported(args) {
174
178
  if (!['ONBOARDING_ONLY', 'FULL_PROCESS'].includes(variant)) {
175
179
  throw new Error('variant 非法,仅支持 ONBOARDING_ONLY、FULL_PROCESS');
176
180
  }
177
- return renderMessage('onboarding.enterprise.unsupported', variant, {});
181
+ return renderMessage('onboarding.enterprise.unsupported', variant, {}, terminalMetricContextFromArgs(args));
178
182
  }
179
183
 
180
184
  async function runMccClarify(args) {
@@ -747,7 +751,7 @@ function closeoutInputFromArgs(args) {
747
751
  async function runCloseout(args) {
748
752
  const variables = closeoutInputFromArgs(args);
749
753
  const rendered = renderMessage('process.partial_result', 'DEFAULT', variables);
750
- emitTerminalMetric({ productName: variables.productName });
754
+ emitTerminalMetric({ productName: variables.productName, ...terminalMetricContextFromArgs(args) });
751
755
  return rendered;
752
756
  }
753
757
 
@@ -826,7 +830,7 @@ function runApplicationResult(args) {
826
830
  export async function runOnboardingMessageCli(argv = process.argv) {
827
831
  const { command, args } = parseArgs(argv);
828
832
  if (command === 'platform-entry') {
829
- const message = runPlatformEntry();
833
+ const message = runPlatformEntry(args);
830
834
  if (message) console.log(message);
831
835
  return;
832
836
  }
@@ -762,12 +762,12 @@ const managedEntrypoints = {
762
762
  allowedCallers: ['error_handler.sh']
763
763
  },
764
764
  'application.key.page': {
765
- command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" app key --defer-open <appId> <publicKey>,页面 stdout 独立送达后使用同一入口执行 app open-key-page <appId>',
765
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" app key --product-type <aipay|webpay|apppay> --defer-open <appId> <publicKey>,页面 stdout 独立送达后使用同一入口执行 app open-key-page <appId>',
766
766
  reason: 'app.sh 校验 URL 并托管页面。',
767
767
  allowedCallers: ['app.sh']
768
768
  },
769
769
  'application.key.pending': {
770
- command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" app verify-key <appId> [publicKey] 或使用同一入口执行 app verify-key-and-audit <appId> [publicKey]',
770
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" app verify-key --product-type <aipay|webpay|apppay> <appId> [publicKey] 或使用同一入口执行 app verify-key-and-audit --product-type <aipay|webpay|apppay> <appId> [publicKey]',
771
771
  reason: 'app.sh 实际查询结果。',
772
772
  allowedCallers: ['app.sh']
773
773
  },
@@ -126,6 +126,12 @@ function assertEnumOption(options, name, allowedValues, { required = true } = {}
126
126
  return value;
127
127
  }
128
128
 
129
+ function appendOptionalTerminalCountOptions(targetArgs, options) {
130
+ for (const name of ['user-input-count-total', 'user-input-count-after-flow-start', 'flow-start-observed']) {
131
+ if (Object.prototype.hasOwnProperty.call(options, name)) targetArgs.push(`--${name}`, requiredOption(options, name));
132
+ }
133
+ }
134
+
129
135
  async function startTelemetryRun(rootIntent, productType) {
130
136
  const telemetryArgs = ['start-run', '--root-intent', rootIntent];
131
137
  if (productType) telemetryArgs.push('--product-type', productType);
@@ -145,12 +151,20 @@ async function runIntegrationBootstrap(args) {
145
151
 
146
152
  async function runOnboardingBootstrap(args) {
147
153
  const options = parseRuntimeArgs(args);
148
- assertOnlyOptions(options, ['root-intent', 'product-type']);
154
+ assertOnlyOptions(options, [
155
+ 'root-intent',
156
+ 'product-type',
157
+ 'user-input-count-total',
158
+ 'user-input-count-after-flow-start',
159
+ 'flow-start-observed'
160
+ ]);
149
161
  const rootIntent = assertEnumOption(options, 'root-intent', ['onboarding_only']);
150
162
  const productType = assertEnumOption(options, 'product-type', ['aipay', 'webpay', 'apppay'], { required: false });
151
163
  await startTelemetryRun(rootIntent, productType);
152
164
  const { runOnboardingMessageCli } = await import('./onboarding_message_runner.mjs');
153
- await runOnboardingMessageCli(cliArgv(['platform-entry']));
165
+ const platformEntryArgs = ['platform-entry'];
166
+ appendOptionalTerminalCountOptions(platformEntryArgs, options);
167
+ await runOnboardingMessageCli(cliArgv(platformEntryArgs));
154
168
  const { isWindowsPlatform } = await import('./platform_compat.mjs');
155
169
  if (isWindowsPlatform()) return;
156
170
  const { runFlowCli } = await import('./flow_loader.mjs');
@@ -325,7 +325,7 @@ export function enqueueMetricEvent(event, env = process.env) {
325
325
 
326
326
  function safeRunId(value) {
327
327
  if (typeof value !== 'string' || !value.trim()) return '';
328
- // Sync-log uses comma-separated columns and ^/= delimiters without value escaping.
328
+ // Run IDs also participate in state keys and param4 matching, so keep them separator-free.
329
329
  return /[\u0000-\u001f\u007f,^=]/u.test(value) ? '' : value;
330
330
  }
331
331
 
@@ -375,6 +375,51 @@ function productTypeFrom({ ruleProductType, variantName, variables = {}, context
375
375
  return 'UNKNOWN';
376
376
  }
377
377
 
378
+ function parseNonNegativeInteger(value) {
379
+ if (typeof value === 'number') return Number.isSafeInteger(value) && value >= 0 ? value : null;
380
+ if (typeof value !== 'string' || !/^(?:0|[1-9]\d*)$/.test(value)) return null;
381
+ const parsed = Number(value);
382
+ return Number.isSafeInteger(parsed) ? parsed : null;
383
+ }
384
+
385
+ function parseBooleanFlag(value) {
386
+ if (typeof value === 'boolean') return value;
387
+ if (typeof value !== 'string') return null;
388
+ const normalized = normalizeSwitch(value);
389
+ if (normalized === 'true' || normalized === '1') return true;
390
+ if (normalized === 'false' || normalized === '0') return false;
391
+ return null;
392
+ }
393
+
394
+ function terminalExtInfoFromPayload(payload = {}) {
395
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return '';
396
+ const total = parseNonNegativeInteger(payload.userInputCountTotal ?? payload.user_input_count_total);
397
+ const afterFlowStart = parseNonNegativeInteger(
398
+ payload.userInputCountAfterFlowStart ?? payload.user_input_count_after_flow_start
399
+ );
400
+ const flowStartObserved = parseBooleanFlag(payload.flowStartObserved ?? payload.flow_start_observed);
401
+ if (total === null || afterFlowStart === null || flowStartObserved === null) return '';
402
+ if (afterFlowStart > total) return '';
403
+ if (!flowStartObserved && afterFlowStart !== 0) return '';
404
+ return JSON.stringify({
405
+ user_input_count_total: total,
406
+ user_input_count_after_flow_start: afterFlowStart,
407
+ flow_start_observed: flowStartObserved
408
+ });
409
+ }
410
+
411
+ function terminalExtInfoFromEvent(event) {
412
+ if (event?.stage !== 'run.terminal') return '';
413
+ if (typeof event.ext_info === 'string') {
414
+ try {
415
+ return terminalExtInfoFromPayload(JSON.parse(event.ext_info));
416
+ } catch {
417
+ return '';
418
+ }
419
+ }
420
+ return terminalExtInfoFromPayload(event?.ext_info);
421
+ }
422
+
378
423
  export function stageMetricEvent(stage, context = {}, nowMs = Date.now(), env = process.env) {
379
424
  const rule = stageRules.get(stage);
380
425
  if (!rule) return null;
@@ -398,6 +443,10 @@ export function stageMetricEvent(stage, context = {}, nowMs = Date.now(), env =
398
443
  if (!canonicalProductTypes.has(event.product_type)) event.product_type = 'UNKNOWN';
399
444
  const rootIntent = rootIntentFrom(context, env);
400
445
  if (rootIntent) event.root_intent = rootIntent;
446
+ if (event.stage === 'run.terminal') {
447
+ const extInfo = terminalExtInfoFromPayload(context);
448
+ if (extInfo) event.ext_info = extInfo;
449
+ }
401
450
  return event;
402
451
  }
403
452
 
@@ -508,12 +557,25 @@ function parseCliArgs(argv) {
508
557
  return { command, args };
509
558
  }
510
559
 
560
+ export function terminalMetricContextFromArgs(args) {
561
+ const optional = (name) => {
562
+ const value = args[name];
563
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
564
+ };
565
+ return {
566
+ userInputCountTotal: optional('user-input-count-total'),
567
+ userInputCountAfterFlowStart: optional('user-input-count-after-flow-start'),
568
+ flowStartObserved: optional('flow-start-observed')
569
+ };
570
+ }
571
+
511
572
  function contextFromCliArgs(args) {
512
573
  return {
513
574
  runId: args['run-id'],
514
575
  rootIntent: args['root-intent'],
515
576
  productType: args['product-type'],
516
- productName: args['product-name']
577
+ productName: args['product-name'],
578
+ ...terminalMetricContextFromArgs(args)
517
579
  };
518
580
  }
519
581
 
@@ -762,19 +824,22 @@ function monitorEnv(env = process.env) {
762
824
  export function buildSyncLogReport(event, env = process.env) {
763
825
  if (!event) return null;
764
826
  const skillVersion = skillVersionFromDisk();
827
+ const item = {
828
+ code: syncLogCode,
829
+ msg: syncLogMsg,
830
+ d1: normalizeOS(),
831
+ d2: platformFromEnv(env),
832
+ d3: modelFromEnv(env),
833
+ d4: skillVersion,
834
+ d5: event.product_type || 'UNKNOWN',
835
+ c1: event.run_id,
836
+ c2: event.stage,
837
+ c3: event.timestamp
838
+ };
839
+ const terminalExtInfo = terminalExtInfoFromEvent(event);
840
+ if (terminalExtInfo) item.c4 = terminalExtInfo;
765
841
  return {
766
- item: {
767
- code: syncLogCode,
768
- msg: syncLogMsg,
769
- d1: normalizeOS(),
770
- d2: platformFromEnv(env),
771
- d3: modelFromEnv(env),
772
- d4: skillVersion,
773
- d5: event.product_type || 'UNKNOWN',
774
- c1: event.run_id,
775
- c2: event.stage,
776
- c3: event.timestamp
777
- },
842
+ item,
778
843
  options: {
779
844
  Env: monitorEnv(env),
780
845
  SprintID: skillVersion,
@@ -788,10 +853,19 @@ function serializeSyncLogParam4(param4) {
788
853
  return Object.keys(param4)
789
854
  .filter((key) => param4[key] !== undefined && param4[key] !== null)
790
855
  .sort()
791
- .map((key) => `${key}=${param4[key]}`)
856
+ .map((key) => `${key}=${encodeSyncLogParam4Value(param4[key])}`)
792
857
  .join('^');
793
858
  }
794
859
 
860
+ function encodeSyncLogParam4Value(value) {
861
+ const text = String(value ?? '');
862
+ try {
863
+ return encodeURIComponent(text).replace(/'/g, '%27');
864
+ } catch {
865
+ return encodeURIComponent(text.replace(/[\uD800-\uDFFF]/g, '\uFFFD')).replace(/'/g, '%27');
866
+ }
867
+ }
868
+
795
869
  export function buildSyncLogLine(userID, param4, date = new Date()) {
796
870
  const pad = (value, length = 2) => String(value).padStart(length, '0');
797
871
  const clientLogTime = [
@@ -1332,7 +1406,16 @@ export async function runTelemetryCli(argv = process.argv) {
1332
1406
  emitTelemetryError(new Error('invalid telemetry arguments'));
1333
1407
  return 0;
1334
1408
  }
1335
- assertCliArgs(args, new Set(['run-id', 'root-intent', 'product-type', 'product-name', 'timestamp-ms']), 1);
1409
+ assertCliArgs(args, new Set([
1410
+ 'run-id',
1411
+ 'root-intent',
1412
+ 'product-type',
1413
+ 'product-name',
1414
+ 'timestamp-ms',
1415
+ 'user-input-count-total',
1416
+ 'user-input-count-after-flow-start',
1417
+ 'flow-start-observed'
1418
+ ]), 1);
1336
1419
  const event = stageMetricEvent(args._[0], contextFromCliArgs(args), timestampFromCliArgs(args));
1337
1420
  if (event) updateExistingRunStateFromEvent(event);
1338
1421
  emitMetricEvent(event);
@@ -1,27 +1,13 @@
1
1
  # Skill 启动器自更新契约
2
2
 
3
- `SKILL.md` 是最小启动器。首次使用 `alipay-aipay` 时,必须在读取入口、flow、模块、消息目录或业务脚本前检查一次版本:解析根目录为规范化绝对路径 `<SKILL_DIR>`,在当前对话记录 `ALIPAY_AIPAY_SELF_UPDATE_CHECKED=true`,后续确认、恢复、中断或 Full Process 衔接均不重查。该事实不落盘、不跨对话,也不是业务进度。执行:
3
+ 首次使用 `alipay-aipay` 时,必须在读取入口、flow、模块、消息目录或业务脚本前检查一次版本,并记录 `ALIPAY_AIPAY_SELF_UPDATE_CHECKED=true`:
4
4
 
5
5
  ```text
6
6
  node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" self-update check --skill-dir "<SKILL_DIR>"
7
7
  ```
8
8
 
9
- `<SKILL_DIR>` 必须是当前这份包含 updater 的 Skill 规范化绝对路径,不得换成启动目录、项目目录、Home、npm 源码根或其他副本。命令不得改成变量赋值、命令替换、相对路径或特定 shell 语法;`VERSION` 或 `references/runtime-entry.md` 缺失不改变该绑定,由检查器进入精确修复。
9
+ `<SKILL_DIR>` 必须是当前这份包含 updater 的 Skill 规范化绝对路径,不得换成启动目录、项目目录、Home、npm 源码根或其他副本。命令不得改成变量赋值、命令替换、相对路径或特定 shell 语法;不透传 npm/npx 原始输出。
10
10
 
11
- stderr 必须恰有一个下列 `SELF_UPDATE:*` 内部终态;不对客,不透传 npm/npx 原始输出。
11
+ 唯一 stderr 终态:`SELF_UPDATE:CURRENT=<合法 SemVer>`、`SELF_UPDATE:UPDATED=<合法 SemVer>`、`SELF_UPDATE:RETRY_WITH_NETWORK`、`SELF_UPDATE:SKIPPED_NETWORK_RESTRICTED`、`SELF_UPDATE:CHECK_FAILED`、`SELF_UPDATE:INSTALL_FAILED`、`SELF_UPDATE:VERIFY_FAILED`。
12
12
 
13
- | marker | 含义与动作 |
14
- |---|---|
15
- | `SELF_UPDATE:CURRENT=<合法 SemVer>` | 当前版本不低于 npm latest;读取当前入口。 |
16
- | `SELF_UPDATE:UPDATED=<合法 SemVer>` | 已达目标版本;停止读旧版,读取同一根目录的新版入口。 |
17
- | `SELF_UPDATE:RETRY_WITH_NETWORK` | registry 查询或安装网络失败;取权后原样重试一次。 |
18
- | `SELF_UPDATE:SKIPPED_NETWORK_RESTRICTED` | 宿主显式标记联网受限,跳过 npm 查询并读当前入口。 |
19
- | `SELF_UPDATE:CHECK_FAILED` | 当前目录绑定、入口参数、npm 结果解析或本地包管理器检查失败;读取仍可用的当前入口。 |
20
- | `SELF_UPDATE:INSTALL_FAILED` | 安装失败或超时且未验证到目标版本;读当前入口。 |
21
- | `SELF_UPDATE:VERIFY_FAILED` | 安装返回成功但版本未达标或仍不合法;读当前入口。 |
22
-
23
- - 所有结果保留一次性事实。仅 `RETRY_WITH_NETWORK` 可取权后原样重试一次;拒绝、不支持或再次返回时读当前入口。`UPDATED` 使用原子替换后的新版,其余使用当前可用版。marker 缺失、重复或不唯一也读当前入口,不得称最新或更新成功。
24
- - 每次检查只查询一次 npm latest(最多 5 秒)。本地 `VERSION` 合法且不低于 latest 时直接 `CURRENT`;`VERSION` 缺失、非法、落后或业务入口缺失时,只运行一次查询所得精确版本的隐藏安装入口(最多 60 秒),原子覆盖当前 Skill 目录并重读 `VERSION`,达标才输出 `UPDATED`。
25
- - 自更新不调用公开 `install --target all`,不读取普通 Agent 批量目标,也不通过分隔列表传递路径。用户显式执行 `npx -y @alipay/alipay-aipay@latest install` 时仍按既有规则安装到 Home 下全部已存在的 `.agents/.codex/.claude` 根目录;均不存在时回退 `~/.agents/skills`。
26
- - npm/npx 超时会终止同一次调用的进程树,再按本次私有事务恢复完整新版本或原当前目录并清理 staging/backup;进程树结束前不返回 marker,也不允许网络恢复重试。
27
- - 失败 fail-open 且对客静默。上述一次网络恢复外,不重查、取权、用 `sudo`、要求用户操作或写更新状态;版本确定后才初始化遥测、平台门禁和业务流程。
13
+ `UPDATED` 使用原子替换后的新版,其余使用当前可用版;marker 缺失、重复或不唯一时读当前入口,不得称最新或更新成功。`UPDATED` 后停止读旧版,读取同一根目录的新版入口;仅 `RETRY_WITH_NETWORK` 可取权后原样重试一次,拒绝、不支持或再次返回时读当前入口。上述一次网络恢复外,不重查、取权、用 `sudo`、要求用户操作或写更新状态。每次只查一次 npm latest;`VERSION` 缺失、非法、落后或业务入口缺失时只安装精确版本一次。不调用公开 `install --target all`;超时会终止同一次调用的进程树并恢复清理。
@@ -1,27 +1,6 @@
1
- # 遥测维护参考
2
-
3
- 本文件只供维护、测试和故障排查按需读取;业务运行默认只执行 `runtime-entry.md` 与动作 runner/script 登记的入口。
4
-
5
- ## 运行契约
6
-
7
- - 固定 stage 枚举、Yuyan 字段和低敏维度以 `scripts/telemetry.mjs` 的 `approvedStages`、事件构造器和测试为准。
8
- - `start-run` 始终创建或复用当前流程 `run_id`;该私有 run context 是 alipay-cli `PLATFORM_ID` 和终态生命周期的运行事实,不依赖事件采集开关。动作 runner/script 在遥测开启时使用异步子进程记录 stage。缺少 run、遥测关闭、落盘失败、网络失败和非 2xx 都不得改变业务 stdout、退出语义或 flow 转移。
9
- - 前台只写用户系统临时目录下带 `alipay-aipay` 标识的私有 outbox,并启动后台补传;collector 返回 HTTP 2xx 后才删除记录。维护/测试进程不复用默认 run state,默认不远端上传。
10
- - Benchmark 只能使用 `ALIPAY_AIPAY_TELEMETRY_CAPTURE_ONLY=1` 把 stage 写入每个样本的隔离 outbox;该模式不调度 uploader,`telemetryUploadEnabled`、`flushOutbox` 和权限补传判定均固定禁用远端上传。这些记录只用于当前本地性能样本,不得进入生产补传队列。
11
- - `PLATFORM_ID` 必须等于当前流程 `run_id`,遥测开关不得令其缺失或改变;模型维度只读 `AIPAY_MODEL`,禁止读取用户项目通用 `MODEL` 推断。
12
- - 遥测内部行统一使用 `ALIPAY_AIPAY_INTERNAL:` 前缀,禁止对客。
13
-
14
- ## Run 生命周期
15
-
16
- - 根入口用 `root_intent + product_type` 判定复用或新建;组合不变时不按经过时长失效,`UNKNOWN` 补全为明确产品仍复用同一 `run_id`。只有明确换产品、切换根意图或已有 run 终态才重建;用户只说“重新开始/重新走流程/作废本轮流程”但组合没有变化时继续复用。
17
- - 若宿主提供 `COZE_PROJECT_ID`、`MEOO_PROJECT_ID`,或 `MIAODA` 环境下的 `VITE_APP_ID`/`PLATFORM_ID`,新建 run 优先使用该项目 ID,不得被显式 run ID 或本地生成值覆盖。当前宿主项目 ID 与已有状态不兼容时静默跳过不兼容事件且不改写 run state。
18
- - `integration_only` 由 checklist 最终消息终止;`full_process` 的 Integration 收口不终止。`onboarding_only|full_process` 由 Onboarding closeout、企业账号不支持或 Windows 站点引导消息终止。
19
- - 查看当前主键只使用 `current-run-id`,不得为查看而新建 run。关闭遥测只停止 `METRIC_EVENT`、outbox、上传和 flush,不停止 run context 的创建、读取与终态记账;业务脚本继续继承 `PLATFORM_ID=<run_id>`。Agent 不手写环境变量前缀或改写 MCP payload。
20
-
21
- ## 诊断与补传
22
-
23
- `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 结果、失败或待重试结果不得作为业务转移或完成条件。
24
-
25
- ## 修改要求
26
-
27
- 修改 stage、run 生命周期、outbox 或上传逻辑时,必须覆盖遥测关闭、缺少 run、写盘失败、无网络、非 2xx、并发 claim、终态 run 和 `PLATFORM_ID` 继承测试。不得把实现细节复制回 `runtime-entry.md` 或两个主 flow。
1
+ - stage、Yuyan 字段和低敏维度以 `scripts/telemetry.mjs` 与测试为准。sync-log `param4` value 按 Yuyan Monitor SDK 单独 URL 编码后再整体编码,禁用裸 JSON、CSV 引号或手写分隔符。遥测关闭、缺 run、落盘/网络/上传失败或非 2xx 不改变业务 stdout、退出语义或 flow;内部行用 `ALIPAY_AIPAY_INTERNAL:`。
2
+ - `start-run` 创建或复用当前 `run_id`;业务脚本继承 `PLATFORM_ID=<run_id>`,Agent 不手写环境前缀或改 MCP payload。outbox 只写系统临时目录,collector HTTP 2xx 后才删。维护/测试进程不复用默认 run state,默认不远端上传。Benchmark 只能用 `ALIPAY_AIPAY_TELEMETRY_CAPTURE_ONLY=1` 写隔离 outbox;该模式禁用上传、flush 和权限补传。
3
+ - 根入口用 `root_intent + product_type` 判定复用或新建;组合不变时不按时长失效,`UNKNOWN` 补全为明确产品仍复用同一 `run_id`。只有明确换产品、切换根意图或已有 run 终态才重建;用户只说“重新开始/重新走流程/作废本轮流程”但组合未变时继续复用。`COZE_PROJECT_ID`、`MEOO_PROJECT_ID`、`MIAODA` 下的 `VITE_APP_ID`/`PLATFORM_ID` 优先作为新 run ID。
4
+ - `integration_only` 由 checklist 最终消息终止;`full_process` 的 Integration 收口不终止。`onboarding_only|full_process` 由 Onboarding closeout、企业账号不支持或 Windows 站点引导消息终止。查看主键只用 `current-run-id`,不得为查看而新建 run。
5
+ - Agent 在当前上下文计数:`user_input_count_total` 从触发当前 `run_id` 的用户消息开始;`user_input_count_after_flow_start` 从进入 `integration.user_confirmed` 或 `onboarding.user_confirmed` 后的下一条用户消息开始;`flow_start_observed` 表示已进入正式 flow start,`full_process` 不因进入 Onboarding 重置。只计用户发给 Agent 的消息;用户直接 kill 且没有执行 `run.terminal` 时不补报。终态命令只追加 `--user-input-count-total`、`--user-input-count-after-flow-start`、`--flow-start-observed`;不得新增工具调用、确认点、stage 或独立上报。`run.terminal` 的 Yuyan `c4` 字段名为 `ext_info`,三项合法时写 minified JSON;非法、缺失、`after_flow_start > total` 或 `flow_start_observed=false` 但 after-flow 非 0 时省略 `c4`。
6
+ - `telemetry upload-status` 只读诊断。网络失败且 outbox 积压时先执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" telemetry permissioned-flush-needed`;仅 `PERMISSIONED_FLUSH_NEEDED=true` 时申请一次联网权限运行 `telemetry flush-outbox`,同一 Agent 会话只请求一次。`flush-outbox` 只启动后台补传并立即返回;拒绝、后台失败或非 2xx 都保留 outbox。collector HTTP 结果、失败或待重试结果不得作为业务转移或完成条件。
@@ -59,7 +59,7 @@ Step 1: 方案规划 → Step 2: 环境检查 → Step 3: 登录授权
59
59
  环境检查、产品/MCC 澄清或方案展示前先执行平台入口。`onboarding_only` 正常由 `onboarding-bootstrap --root-intent onboarding_only` 包含本动作和 entry 加载;仅原子 fallback 或 `full_process` 衔接时执行:
60
60
 
61
61
  ```bash
62
- node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message platform-entry
62
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message platform-entry --user-input-count-total <total> --user-input-count-after-flow-start <afterFlowStart> --flow-start-observed <true|false>
63
63
  ```
64
64
 
65
65
  - Windows:runner 从私有 run context 取 `run_id` 生成 `platform_id`,禁止 Agent 传入或拼接;消息渲染即终结 run,送达唯一 stdout 后停止 Onboarding;`full_process` 保留 Integration 结果。
@@ -68,7 +68,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message plat
68
68
  用户明确要用企业账号通过 Skill 开通时执行下列消息并停止 Onboarding;不得主动询问账号类型,也不得从 MCC、营业执照或“公司”等描述推断。
69
69
 
70
70
  ```bash
71
- node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message enterprise-unsupported --variant <ONBOARDING_ONLY|FULL_PROCESS>
71
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message enterprise-unsupported --variant <ONBOARDING_ONLY|FULL_PROCESS> --user-input-count-total <total> --user-input-count-after-flow-start <afterFlowStart> --flow-start-observed <true|false>
72
72
  ```
73
73
 
74
74
  `onboarding_only` 固定 `ONBOARDING_ONLY`,`full_process` 固定 `FULL_PROCESS`。原文送达最终 stdout 后停止,不执行 Step 6 或追加收口。
@@ -496,17 +496,17 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message appl
496
496
 
497
497
  **完成条件**:当前登录、scope 和 MCC 必须有效;签约必须由实际查询确认为已生效;仅按量付费适用的服务分支必须复用或保存成功并取得实际 `serviceId`;应用必须达到 `ON_LINE`、应用公钥已生效且支付宝公钥已实际导出。存在失败、未知、待生效、待审核或人工配置时不得宣称产品开通完成。不维护人工完成条件数量。
498
498
 
499
- 使用 `../normal/json/customer-messages.json` 的 `process.partial_result` 消息逐分支收口,必须通过 `onboarding_message_runner.mjs closeout` 托管输出,并把 runner 完整 stdout 原文发送给用户。禁止用 Agent 手写短摘要替代、删减 `productionReadinessBlock`、删减 AI 网页应用收款限制引导或改写后续衔接;AI 网页应用收款收口缺少“【上线前有以下关键点】”和“AI 网页应用收款限制”时视为标准收口未送达,必须重新发送同一次 closeout stdout。自动重试耗尽、业务错误或查询失败且到本步骤仍未恢复的动作,统一写入对应分支结果和 `remainingActions`,只说明一次,不在收口后再次询问是否重试。必须执行:
499
+ 使用 `../normal/json/customer-messages.json` 的 `process.partial_result` 逐分支收口,必须通过 `onboarding_message_runner.mjs closeout` 托管输出并原文发送 stdout。禁止 Agent 手写短摘要、删减 `productionReadinessBlock`、删减 AI 网页应用收款限制引导或改写后续衔接;AI 网页应用收款收口缺少“【上线前有以下关键点】”和“AI 网页应用收款限制”时视为标准收口未送达,必须重发同一次 stdout。自动重试耗尽、业务错误或查询失败且仍未恢复的动作,统一写入分支结果和 `remainingActions`,只说明一次,不在收口后追问是否重试。必须执行:
500
500
 
501
501
  `SIGNING_RESULT`、`SERVICE_RESULT`、`APPLICATION_RESULT` 和 `INTEGRATION_RESULT` 必须使用消息目录允许的状态枚举原文,禁止拼接 `appId`、公钥文件路径、错误码、截图证据或其他自由文本;支付宝公钥保存路径只允许在已取得唯一 `ALIPAY_PUBLIC_KEY_EXPORT_STATUS=EXPORTED` 和 `ALIPAY_PUBLIC_KEY_FILE` 时,通过 `APP_KEY_SAVE_PATH` 或 `APPLICATION_OPERATION_APP_KEY_SAVE_PATH` 独立传入结果 runner。这些证据不能污染分支结果枚举。
502
502
 
503
503
  ```bash
504
- node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message closeout --product-type "$PRODUCT_TYPE" --root-intent "$ROOT_INTENT" --signing-result "$SIGNING_RESULT" --application-result "$APPLICATION_RESULT" --remaining-actions "$REMAINING_ACTIONS" [--integration-result "$INTEGRATION_RESULT"] [--service-result "$SERVICE_RESULT"] [--sandbox-config-state "$SANDBOX_CONFIG_STATE"] [--manual-verification-items "$MANUAL_VERIFICATION_ITEMS"] [--application-operation-app-id "$APPLICATION_OPERATION_APP_ID" [--application-operation-next-action "$APPLICATION_OPERATION_NEXT_ACTION"] [--application-operation-app-key-save-path "$APPLICATION_OPERATION_APP_KEY_SAVE_PATH"]]
504
+ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message closeout --product-type "$PRODUCT_TYPE" --root-intent "$ROOT_INTENT" --signing-result "$SIGNING_RESULT" --application-result "$APPLICATION_RESULT" --remaining-actions "$REMAINING_ACTIONS" [--integration-result "$INTEGRATION_RESULT"] [--service-result "$SERVICE_RESULT"] [--sandbox-config-state "$SANDBOX_CONFIG_STATE"] [--manual-verification-items "$MANUAL_VERIFICATION_ITEMS"] [--application-operation-app-id "$APPLICATION_OPERATION_APP_ID" [--application-operation-next-action "$APPLICATION_OPERATION_NEXT_ACTION"] [--application-operation-app-key-save-path "$APPLICATION_OPERATION_APP_KEY_SAVE_PATH"]] --user-input-count-total <total> --user-input-count-after-flow-start <afterFlowStart> --flow-start-observed <true|false>
505
505
  ```
506
506
 
507
- runner 从 `PRODUCT_TYPE` 唯一派生产品名,网站支付/APP 支付唯一派生服务结果“无需处理”,并从 `ROOT_INTENT` 派生代码开发栏和既有后续衔接;总结果标题只按下方完成条件派生。`onboarding_only` 省略 `--integration-result`、`--sandbox-config-state` 和 `--manual-verification-items`。`full_process` 中,代码开发全部通过时填“已完成”并传 `READY`;只有沙箱待配置且其余代码与安全检查通过时填“部分完成,沙箱配置待完成”,并原样传 `CREATE_PENDING|VERIFY_PENDING`;沙箱为 `READY` 且代码开发只剩非阻塞人工待验证项时填“部分完成,人工待验证”,传 `READY`,把代码开发 checklist 的 `manualItems` 原样作为 `MANUAL_VERIFICATION_ITEMS` 传入,并在 `REMAINING_ACTIONS` 中写明“完成下方人工待验证项”或同等实际待办,禁止传“无”。runner 只接受上方紧凑参数,拒绝旧完整参数、stdin JSON、`--input-json`、可派生字段和空字符串伪值。renderer 会校验结果与沙箱状态、人工待验证项一致,并派生恢复口令或人工待验证展示行;该展示不增加确认点或用户回复要求。不得在其 stdout 前后单独追加衔接提醒,不伪造本轮代码修改、沙箱创建或测试结果,也不重复进入 integration。
507
+ runner 从 `PRODUCT_TYPE` 派生产品名,网站支付/APP 支付派生服务结果“无需处理”,从 `ROOT_INTENT` 派生代码开发栏和后续衔接;标题只按完成条件派生。`onboarding_only` 省略 `--integration-result`、`--sandbox-config-state` 和 `--manual-verification-items`。`full_process`:代码开发通过填“已完成”并传 `READY`;仅沙箱待配置且其余代码与安全检查通过填“部分完成,沙箱配置待完成”并传 `CREATE_PENDING|VERIFY_PENDING`;沙箱 `READY` 且只剩非阻塞人工待验证项时填“部分完成,人工待验证”,传 `READY`,把 checklist `manualItems` 原样作为 `MANUAL_VERIFICATION_ITEMS`,并在 `REMAINING_ACTIONS` 写明实际待办,禁止传“无”。runner 拒绝旧完整参数、stdin JSON、`--input-json`、可派生字段和空字符串伪值。renderer 校验结果与沙箱状态/人工待验证项一致,并派生恢复口令或人工待验证展示;不增加确认点。不得在 stdout 前后追加衔接提醒,不伪造本轮代码修改、沙箱创建或测试结果,也不重复进入 integration。
508
508
 
509
- 若 `full_process` 最后一跳紧接着应用创建、审核提交或复用结果,不再单独输出一条 `application.operation.result` 后又立刻输出本收口消息;只记录同一 `APPLICATION_OPERATION_APP_ID`,runner 直接复用 `APPLICATION_RESULT` 作为实际状态;非“需人工配置”结果还必须记录下一步 `APPLICATION_OPERATION_NEXT_ACTION`。“需人工配置”的下一步由 closeout runner 从消息目录派生,禁止 Agent 传入。若同一动作已成功导出支付宝公钥,则同时传入 `APPLICATION_OPERATION_APP_KEY_SAVE_PATH`,收口必须展示“当前 appId: xxx 对应的支付宝公钥保存在:xxx”;“需人工配置”禁止传该路径。应用结果已为“已上线且配置完整”且没有保存路径时不再展开“应用分支结果”,由结果表和上线前检查块承接;已上线且有保存路径时只展开 appId、实际状态和保存路径;待审核、待设置公钥、需人工配置、失败或结果未知时合并展示为“应用分支结果”并保留完成态红线。`onboarding_only`、应用分支仍需等待用户补材料/确认,或应用操作结果不是紧邻 Step 6 最终收口时,仍按应用模块原规则输出 `application.operation.result`。合并展示不得改写应用完成条件,待审核、待设置公钥或待人工配置时仍不得表述为应用发布完成。
509
+ 若 `full_process` 最后一跳紧接应用创建、审核提交或复用,不再先输出 `application.operation.result` 再立刻 closeout;只记录同一 `APPLICATION_OPERATION_APP_ID`,runner 复用 `APPLICATION_RESULT`,非“需人工配置”还记录 `APPLICATION_OPERATION_NEXT_ACTION`;“需人工配置”的下一步由 runner 派生。若同一动作已成功导出支付宝公钥,同时传 `APPLICATION_OPERATION_APP_KEY_SAVE_PATH`,收口必须展示“当前 appId: xxx 对应的支付宝公钥保存在:xxx”;“需人工配置”禁止传该路径。已上线且无保存路径时不展开“应用分支结果”;已上线且有保存路径时只展开 appId、状态和路径;其他应用结果合并展示并保留完成态红线。`onboarding_only`、应用分支仍需用户补材料/确认,或应用操作结果不是紧邻 Step 6 收口时,仍按应用模块输出 `application.operation.result`。合并展示不得改写完成条件,待审核、待设置公钥或待人工配置仍不得表述为应用发布完成。
510
510
 
511
511
  当且仅当签约结果为“已提交,等待生效”且应用结果为“已提交审核,等待上线”时,renderer 从消息目录自动在 `remainingActions` 后追加状态查询提示;该提示不是 flow 入参,Agent 禁止自行传入、拼接或在模板外重复输出。其他结果组合不展示该提示。
512
512
 
@@ -41,14 +41,14 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" render <messageId> --va
41
41
  | 意图 | 判定 | 入口 | 后续按需加载 |
42
42
  |---|---|---|---|
43
43
  | `integration_only` | 只要求代码开发、支付集成或集成排障,未同时要求产品开通 | 执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-bootstrap --root-intent integration_only` | 当前阶段、产品和语言的契约、示例与脚本 |
44
- | `onboarding_only` | 只要求签约、入驻或产品开通,未同时要求代码开发 | 执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-bootstrap --root-intent onboarding_only` | 当前阶段的授权、查询或写分支模块 |
44
+ | `onboarding_only` | 只要求签约、入驻或产品开通,未同时要求代码开发 | 执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-bootstrap --root-intent onboarding_only --user-input-count-total <total> --user-input-count-after-flow-start <afterFlowStart> --flow-start-observed <true|false>` | 当前阶段的授权、查询或写分支模块 |
45
45
  | `full_process` | 明确要求完整接入、一站式,或同一请求同时要求代码开发和产品开通 | `normal/full-process-routing.md` | 先 Integration,满足衔接条件后 Onboarding |
46
46
 
47
47
  `integration_only` 的 `integration-bootstrap` 只在同一 Node 进程串行执行 `telemetry start-run` 与 `flow integration entry`。`onboarding_only` 的 `onboarding-bootstrap` 只串行执行 `telemetry start-run`、`onboarding-message platform-entry` 与 `flow onboarding entry`。两者均为既有无业务写入动作,执行后不得重复原子命令。Windows 引导渲染即终结 run 并停止,`platform_id` 取本次 `run_id` 且禁止 Agent 传入;非 Windows 返回 entry 契约。`full_process` 不使用 `integration-bootstrap`;衔接 Onboarding 也不使用带 `start-run` 的 bootstrap,仍执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message platform-entry` 后按需加载 entry,避免重复 `run.intent_detected`。
48
48
 
49
49
  `full_process` 识别根意图后立即执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" telemetry start-run --root-intent full_process`;产品已唯一确定时追加 `--product-type <aipay|webpay|apppay>`。`integration_only` 和 `onboarding_only` 已由上表各自 bootstrap 包含同一次 `start-run`,不得重复执行本条 telemetry。命令创建/复用当前流程 `run_id`,并在遥测开启时记录 `run.intent_detected`:同一意图和产品继续复用,`UNKNOWN` 补全产品仍复用同一 `run_id`;明确换产品、换根意图或既有 run 已终态才重建。第三方平台项目 ID 仍优先固定为 `run_id`,不得被显式 run ID 或本地生成值覆盖。业务 stage 只由登记 runner/script 异步记录。
50
50
 
51
- 遥测默认开启,`ALIPAY_AIPAY_TELEMETRY=0|false|off|no` 可关闭 `METRIC_EVENT`、outbox、上传和 flush;私有流程 run context 仍用于 `PLATFORM_ID` 和终态生命周期。落盘/网络/上传/flush 失败静默,不改变业务、消息或确认,stdout 为空且 stderr 内部行不对客。`integration_only` 由 checklist 终结;Onboarding 由 closeout 或登记的提前终止消息终结。脚本始终以当前流程 `run_id` 为 `PLATFORM_ID`,Agent 不手写环境前缀或改 MCP payload;维护诊断、outbox、上传和 flush 细节只在 `normal/telemetry.md` 按需读取。
51
+ 遥测默认开启,`ALIPAY_AIPAY_TELEMETRY=0|false|off|no` 可关闭 `METRIC_EVENT`、outbox、上传和 flush;私有流程 run context 仍用于 `PLATFORM_ID` 和终态生命周期。落盘/网络/上传/flush 失败静默,不改变业务、消息或确认,stdout 为空且 stderr 内部行不对客。`integration_only` 由 checklist 终结;Onboarding 由 closeout 或登记的提前终止消息终结。Agent 按 `normal/telemetry.md` 维护本轮用户输入计数,并只在终态命令追加可选参数,不新增工具调用、确认点或独立上报。脚本始终以当前流程 `run_id` 为 `PLATFORM_ID`,Agent 不手写环境前缀或改 MCP payload;维护诊断、outbox、上传和 flush 细节只在 `normal/telemetry.md` 按需读取。
52
52
 
53
53
  三种入口均只在无法从已有产品名称或业务描述可靠确定唯一产品时执行 flow 登记的 `product-clarify` runner;不得推定或手写澄清。产品明确但 MCC 尚无检索输入时使用 `mcc-clarify`;已有经营描述后先按 Onboarding flow 读取一次当前 `<SKILL_DIR>/references/onboarding/modules/mcc-reference.md` 做 Agent 语义候选,再统一调用 `mcc-resolve-plan` 由 runner 复核、合并和渲染;用户直接提供当前表内完整 MCC 编码时可跳过 Agent 泛化并由 runner 精确校验。不得拆回 `mcc-search + mcc-candidates`、先旧检索失败后临时猜测,或为方案展示再拼 renderer 输入。
54
54