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

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.3",
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.3
@@ -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
  }
@@ -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');
@@ -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,
@@ -1332,7 +1397,16 @@ export async function runTelemetryCli(argv = process.argv) {
1332
1397
  emitTelemetryError(new Error('invalid telemetry arguments'));
1333
1398
  return 0;
1334
1399
  }
1335
- assertCliArgs(args, new Set(['run-id', 'root-intent', 'product-type', 'product-name', 'timestamp-ms']), 1);
1400
+ assertCliArgs(args, new Set([
1401
+ 'run-id',
1402
+ 'root-intent',
1403
+ 'product-type',
1404
+ 'product-name',
1405
+ 'timestamp-ms',
1406
+ 'user-input-count-total',
1407
+ 'user-input-count-after-flow-start',
1408
+ 'flow-start-observed'
1409
+ ]), 1);
1336
1410
  const event = stageMetricEvent(args._[0], contextFromCliArgs(args), timestampFromCliArgs(args));
1337
1411
  if (event) updateExistingRunStateFromEvent(event);
1338
1412
  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` 与测试为准。遥测关闭、缺少 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;该模式不调度 uploader,`telemetryUploadEnabled`、`flushOutbox` 和权限补传判定禁用远端上传。
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` 时才申请一次可复用联网权限运行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" telemetry flush-outbox`,同一 Agent 会话只请求一次。`flush-outbox` 只启动后台补传并立即返回;用户拒绝、后台失败或非 2xx 都保留 outbox 并继续业务。collector HTTP 结果、失败或待重试结果不得作为业务转移或完成条件。
@@ -48,7 +48,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" render <messageId> --va
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