@thinkingai/ae-cli 6.0.52 → 6.0.53

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 (119) hide show
  1. package/README.md +7 -1
  2. package/README.zh.md +7 -1
  3. package/dist/{auth-XZNXFRJS.js → auth-LHM7NQPR.js} +167 -7
  4. package/dist/{capability-RJZ4KRGP.js → capability-F2RIR5DS.js} +11 -6
  5. package/dist/{chunk-57RRFUEZ.js → chunk-5XRQ3CZR.js} +4 -4
  6. package/dist/{chunk-L2QUIZTS.js → chunk-647BRKZK.js} +3 -3
  7. package/dist/{chunk-EQ255HKO.js → chunk-7WZACOAI.js} +2 -2
  8. package/dist/{chunk-DX5CDO34.js → chunk-BW7JUDEI.js} +84 -19
  9. package/dist/{chunk-7XGFSUOM.js → chunk-DU72X2IO.js} +271 -76
  10. package/dist/{chunk-V2NUHPXF.js → chunk-GK7WDY7E.js} +1 -1
  11. package/dist/{chunk-7FTTULED.js → chunk-IG3DYEIR.js} +4 -1
  12. package/dist/{chunk-HBGADCKA.js → chunk-KJDOTPYU.js} +3 -3
  13. package/dist/{chunk-4NRCTUZJ.js → chunk-KS4DE3MX.js} +4 -4
  14. package/dist/{chunk-JOCDD4ON.js → chunk-N4BLPJC7.js} +1 -1
  15. package/dist/{chunk-WEPZ5M3Z.js → chunk-PGCIKZRE.js} +7 -4
  16. package/dist/{chunk-HFGZGYCG.js → chunk-QYXDJOLA.js} +252 -6
  17. package/dist/{chunk-DVMLWQD4.js → chunk-SO5LKIWP.js} +2 -2
  18. package/dist/{chunk-MAPZ7VB6.js → chunk-YVGXGTD6.js} +13 -0
  19. package/dist/{community-report-client-CU22W53K.js → community-report-client-6VJDDMJJ.js} +1 -1
  20. package/dist/{config-XO3LI3SA.js → config-XQWOPDHN.js} +25 -6
  21. package/dist/{context-IFJI3LIT.js → context-M3HDGEIR.js} +4 -4
  22. package/dist/{data-integration-HNGUKFIC.js → data-integration-XBTCGXQT.js} +6 -6
  23. package/dist/index.js +77 -40
  24. package/dist/{local-data-upload-client-GTRIT64F.js → local-data-upload-client-3P7NIVQ4.js} +1 -1
  25. package/dist/{memory-QGPSNCQS.js → memory-6APEB2JB.js} +4 -4
  26. package/dist/{metadata-JAQW7KGZ.js → metadata-V5ZDTKWZ.js} +7 -7
  27. package/dist/{model-RNPQXIBI.js → model-ZS7HVGKN.js} +4 -4
  28. package/dist/{personal-semantic-preference-QCZ2IZTK.js → personal-semantic-preference-2XANU3F6.js} +8 -6
  29. package/dist/{project-semantic-64JURXKF.js → project-semantic-LP3LY7QP.js} +7 -7
  30. package/dist/{sync-O45NXEQY.js → sync-EQLSTUS7.js} +7 -7
  31. package/dist/{te-agent-K5MSPNHJ.js → te-agent-Z3HCTKR7.js} +348 -55
  32. package/dist/{te-analysis-KQKCAI5H.js → te-analysis-3EVYQ3IO.js} +803 -57
  33. package/dist/{te-community-4OYU3BDA.js → te-community-Z7JACGF3.js} +3 -3
  34. package/dist/{te-dataops-NFIPB6FX.js → te-dataops-F7GQOIS6.js} +644 -276
  35. package/dist/{te-engage-HSVSRW22.js → te-engage-LFBU6WWH.js} +6 -6
  36. package/dist/{te-kb-3TS73NO6.js → te-kb-QJLJEX5K.js} +323 -181
  37. package/dist/{te-system-STMHLS5J.js → te-system-IXC43QHZ.js} +6 -6
  38. package/dist/{te-team-3I5AOX3N.js → te-team-BIZIKIHZ.js} +6 -6
  39. package/dist/{update-RZXLPCRH.js → update-W2SGQNKT.js} +4 -4
  40. package/package.json +11 -4
  41. package/skills/ae-agent/SKILL.md +9 -4
  42. package/skills/ae-agent/references/notification.md +40 -0
  43. package/skills/ae-analysis/SKILL.md +64 -12
  44. package/skills/ae-analysis/references/agent_review_submit_to_page.md +2 -0
  45. package/skills/ae-analysis/references/ai_models/event.md +6 -1
  46. package/skills/ae-analysis/references/ai_models/funnel.md +7 -1
  47. package/skills/ae-analysis/references/ai_models/heat_map.md +23 -0
  48. package/skills/ae-analysis/references/ai_models/interval.md +24 -0
  49. package/skills/ae-analysis/references/ai_models/path.md +20 -0
  50. package/skills/ae-analysis/references/ai_models/prop_analysis.md +2 -0
  51. package/skills/ae-analysis/references/ai_models/rank_list.md +30 -0
  52. package/skills/ae-analysis/references/ai_models/retention.md +45 -1
  53. package/skills/ae-analysis/references/ai_models/revenue.md +20 -2
  54. package/skills/ae-analysis/references/ai_models/tag.md +2 -0
  55. package/skills/ae-analysis/references/ai_models.md +69 -0
  56. package/skills/ae-analysis/references/analysis_data_retrieval.md +4 -0
  57. package/skills/ae-analysis/references/asset_batch_info_export.md +10 -0
  58. package/skills/ae-analysis/references/asset_batch_sql_export.md +13 -1
  59. package/skills/ae-analysis/references/asset_export.md +12 -1
  60. package/skills/ae-analysis/references/asset_list.md +1 -0
  61. package/skills/ae-analysis/references/asset_search.md +11 -5
  62. package/skills/ae-analysis/references/bi_panel_list.md +1 -1
  63. package/skills/ae-analysis/references/catalog_list.md +1 -1
  64. package/skills/ae-analysis/references/collaboration.md +48 -0
  65. package/skills/ae-analysis/references/command_index.md +16 -15
  66. package/skills/ae-analysis/references/dashboard_list.md +4 -4
  67. package/skills/ae-analysis/references/dashboard_report_data_export.md +2 -0
  68. package/skills/ae-analysis/references/event_export.md +3 -1
  69. package/skills/ae-analysis/references/governance_recommendation_auto_review.md +71 -0
  70. package/skills/ae-analysis/references/governance_recommendation_export.md +11 -0
  71. package/skills/ae-analysis/references/metadata_resolution.md +3 -3
  72. package/skills/ae-analysis/references/metric_export.md +3 -1
  73. package/skills/ae-analysis/references/metric_list.md +1 -1
  74. package/skills/ae-analysis/references/operation_record_export.md +10 -0
  75. package/skills/ae-analysis/references/personal_semantic_preference_add.md +2 -2
  76. package/skills/ae-analysis/references/personal_semantic_preference_get.md +6 -4
  77. package/skills/ae-analysis/references/personal_semantic_preference_list.md +5 -3
  78. package/skills/ae-analysis/references/project_semantic_knowledge_wiki.md +1 -1
  79. package/skills/ae-analysis/references/property_export.md +3 -1
  80. package/skills/ae-analysis/references/report_create.md +4 -0
  81. package/skills/ae-analysis/references/report_data_export.md +6 -0
  82. package/skills/ae-analysis/references/report_get.md +2 -0
  83. package/skills/ae-analysis/references/report_list.md +5 -5
  84. package/skills/ae-analysis/references/report_update.md +6 -0
  85. package/skills/ae-analysis/references/sql_table_columns.md +4 -4
  86. package/skills/ae-analysis/references/sql_table_list.md +5 -5
  87. package/skills/ae-analysis/references/user_cluster_models.md +8 -0
  88. package/skills/ae-analysis/references/user_tag_create.md +7 -1
  89. package/skills/ae-analysis/references/user_tag_get.md +1 -1
  90. package/skills/ae-analysis/references/user_tag_models.md +18 -2
  91. package/skills/ae-analysis/references/user_tag_refresh.md +1 -1
  92. package/skills/ae-analysis/references/user_tag_update.md +2 -2
  93. package/skills/ae-capability/SKILL.md +63 -1
  94. package/skills/ae-capability/references/collaboration.md +48 -0
  95. package/skills/ae-community/SKILL.md +5 -1
  96. package/skills/ae-community/references/collaboration.md +48 -0
  97. package/skills/ae-data-integration/SKILL.md +4 -0
  98. package/skills/ae-data-integration/references/collaboration.md +48 -0
  99. package/skills/ae-dataops/SKILL.md +54 -74
  100. package/skills/ae-dataops/references/collaboration.md +48 -0
  101. package/skills/ae-dataops/references/dataops-backfill.md +23 -18
  102. package/skills/ae-dataops/references/dataops-flow-create.md +74 -23
  103. package/skills/ae-dataops/references/dataops-flow-monitor.md +45 -13
  104. package/skills/ae-dataops/references/dataops-integration.md +57 -30
  105. package/skills/ae-dataops/references/dataops-query.md +21 -4
  106. package/skills/ae-dataops/references/dataops-table.md +143 -11
  107. package/skills/ae-engage/SKILL.md +21 -6
  108. package/skills/ae-engage/references/channel-mgmt.md +3 -3
  109. package/skills/ae-engage/references/collaboration.md +48 -0
  110. package/skills/ae-engage/references/scene-config-channel.md +4 -4
  111. package/skills/ae-generate-tracking-plan/SKILL.md +4 -0
  112. package/skills/ae-generate-tracking-plan/references/collaboration.md +48 -0
  113. package/skills/ae-kb/SKILL.md +9 -1
  114. package/skills/ae-kb/references/collaboration.md +48 -0
  115. package/skills/ae-kb/references/schema-import.md +30 -0
  116. package/skills/ae-kb-discovery/SKILL.md +7 -2
  117. package/skills/ae-kb-discovery/references/collaboration.md +48 -0
  118. package/skills/ae-metadata/SKILL.md +20 -12
  119. package/skills/ae-metadata/references/collaboration.md +48 -0
@@ -7,12 +7,12 @@ import {
7
7
  waitForAnalysisRun,
8
8
  withAsyncArtifactLifecycle,
9
9
  withInterruptSignal
10
- } from "./chunk-WEPZ5M3Z.js";
10
+ } from "./chunk-PGCIKZRE.js";
11
11
  import {
12
12
  createCapabilityCommand,
13
13
  definitionIntentSnapshotFlag,
14
14
  modelDefinitionIntentSnapshotFlag
15
- } from "./chunk-L2QUIZTS.js";
15
+ } from "./chunk-647BRKZK.js";
16
16
  import {
17
17
  registerCapabilityGatewayRoute,
18
18
  resolveGatewayDomain
@@ -26,19 +26,20 @@ import {
26
26
  requestCapabilityGateway,
27
27
  requestCapabilityGatewayWithEnvelope,
28
28
  uploadInputFile,
29
- validateCapability
30
- } from "./chunk-HFGZGYCG.js";
29
+ validateCapability,
30
+ validateCapabilityWithEnvelope
31
+ } from "./chunk-QYXDJOLA.js";
31
32
  import {
32
33
  withOutputMetadata
33
- } from "./chunk-DX5CDO34.js";
34
+ } from "./chunk-BW7JUDEI.js";
34
35
  import "./chunk-Y74WTIKJ.js";
35
36
  import {
36
37
  getCliToken
37
- } from "./chunk-7XGFSUOM.js";
38
- import "./chunk-7FTTULED.js";
38
+ } from "./chunk-DU72X2IO.js";
39
+ import "./chunk-IG3DYEIR.js";
39
40
  import {
40
41
  CliValidationError
41
- } from "./chunk-MAPZ7VB6.js";
42
+ } from "./chunk-YVGXGTD6.js";
42
43
 
43
44
  // src/commands/te-analysis/capability-shared.ts
44
45
  var ANALYSIS_GATEWAY_CLI_SERVICES = /* @__PURE__ */ new Set(["project", "system"]);
@@ -148,7 +149,7 @@ var sqlTableUsageFlag = {
148
149
  name: "usage",
149
150
  type: "string",
150
151
  required: false,
151
- desc: "Authorized table set: analysis (default) or tag_cluster. Use tag_cluster before creating SQL tags or clusters."
152
+ desc: "Authorized table set: analysis (default), tag_cluster for SQL tags/clusters, or sql_datatable for SQL-built data tables. Use the same usage for list and columns."
152
153
  };
153
154
  var reportListLimitFlag = {
154
155
  name: "limit",
@@ -288,7 +289,7 @@ function certificationScopeInput(ctx) {
288
289
  }
289
290
  return value;
290
291
  }
291
- function dashboardReportDataInput(ctx) {
292
+ function dashboardReportDataInput(ctx, includeCache = true) {
292
293
  return compactInput({
293
294
  ...projectInput(ctx),
294
295
  dashboard_id: ctx.num("dashboard-id"),
@@ -299,7 +300,7 @@ function dashboardReportDataInput(ctx) {
299
300
  cluster_query_scope: optionalString(ctx, "cluster-query-scope"),
300
301
  slave_cluster_id: optionalString(ctx, "slave-cluster-id"),
301
302
  zone_offset: optionalNumber(ctx, "zone-offset"),
302
- use_cache: optionalBoolean(ctx, "use-cache"),
303
+ ...includeCache ? { use_cache: optionalBoolean(ctx, "use-cache") } : {},
303
304
  request_id: optionalString(ctx, "request-id"),
304
305
  preview_rows: optionalNumber(ctx, "preview-rows"),
305
306
  timeout_seconds: optionalNumber(ctx, "timeout-seconds"),
@@ -756,6 +757,7 @@ var metadataEventExport = createAnalysisMetaCapabilityCommand({
756
757
  queriesFlag,
757
758
  fieldsFlag,
758
759
  { name: "authenticated-only", type: "boolean", required: false, desc: "When true, export only authenticated events." },
760
+ certificationScopeFlag,
759
761
  metadataExportOutputFlag
760
762
  ],
761
763
  risk: "read",
@@ -764,7 +766,8 @@ var metadataEventExport = createAnalysisMetaCapabilityCommand({
764
766
  ...projectInput(ctx),
765
767
  queries: optionalQueries(ctx),
766
768
  fields: optionalJson(ctx, "fields"),
767
- authenticated_only: optionalBoolean(ctx, "authenticated-only")
769
+ authenticated_only: optionalBoolean(ctx, "authenticated-only"),
770
+ certification_scope: certificationScopeInput(ctx)
768
771
  }),
769
772
  postProcess: metadataExportPostProcess("event", "events")
770
773
  });
@@ -947,6 +950,7 @@ var metadataPropertyExport = createAnalysisMetaCapabilityCommand({
947
950
  queriesFlag,
948
951
  fieldsFlag,
949
952
  { name: "authenticated-only", type: "boolean", required: false, desc: "When true, export only authenticated properties." },
953
+ certificationScopeFlag,
950
954
  metadataExportOutputFlag
951
955
  ],
952
956
  risk: "read",
@@ -958,7 +962,8 @@ var metadataPropertyExport = createAnalysisMetaCapabilityCommand({
958
962
  event_name: optionalString(ctx, "event-name"),
959
963
  queries: optionalQueries(ctx),
960
964
  fields: optionalJson(ctx, "fields"),
961
- authenticated_only: optionalBoolean(ctx, "authenticated-only")
965
+ authenticated_only: optionalBoolean(ctx, "authenticated-only"),
966
+ certification_scope: certificationScopeInput(ctx)
962
967
  }),
963
968
  postProcess: metadataExportPostProcess("property", "properties")
964
969
  });
@@ -1350,6 +1355,7 @@ var metadataMetricExport = createAnalysisMetaCapabilityCommand({
1350
1355
  queriesFlag,
1351
1356
  fieldsFlag,
1352
1357
  { name: "authenticated-only", type: "boolean", required: false, desc: "When true, export only authenticated metrics." },
1358
+ certificationScopeFlag,
1353
1359
  metadataExportOutputFlag
1354
1360
  ],
1355
1361
  risk: "read",
@@ -1359,7 +1365,8 @@ var metadataMetricExport = createAnalysisMetaCapabilityCommand({
1359
1365
  ignore_authentication: optionalBoolean(ctx, "ignore-authentication"),
1360
1366
  queries: optionalQueries(ctx),
1361
1367
  fields: optionalJson(ctx, "fields"),
1362
- authenticated_only: optionalBoolean(ctx, "authenticated-only")
1368
+ authenticated_only: optionalBoolean(ctx, "authenticated-only"),
1369
+ certification_scope: certificationScopeInput(ctx)
1363
1370
  }),
1364
1371
  postProcess: metadataExportPostProcess("metric", "metrics")
1365
1372
  });
@@ -1500,13 +1507,13 @@ var ownTypesFlag = {
1500
1507
  var requiredQueriesFlag = {
1501
1508
  ...queriesFlag,
1502
1509
  required: true,
1503
- desc: "Required JSON array of 1 to 20 keyword filters. For first-pass asset discovery, use one broad search without --asset-types so results can include both dashboards and reports."
1510
+ desc: "Required JSON array of 1 to 20 keyword filters. For first-pass asset discovery, use one broad search without --asset-types and request a large page; when has_more is true and no strong candidate is present, continue with next_offset before narrowing or rewriting keywords."
1504
1511
  };
1505
1512
  var analysisAssetSearch = createAnalysisCapabilityCommand({
1506
1513
  resource: "asset",
1507
1514
  command: "search",
1508
1515
  capabilityId: "analysis.asset.search",
1509
- description: "Search visible saved analysis assets by keyword. Default first-pass search should omit --asset-types to cover both dashboards and reports.",
1516
+ description: "Search readable saved analysis assets by keyword, including shared read-only and editable dashboards and reports. Results are ranked before pagination by match quality, certification, recent heat/users, and governance impact. Omit --asset-types for first-pass discovery.",
1510
1517
  flags: [
1511
1518
  projectIdFlag,
1512
1519
  requiredQueriesFlag,
@@ -1635,7 +1642,16 @@ var statusFlag = { name: "status", type: "json", required: false, desc: "Operati
1635
1642
  var sortFieldFlag = { name: "sort-field", type: "string", required: false, desc: "Sort field." };
1636
1643
  var sortOrderFlag = { name: "sort-order", type: "string", required: false, desc: "Sort order: asc or desc." };
1637
1644
  var recordIdFlag = { name: "record-id", type: "number", required: false, desc: "Operation record ID." };
1645
+ var governanceExportFlags = (format) => [
1646
+ requestIdFlag,
1647
+ { ...artifactFormatFlag, desc: `Artifact format: ${format}. This capability supports only ${format}.` },
1648
+ { ...asyncTimeoutSecondsFlag, max: 7200, desc: "Async export runtime in seconds. Default: 3600, max: 7200." }
1649
+ ];
1650
+ var governanceExportFields = ["request_id", "format", "timeout_seconds"];
1638
1651
  var readers = {
1652
+ request_id: (ctx) => optionalString(ctx, "request-id"),
1653
+ format: (ctx) => optionalString(ctx, "artifact-format"),
1654
+ timeout_seconds: (ctx) => optionalNumber(ctx, "timeout-seconds"),
1639
1655
  node_id: (ctx) => optionalString(ctx, "node-id"),
1640
1656
  resource_id: (ctx) => optionalString(ctx, "resource-id"),
1641
1657
  resource_type: (ctx) => optionalString(ctx, "resource-type"),
@@ -2238,6 +2254,715 @@ var metadataGovernanceRecommendationSubmit = createAnalysisMetaCapabilityCommand
2238
2254
  })
2239
2255
  });
2240
2256
 
2257
+ // src/commands/te-analysis/meta/governance-recommendation/auto-review.ts
2258
+ import { randomUUID } from "crypto";
2259
+ var windowDaysFlag3 = {
2260
+ name: "window-days",
2261
+ type: "number",
2262
+ required: false,
2263
+ min: 1,
2264
+ max: 365,
2265
+ desc: "Usage window in days for the recommendation batch. Default: 90."
2266
+ };
2267
+ var limitFlag4 = {
2268
+ name: "limit",
2269
+ type: "number",
2270
+ required: false,
2271
+ min: 1,
2272
+ max: 100,
2273
+ desc: "Maximum hot dashboard evidence packages selected before automatic asset review. Omit for CLI bounded expansion over top-20/top-50/top-100; pass explicitly for a fixed diagnostic scope."
2274
+ };
2275
+ var GOVERNANCE_RECOMMENDATION_AUTO_REVIEW_HELP = [
2276
+ "Automatic asset certification contract:",
2277
+ "- Use this command only when the user explicitly asks to automatically review or automatically certify recommended assets.",
2278
+ "- Do not call this command for a plain recommendation request.",
2279
+ "- Do not call this command when the user asks to submit recommendations to the review page.",
2280
+ "- The command first validates project config agent_auto_asset_certification_enabled and project_semantic_enable. If disabled, report the returned config error and stop before collecting recommendations.",
2281
+ "- When --limit is omitted, the CLI performs the same bounded expansion used by recurring page-review jobs: inspect top-20 pending material, then top-50/top-100 only when pending assets or pending dashboard branches are insufficient.",
2282
+ "- Expansion is read-only. The CLI Agent builds automatic decisions once from the final selected material, then submits those decisions for execution and audit.",
2283
+ "- When --limit is explicitly provided, respect that scope and do not expand it.",
2284
+ "- Automatic review only certifies eligible asset candidates from the current recommendation batch. It does not create recommended metrics.",
2285
+ "- CLI Agent blocks semantic duplicates, copy-like assets, synonym properties and ambiguous close definitions; every duplicate skip must name the conflicting asset targets.",
2286
+ "- The result includes manual_review_handoff for assets still uncertified after automatic review. Use it only when the user later asks to submit those assets to the review page.",
2287
+ "- For that later page submission, build the review packet from manual_review_handoff.page_review_items and relations so skipped assets keep their dashboard/report parent context; manual_review_handoff.items remains the exact unapproved review workload.",
2288
+ "- Automatic decisions are recorded for audit. Page-visible reasons must be reviewer-readable Chinese; keep raw source/rule codes only in debug exports.",
2289
+ "- Returned SKIP decisions mean the asset remains for manual review; do not describe skipped items as certified."
2290
+ ].join("\n");
2291
+ var AUTO_REVIEW_CAPABILITY_ID = "metadata.governance_recommendation.auto_review";
2292
+ var EXPORT_CAPABILITY_ID = "metadata.governance_recommendation.export";
2293
+ var SUBMIT_CAPABILITY_ID = "metadata.governance_recommendation.submit";
2294
+ var DEFAULT_LIMIT = 20;
2295
+ var EXPANSION_LIMITS = [20, 50, 100];
2296
+ var MIN_PENDING_ASSETS = 10;
2297
+ var MIN_PENDING_WORK_UNITS = 3;
2298
+ var AUTO_REVIEW_DECISION_LIMIT = 100;
2299
+ var AUTO_REVIEW_MIN_HEAT = 50;
2300
+ var AUTO_REVIEW_MIN_USERS = 5;
2301
+ var AUTO_REVIEW_MIN_IMPACT = 2;
2302
+ var metadataGovernanceRecommendationAutoReview = {
2303
+ service: "analysis-meta",
2304
+ resource: "governance-recommendation",
2305
+ command: "auto-review",
2306
+ capabilityId: AUTO_REVIEW_CAPABILITY_ID,
2307
+ description: "Automatically review and certify eligible recommended assets when the project switch is enabled.",
2308
+ helpText: GOVERNANCE_RECOMMENDATION_AUTO_REVIEW_HELP,
2309
+ flags: [projectIdFlag, windowDaysFlag3, limitFlag4],
2310
+ risk: "write",
2311
+ preflight: (ctx) => {
2312
+ autoReviewInput(ctx);
2313
+ },
2314
+ validateInput: async (ctx) => validateCapability(
2315
+ ctx.host(),
2316
+ gatewayDomain(),
2317
+ AUTO_REVIEW_CAPABILITY_ID,
2318
+ autoReviewInput(ctx)
2319
+ ),
2320
+ dryRun: async (ctx) => dryRunCapability(
2321
+ ctx.host(),
2322
+ gatewayDomain(),
2323
+ AUTO_REVIEW_CAPABILITY_ID,
2324
+ autoReviewInput(ctx)
2325
+ ),
2326
+ execute: async (ctx) => {
2327
+ const input = autoReviewInput(ctx);
2328
+ if (hasExplicitLimit(ctx)) {
2329
+ await validateCapabilityWithEnvelope(ctx.host(), gatewayDomain(), AUTO_REVIEW_CAPABILITY_ID, input);
2330
+ const finalExport = await exportRecommendationMaterial(ctx, input, numberValue(input.limit) || DEFAULT_LIMIT);
2331
+ const result2 = await executeCliAgentAutoReview(ctx, input, finalExport.data);
2332
+ return withOutputMetadata({
2333
+ ...record(result2.data),
2334
+ manual_review_handoff: buildManualReviewHandoff(result2.data, finalExport.data)
2335
+ }, result2.meta);
2336
+ }
2337
+ const plan = await planBoundedExpansion(ctx, input);
2338
+ const expansion = plan.expansion;
2339
+ const finalInput = { ...input, limit: expansion.final_limit };
2340
+ const result = await executeCliAgentAutoReview(ctx, finalInput, plan.finalExport);
2341
+ if (result.data && typeof result.data === "object" && !Array.isArray(result.data)) {
2342
+ return withOutputMetadata({
2343
+ ...result.data,
2344
+ auto_review_expansion: expansion,
2345
+ manual_review_handoff: buildManualReviewHandoff(result.data, plan.finalExport)
2346
+ }, result.meta);
2347
+ }
2348
+ return withOutputMetadata(result.data, result.meta);
2349
+ }
2350
+ };
2351
+ function autoReviewInput(ctx) {
2352
+ return compactInput({
2353
+ ...projectInput(ctx),
2354
+ window_days: optionalNumber(ctx, "window-days"),
2355
+ limit: optionalNumber(ctx, "limit")
2356
+ });
2357
+ }
2358
+ function gatewayDomain() {
2359
+ return resolveGatewayDomain("analysis-meta", "analysis");
2360
+ }
2361
+ function hasExplicitLimit(ctx) {
2362
+ return typeof ctx.has === "function" ? ctx.has("limit") : ctx.str("limit") !== "";
2363
+ }
2364
+ async function executeCliAgentAutoReview(ctx, input, exportResult) {
2365
+ const review = buildCliAgentAutoReview(input, exportResult);
2366
+ if (review.decisions.length === 0) {
2367
+ return {
2368
+ data: {
2369
+ ...review.base,
2370
+ total: 0,
2371
+ applied: 0,
2372
+ recorded: 0,
2373
+ failed: 0,
2374
+ items: []
2375
+ },
2376
+ meta: {}
2377
+ };
2378
+ }
2379
+ const submitted = await executeCapabilityWithEnvelope(ctx.host(), gatewayDomain(), SUBMIT_CAPABILITY_ID, {
2380
+ ...input,
2381
+ run_id: review.runId,
2382
+ snapshot_hash: review.snapshotHash,
2383
+ topic_id: "agent_auto_asset_certification",
2384
+ topic_name: "Agent \u81EA\u52A8\u8BA4\u8BC1\u8D44\u4EA7",
2385
+ decisions: review.decisions
2386
+ });
2387
+ const submittedData = record(submitted.data);
2388
+ return {
2389
+ data: {
2390
+ ...review.base,
2391
+ total: submittedData.total,
2392
+ applied: submittedData.applied,
2393
+ recorded: submittedData.recorded,
2394
+ failed: submittedData.failed,
2395
+ items: autoReviewItemsWithDisplayReason(submittedData.items)
2396
+ },
2397
+ meta: submitted.meta
2398
+ };
2399
+ }
2400
+ async function planBoundedExpansion(ctx, input) {
2401
+ await validateCapabilityWithEnvelope(ctx.host(), gatewayDomain(), AUTO_REVIEW_CAPABILITY_ID, {
2402
+ ...input,
2403
+ limit: DEFAULT_LIMIT
2404
+ });
2405
+ const attempts = [];
2406
+ let finalExport = {};
2407
+ let finalLimit = DEFAULT_LIMIT;
2408
+ let stopReason = "top_100_inspected";
2409
+ for (const limit of EXPANSION_LIMITS) {
2410
+ const exported = await exportRecommendationMaterial(ctx, input, limit);
2411
+ finalExport = exported.data;
2412
+ const summary = summarizeExport(exported.data, limit);
2413
+ attempts.push(summary);
2414
+ finalLimit = limit;
2415
+ const pendingAssets = numberValue(summary.pending_asset_candidates);
2416
+ const pendingWorkUnits = numberValue(summary.pending_work_units);
2417
+ const hasMore = summary.has_more === true;
2418
+ if (pendingAssets >= MIN_PENDING_ASSETS && pendingWorkUnits >= MIN_PENDING_WORK_UNITS) {
2419
+ stopReason = "sufficient_pending_scope";
2420
+ break;
2421
+ }
2422
+ if (!hasMore) {
2423
+ stopReason = "dashboard_pool_exhausted";
2424
+ break;
2425
+ }
2426
+ }
2427
+ return {
2428
+ expansion: {
2429
+ strategy: "bounded_hot_dashboard_expansion",
2430
+ initial_limit: DEFAULT_LIMIT,
2431
+ final_limit: finalLimit,
2432
+ inspected_limits: attempts.map((attempt) => attempt.dashboard_limit),
2433
+ min_pending_asset_candidates: MIN_PENDING_ASSETS,
2434
+ min_pending_work_units: MIN_PENDING_WORK_UNITS,
2435
+ attempts,
2436
+ stop_reason: stopReason,
2437
+ write_policy: "single_auto_review_execute_after_read_only_expansion"
2438
+ },
2439
+ finalExport
2440
+ };
2441
+ }
2442
+ function exportRecommendationMaterial(ctx, input, limit) {
2443
+ return executeCapabilityWithEnvelope(
2444
+ ctx.host(),
2445
+ gatewayDomain(),
2446
+ EXPORT_CAPABILITY_ID,
2447
+ compactInput({ ...input, limit, include_completed: false })
2448
+ );
2449
+ }
2450
+ function summarizeExport(result, requestedLimit) {
2451
+ const data = record(result);
2452
+ const workUnits = array(data.work_units);
2453
+ const coverage = record(data.coverage);
2454
+ const pendingAssetCandidates = countPendingAssetCandidates(workUnits);
2455
+ return {
2456
+ dashboard_limit: numberValue(data.dashboard_limit) || requestedLimit,
2457
+ run_id: stringValue(data.run_id),
2458
+ snapshot_hash: stringValue(data.snapshot_hash),
2459
+ selected_dashboards: numberValue(coverage.selected_dashboards),
2460
+ returned_work_units: workUnits.length,
2461
+ pending_work_units: workUnits.filter((unit) => numberValue(record(unit).actionable_count) > 0).length,
2462
+ pending_asset_candidates: pendingAssetCandidates,
2463
+ has_more: coverage.has_more === true
2464
+ };
2465
+ }
2466
+ function countPendingAssetCandidates(workUnits) {
2467
+ const seen = /* @__PURE__ */ new Set();
2468
+ for (const unit of workUnits) {
2469
+ for (const candidate of array(record(unit).asset_candidates)) {
2470
+ const item = record(candidate);
2471
+ if (item.actionable !== true || item.action_state !== "PENDING") {
2472
+ continue;
2473
+ }
2474
+ const type = stringValue(item.resource_type);
2475
+ const key = stringValue(item.resource_key);
2476
+ if (type && key) {
2477
+ seen.add(`${type}|${key}`);
2478
+ }
2479
+ }
2480
+ }
2481
+ return seen.size;
2482
+ }
2483
+ function buildCliAgentAutoReview(input, exportResult) {
2484
+ const exported = record(exportResult);
2485
+ const material = record(exported.review_material_package);
2486
+ const candidates = array(material.candidate_assets).map(record);
2487
+ const runId = `rec_auto_${randomUUID().replace(/-/g, "")}`;
2488
+ const snapshotHash = stringValue(exported.snapshot_hash);
2489
+ const semanticConflicts = semanticDuplicateConflicts(candidates);
2490
+ const decisions = [];
2491
+ let approved = 0;
2492
+ let skipped = 0;
2493
+ let overflow = 0;
2494
+ const seen = /* @__PURE__ */ new Set();
2495
+ for (const candidate of candidates) {
2496
+ const target = record(candidate.target_ref);
2497
+ const type = stringValue(target.type);
2498
+ const key = stringValue(target.key);
2499
+ if (!type || !key || !seen.add(`${type}|${key}`)) continue;
2500
+ if (candidate.actionable !== true || candidate.action_state !== "PENDING") continue;
2501
+ if (decisions.length >= AUTO_REVIEW_DECISION_LIMIT) {
2502
+ overflow++;
2503
+ continue;
2504
+ }
2505
+ const verdict = cliAgentAutoReviewVerdict(candidate, semanticConflicts.get(`${type}|${key}`));
2506
+ if (verdict.decision === "APPROVE") approved++;
2507
+ else skipped++;
2508
+ decisions.push({
2509
+ item_type: "asset",
2510
+ decision: verdict.decision,
2511
+ resource_type: type,
2512
+ resource_key: key,
2513
+ evidence_hash: candidate.evidence_hash,
2514
+ reason: verdict.reason
2515
+ });
2516
+ }
2517
+ return {
2518
+ runId,
2519
+ snapshotHash,
2520
+ decisions,
2521
+ base: {
2522
+ schema_version: "1.0",
2523
+ run_id: runId,
2524
+ project_id: input.project_id ?? exported.project_id,
2525
+ window_days: input.window_days ?? exported.window_days,
2526
+ dashboard_limit: input.limit ?? exported.dashboard_limit,
2527
+ snapshot_hash: snapshotHash,
2528
+ rule_version: "agent_auto_asset_certification_v1",
2529
+ config_key: "agent_auto_asset_certification_enabled",
2530
+ semantic_config_key: "project_semantic_enable",
2531
+ auto_approved: approved,
2532
+ skipped,
2533
+ overflow
2534
+ }
2535
+ };
2536
+ }
2537
+ function cliAgentAutoReviewVerdict(candidate, semanticConflict) {
2538
+ if (semanticConflict) {
2539
+ return {
2540
+ decision: "SKIP",
2541
+ reason: autoReviewReason(`semantic_duplicate_targets=${encodeReasonPayload(semanticConflict)}`)
2542
+ };
2543
+ }
2544
+ if (listIncludes(candidate.conflict_risks, "same_evidence_rejected_before")) {
2545
+ return {
2546
+ decision: "SKIP",
2547
+ reason: autoReviewReason("same_evidence_rejected_before")
2548
+ };
2549
+ }
2550
+ if (array(candidate.source_evidence).length === 0 && candidate.recommendation_role !== "source_dashboard") {
2551
+ return {
2552
+ decision: "SKIP",
2553
+ reason: autoReviewReason("source_evidence_missing")
2554
+ };
2555
+ }
2556
+ const heat = numberValue(candidate.heat_count90d);
2557
+ const users = numberValue(candidate.user_count90d);
2558
+ const impact = numberValue(candidate.impact_degree);
2559
+ const matched = [];
2560
+ if (heat >= AUTO_REVIEW_MIN_HEAT) matched.push(`heat_count90d>=${AUTO_REVIEW_MIN_HEAT}`);
2561
+ if (users >= AUTO_REVIEW_MIN_USERS) matched.push(`user_count90d>=${AUTO_REVIEW_MIN_USERS}`);
2562
+ if (impact > AUTO_REVIEW_MIN_IMPACT) matched.push(`impact_degree>${AUTO_REVIEW_MIN_IMPACT}.0`);
2563
+ if (matched.length === 0) {
2564
+ return {
2565
+ decision: "SKIP",
2566
+ reason: autoReviewReason(`signals_below_threshold: heat_count90d=${heat}, user_count90d=${users}, impact_degree=${impact}`)
2567
+ };
2568
+ }
2569
+ return {
2570
+ decision: "APPROVE",
2571
+ reason: autoReviewReason(`matched=${matched.join(",")}; role=${stringValue(candidate.recommendation_role) ?? "asset"}`)
2572
+ };
2573
+ }
2574
+ function autoReviewReason(reason) {
2575
+ return `source=AGENT_AUTO_REVIEW; rule_version=agent_auto_asset_certification_v1; ${reason}`;
2576
+ }
2577
+ function autoReviewItemsWithDisplayReason(items) {
2578
+ return array(items).map((value) => {
2579
+ const item = record(value);
2580
+ const copy = {
2581
+ ...item,
2582
+ semantic_duplicate_targets: semanticDuplicateTargetsFromReason(stringValue(item.reason) ?? "")
2583
+ };
2584
+ if (item.decision !== "APPROVE") {
2585
+ copy.display_reason = autoReviewDisplayReason(item);
2586
+ }
2587
+ return copy;
2588
+ });
2589
+ }
2590
+ function semanticDuplicateConflicts(candidates) {
2591
+ const result = /* @__PURE__ */ new Map();
2592
+ const comparable = candidates.map((candidate) => comparableAsset(candidate)).filter((candidate) => !!candidate);
2593
+ for (const candidate of comparable) {
2594
+ if (candidate.actionable !== true || candidate.actionState !== "PENDING") continue;
2595
+ const matches = [];
2596
+ for (const other of comparable) {
2597
+ if (candidate.assetKey === other.assetKey) continue;
2598
+ const basis = semanticDuplicateBasis(candidate, other);
2599
+ if (basis) matches.push({ asset: other, basis });
2600
+ }
2601
+ if (!matches.length) continue;
2602
+ matches.sort((left, right) => duplicateRank(right.asset) - duplicateRank(left.asset));
2603
+ const targets = matches.slice(0, 3).map(({ asset, basis }) => ({
2604
+ target_ref: { type: asset.type, key: asset.key },
2605
+ display_name: asset.displayName,
2606
+ resource_type: asset.type,
2607
+ resource_key: asset.key,
2608
+ authentication_status: asset.authenticationStatus,
2609
+ action_state: asset.actionState,
2610
+ reason: duplicateBasisText(basis)
2611
+ }));
2612
+ result.set(candidate.assetKey, {
2613
+ targets,
2614
+ basis: Array.from(new Set(matches.map((match) => match.basis))).map(duplicateBasisText)
2615
+ });
2616
+ }
2617
+ return result;
2618
+ }
2619
+ function comparableAsset(candidate) {
2620
+ const target = record(candidate.target_ref);
2621
+ const type = stringValue(target.type);
2622
+ const key = stringValue(target.key);
2623
+ if (!type || !key) return void 0;
2624
+ const displayName = stringValue(candidate.display_name) ?? key;
2625
+ const evidence = record(candidate.evidence_snapshot);
2626
+ const evidenceAsset = record(evidence.asset);
2627
+ const description = stringValue(record(candidate.evidence_snapshot).description) ?? stringValue(evidenceAsset.description) ?? stringValue(candidate.description) ?? "";
2628
+ return {
2629
+ assetKey: `${type}|${key}`,
2630
+ type,
2631
+ key,
2632
+ displayName,
2633
+ description,
2634
+ authenticationStatus: numberValue(evidenceAsset.authentication_status ?? candidate.authentication_status),
2635
+ actionState: stringValue(candidate.action_state) ?? "",
2636
+ actionable: candidate.actionable === true,
2637
+ normalizedName: normalizeSemanticText(displayName || key),
2638
+ canonicalName: canonicalDuplicateText(displayName || key),
2639
+ aliasName: aliasDuplicateText(displayName || key),
2640
+ normalizedDescription: normalizeSemanticText(description),
2641
+ heat: numberValue(candidate.heat_count90d),
2642
+ users: numberValue(candidate.user_count90d),
2643
+ impact: numberValue(candidate.impact_degree)
2644
+ };
2645
+ }
2646
+ function semanticDuplicateBasis(left, right) {
2647
+ if (!left.normalizedName || !right.normalizedName) return void 0;
2648
+ if (left.normalizedName.length >= 3 && left.normalizedName === right.normalizedName) return "same_display_name";
2649
+ if (left.canonicalName.length >= 3 && left.canonicalName === right.canonicalName) return "copy_or_suffix_variant";
2650
+ if (left.aliasName.length >= 3 && left.aliasName === right.aliasName && relatedAssetTypes(left.type, right.type)) {
2651
+ return "business_alias_equivalent";
2652
+ }
2653
+ if (left.normalizedDescription.length >= 6 && left.normalizedDescription === right.normalizedDescription) {
2654
+ return "same_description";
2655
+ }
2656
+ return void 0;
2657
+ }
2658
+ function relatedAssetTypes(left, right) {
2659
+ if (left === right) return true;
2660
+ return left.endsWith("_prop") && right.endsWith("_prop");
2661
+ }
2662
+ function duplicateRank(asset) {
2663
+ return (asset.authenticationStatus === 1 ? 1e5 : 0) + (asset.actionState === "AUTHENTICATED" ? 5e4 : 0) + asset.heat + asset.users * 10 + asset.impact * 100;
2664
+ }
2665
+ function normalizeSemanticText(value) {
2666
+ return value.toLowerCase().replace(/[\s_\-—–/\\|::,,.。"'`【】\[\]()(){}<>《》]/g, "").trim();
2667
+ }
2668
+ function canonicalDuplicateText(value) {
2669
+ return normalizeSemanticText(value).replace(/(副本|复制|拷贝|备份|copy|backup|bak|test|测试|临时|tmp)+$/g, "").replace(/v\d+$/g, "").replace(/\d+$/g, "");
2670
+ }
2671
+ function aliasDuplicateText(value) {
2672
+ return canonicalDuplicateText(value).replace(/(公司|企业|客户|客戶|client|customer|company|account)/g, "\u5BA2\u6237").replace(/(项目|工程|project)/g, "\u9879\u76EE").replace(/(用户|玩家|会员|user|player|member)/g, "\u7528\u6237");
2673
+ }
2674
+ function duplicateBasisText(value) {
2675
+ switch (value) {
2676
+ case "same_display_name":
2677
+ return "\u8D44\u4EA7\u540D\u79F0\u4E00\u81F4";
2678
+ case "copy_or_suffix_variant":
2679
+ return "\u540D\u79F0\u4EC5\u5B58\u5728\u526F\u672C\u3001\u7248\u672C\u6216\u4E34\u65F6\u540E\u7F00\u5DEE\u5F02";
2680
+ case "business_alias_equivalent":
2681
+ return "\u4E1A\u52A1\u5BF9\u8C61\u8868\u8FF0\u76F8\u8FD1\uFF0C\u53EF\u80FD\u662F\u540C\u4E49\u5C5E\u6027\u6216\u76F8\u8FD1\u53E3\u5F84";
2682
+ case "same_description":
2683
+ return "\u8D44\u4EA7\u63CF\u8FF0\u4E00\u81F4";
2684
+ default:
2685
+ return value;
2686
+ }
2687
+ }
2688
+ function encodeReasonPayload(value) {
2689
+ return Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
2690
+ }
2691
+ function decodeReasonPayload(value) {
2692
+ try {
2693
+ return record(JSON.parse(Buffer.from(value, "base64url").toString("utf8")));
2694
+ } catch {
2695
+ return {};
2696
+ }
2697
+ }
2698
+ function semanticDuplicateTargetsFromReason(reason) {
2699
+ const encoded = /semantic_duplicate_targets=([A-Za-z0-9_-]+)/.exec(reason)?.[1];
2700
+ if (!encoded) return [];
2701
+ return array(decodeReasonPayload(encoded).targets);
2702
+ }
2703
+ function listIncludes(value, expected) {
2704
+ return array(value).some((item) => item === expected);
2705
+ }
2706
+ function buildManualReviewHandoff(autoReviewResult, exportResult) {
2707
+ const result = record(autoReviewResult);
2708
+ const exported = record(exportResult);
2709
+ const material = record(exported.review_material_package);
2710
+ const decisions = decisionsByAsset(result.items);
2711
+ const candidates = array(material.candidate_assets);
2712
+ const relations = array(material.relations).map(record);
2713
+ const handoffItems = [];
2714
+ const approved = /* @__PURE__ */ new Set();
2715
+ for (const decision of decisions.values()) {
2716
+ if (decision.decision === "APPROVE" && decision.landing_status === "APPLIED") {
2717
+ approved.add(decision.assetKey);
2718
+ }
2719
+ }
2720
+ const autoCertifiedItems = buildAutoCertifiedItems(decisions, candidates);
2721
+ for (const value of candidates) {
2722
+ const candidate = record(value);
2723
+ const target = record(candidate.target_ref);
2724
+ const type = stringValue(target.type);
2725
+ const key = stringValue(target.key);
2726
+ if (!type || !key) {
2727
+ continue;
2728
+ }
2729
+ const assetKey = `${type}|${key}`;
2730
+ if (approved.has(assetKey)) {
2731
+ continue;
2732
+ }
2733
+ const decision = decisions.get(assetKey);
2734
+ const actionable = candidate.actionable === true;
2735
+ if (!decision && !actionable) {
2736
+ continue;
2737
+ }
2738
+ const reviewReason = decision ? manualReviewReason(decision, candidate) : "\u672C\u6B21\u81EA\u52A8\u8BA4\u8BC1\u672A\u4EA7\u751F\u660E\u786E\u7ED3\u8BBA\uFF0C\u53EF\u80FD\u662F\u5019\u9009\u8D44\u4EA7\u8D85\u51FA\u672C\u6B21\u81EA\u52A8\u8BA4\u8BC1\u5904\u7406\u4E0A\u9650\u6216\u88AB\u53BB\u91CD\uFF0C\u8BF7\u4EBA\u5DE5\u786E\u8BA4\u3002";
2739
+ handoffItems.push({
2740
+ ...candidate,
2741
+ auto_review_decision: decision ? decision.raw : {
2742
+ decision: "NOT_REVIEWED",
2743
+ reason: reviewReason
2744
+ },
2745
+ semantic_duplicate_targets: decision ? array(decision.raw.semantic_duplicate_targets) : [],
2746
+ manual_review_reason: reviewReason
2747
+ });
2748
+ }
2749
+ const pageReviewItems = buildPageReviewItems(handoffItems, candidates, relations, decisions);
2750
+ const pageReviewIds = new Set(pageReviewItems.map((item) => stringValue(item.client_item_id)).filter(Boolean));
2751
+ return {
2752
+ schema_version: "1.0",
2753
+ handoff_type: "auto_review_uncertified_assets_to_page_review",
2754
+ project_id: result.project_id ?? exported.project_id,
2755
+ source_auto_review_run_id: result.run_id,
2756
+ source_export_run_id: exported.run_id,
2757
+ snapshot_hash: exported.snapshot_hash ?? result.snapshot_hash,
2758
+ dashboard_limit: exported.dashboard_limit ?? result.dashboard_limit,
2759
+ auto_review_trace: {
2760
+ run_id: result.run_id,
2761
+ rule_version: result.rule_version,
2762
+ auto_approved: result.auto_approved,
2763
+ skipped: result.skipped,
2764
+ overflow: result.overflow,
2765
+ approved_items: autoCertifiedItems,
2766
+ skipped_items: handoffItems.map((item) => ({
2767
+ target_ref: record(item.target_ref),
2768
+ display_name: item.display_name,
2769
+ manual_review_reason: item.manual_review_reason
2770
+ }))
2771
+ },
2772
+ auto_certified_items: autoCertifiedItems,
2773
+ candidate_count: handoffItems.length,
2774
+ items: handoffItems,
2775
+ page_review_items: pageReviewItems,
2776
+ page_review_context_count: pageReviewItems.length - handoffItems.length,
2777
+ relations: relations.filter((edge) => pageReviewIds.has(stringValue(edge.parent)) && pageReviewIds.has(stringValue(edge.child))),
2778
+ source_index: array(material.source_index).map(record).filter((source) => pageReviewIds.has(stringValue(source.client_item_id))),
2779
+ quality_gates: array(material.quality_gates),
2780
+ definition_policy: record(material.definition_policy),
2781
+ submit_to_page_policy: {
2782
+ command: "analysis-meta agent-review submit-to-page",
2783
+ scope: "Submit page_review_items so unapproved assets keep dashboard/report parent context; only items without manual_review_context require human certification decisions.",
2784
+ reason_field: "manual_review_reason",
2785
+ source_metadata: "When submitting leftovers, copy manual_review_handoff.auto_review_trace to submit-to-page source_metadata.auto_review_trace so the page audit records show the assets Agent already certified.",
2786
+ requires_user_submission_intent: true
2787
+ }
2788
+ };
2789
+ }
2790
+ function buildPageReviewItems(handoffItems, candidates, relations, decisions) {
2791
+ const byId = /* @__PURE__ */ new Map();
2792
+ const requiredIds = /* @__PURE__ */ new Set();
2793
+ for (const value of candidates) {
2794
+ const candidate = record(value);
2795
+ const id = stringValue(candidate.client_item_id);
2796
+ if (id) byId.set(id, candidate);
2797
+ }
2798
+ for (const item of handoffItems) {
2799
+ const id = stringValue(item.client_item_id);
2800
+ if (id) requiredIds.add(id);
2801
+ }
2802
+ let changed = true;
2803
+ while (changed) {
2804
+ changed = false;
2805
+ for (const edge of relations) {
2806
+ const parent = stringValue(edge.parent);
2807
+ const child = stringValue(edge.child);
2808
+ if (parent && child && requiredIds.has(child) && !requiredIds.has(parent) && byId.has(parent)) {
2809
+ requiredIds.add(parent);
2810
+ changed = true;
2811
+ }
2812
+ }
2813
+ }
2814
+ const handoffById = new Map(handoffItems.map((item) => [stringValue(item.client_item_id), item]).filter((entry) => !!entry[0]));
2815
+ return candidates.map(record).filter((candidate) => {
2816
+ const id = stringValue(candidate.client_item_id);
2817
+ return id !== void 0 && requiredIds.has(id);
2818
+ }).map((candidate) => {
2819
+ const id = stringValue(candidate.client_item_id) ?? "";
2820
+ const handoff = handoffById.get(id);
2821
+ if (handoff) return handoff;
2822
+ const target = record(candidate.target_ref);
2823
+ const type = stringValue(target.type);
2824
+ const key = stringValue(target.key);
2825
+ const decision = type && key ? decisions.get(`${type}|${key}`) : void 0;
2826
+ return markContextCandidate(candidate, decision);
2827
+ });
2828
+ }
2829
+ function markContextCandidate(candidate, decision) {
2830
+ const item = { ...candidate };
2831
+ item.manual_review_context = true;
2832
+ item.manual_review_reason = "\u8BE5\u8D44\u4EA7\u7528\u4E8E\u4FDD\u7559\u81EA\u52A8\u53D1\u73B0\u9875\u9762\u7684\u7236\u5B50\u5C42\u7EA7\u548C\u6765\u6E90\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u4F5C\u4E3A\u672C\u6B21\u4EBA\u5DE5\u5BA1\u6838\u5BF9\u8C61\u3002";
2833
+ if (decision?.decision === "APPROVE" && decision.landing_status === "APPLIED") {
2834
+ item.auto_review_decision = decision.raw;
2835
+ item.auto_review_context_reason = autoReviewDisplayReason(decision.raw, candidate);
2836
+ item.evidence_snapshot = markEvidenceAuthenticated(record(candidate.evidence_snapshot));
2837
+ }
2838
+ return item;
2839
+ }
2840
+ function markEvidenceAuthenticated(evidence) {
2841
+ const snapshot = { ...evidence };
2842
+ const asset = record(snapshot.asset);
2843
+ if (Object.keys(asset).length) {
2844
+ snapshot.asset = { ...asset, authentication_status: 1 };
2845
+ }
2846
+ return snapshot;
2847
+ }
2848
+ function decisionsByAsset(value) {
2849
+ const result = /* @__PURE__ */ new Map();
2850
+ for (const itemValue of array(value)) {
2851
+ const item = record(itemValue);
2852
+ if (item.item_type !== "asset") {
2853
+ continue;
2854
+ }
2855
+ const type = stringValue(item.resource_type);
2856
+ const key = stringValue(item.resource_key);
2857
+ if (!type || !key) {
2858
+ continue;
2859
+ }
2860
+ const assetKey = `${type}|${key}`;
2861
+ result.set(assetKey, {
2862
+ assetKey,
2863
+ decision: stringValue(item.decision) ?? "",
2864
+ landing_status: stringValue(item.landing_status) ?? "",
2865
+ raw: item
2866
+ });
2867
+ }
2868
+ return result;
2869
+ }
2870
+ function buildAutoCertifiedItems(decisions, candidates) {
2871
+ const byAsset = /* @__PURE__ */ new Map();
2872
+ for (const value of candidates) {
2873
+ const candidate = record(value);
2874
+ const target = record(candidate.target_ref);
2875
+ const type = stringValue(target.type);
2876
+ const key = stringValue(target.key);
2877
+ if (type && key) byAsset.set(`${type}|${key}`, candidate);
2878
+ }
2879
+ const items = [];
2880
+ for (const decision of decisions.values()) {
2881
+ if (decision.decision !== "APPROVE" || decision.landing_status !== "APPLIED") continue;
2882
+ const candidate = byAsset.get(decision.assetKey) ?? {};
2883
+ items.push({
2884
+ target_ref: record(candidate.target_ref),
2885
+ display_name: candidate.display_name ?? decision.raw.resource_key,
2886
+ auto_review_reason: autoReviewDisplayReason(decision.raw, candidate),
2887
+ landing_message: decision.raw.landing_message
2888
+ });
2889
+ }
2890
+ return items;
2891
+ }
2892
+ function manualReviewReason(decision, context) {
2893
+ const reason = autoReviewDisplayReason(decision.raw, context);
2894
+ if (decision.decision === "SKIP") {
2895
+ return reason;
2896
+ }
2897
+ if (decision.decision === "APPROVE" && decision.landing_status !== "APPLIED") {
2898
+ const landing = stringValue(decision.raw.landing_message);
2899
+ return landing ? `${reason}; landing_${decision.landing_status.toLowerCase()}: ${landing}` : `${reason}; landing_${decision.landing_status.toLowerCase()}`;
2900
+ }
2901
+ return reason;
2902
+ }
2903
+ function autoReviewDisplayReason(raw, context = {}) {
2904
+ const display = stringValue(raw.display_reason);
2905
+ if (display) return display;
2906
+ const reason = stringValue(raw.reason);
2907
+ if (!reason) return "\u672C\u6B21\u81EA\u52A8\u8BA4\u8BC1\u6CA1\u6709\u8FD4\u56DE\u539F\u56E0\uFF0C\u8BF7\u4EBA\u5DE5\u786E\u8BA4\u3002";
2908
+ return humanizeAutoReviewReason(reason, context);
2909
+ }
2910
+ function humanizeAutoReviewReason(reason, context) {
2911
+ if (reason.includes("semantic_duplicate_targets=")) {
2912
+ const targets = semanticDuplicateTargetsFromReason(reason).map(record).map((target) => {
2913
+ const ref = record(target.target_ref);
2914
+ const label = stringValue(target.display_name) ?? stringValue(target.resource_key) ?? stringValue(ref.key) ?? "\u672A\u77E5\u8D44\u4EA7";
2915
+ const type = stringValue(target.resource_type) ?? stringValue(ref.type) ?? "asset";
2916
+ const key = stringValue(target.resource_key) ?? stringValue(ref.key) ?? "-";
2917
+ const targetReason = stringValue(target.reason);
2918
+ return `${label}\uFF08${type}:${key}${targetReason ? `\uFF0C${targetReason}` : ""}\uFF09`;
2919
+ });
2920
+ const targetText = targets.length ? targets.join("\uFF1B") : "\u76F8\u8FD1\u8D44\u4EA7";
2921
+ return `\u672A\u81EA\u52A8\u8BA4\u8BC1\u539F\u56E0\uFF1A\u7591\u4F3C\u4E0E ${targetText} \u8BED\u4E49\u91CD\u590D\u6216\u53E3\u5F84\u76F8\u8FD1\u3002\u8BF7\u4EBA\u5DE5\u786E\u8BA4\u662F\u5426\u5E94\u4F5C\u4E3A\u72EC\u7ACB\u8D44\u4EA7\u8BA4\u8BC1\uFF1B\u5982\u679C\u53EA\u662F\u526F\u672C\u3001\u540C\u4E49\u5C5E\u6027\u6216\u76F8\u8FD1\u53E3\u5F84\uFF0C\u8BF7\u4F18\u5148\u9009\u62E9\u66F4\u6807\u51C6\u7684\u8D44\u4EA7\u3002`;
2922
+ }
2923
+ if (reason.includes("previous_decision_exists_for_different_evidence")) {
2924
+ return "\u672A\u81EA\u52A8\u8BA4\u8BC1\u539F\u56E0\uFF1A\u68C0\u6D4B\u5230\u8BE5\u8D44\u4EA7\u5B58\u5728\u5386\u53F2\u5904\u7406\u8BB0\u5F55\uFF0C\u4F46\u672C\u6B21\u672A\u8FD4\u56DE\u53EF\u6838\u9A8C\u7684\u91CD\u590D\u5BF9\u8C61\u3002\u8BF7\u6309\u5F53\u524D\u8D44\u4EA7\u5B9A\u4E49\u548C\u6765\u6E90\u8BC1\u636E\u4EBA\u5DE5\u786E\u8BA4\uFF1B\u8FD9\u4E0D\u4EE3\u8868\u7CFB\u7EDF\u53D1\u73B0\u4E86\u91CD\u590D\u8D44\u4EA7\u3002";
2925
+ }
2926
+ if (reason.includes("same_evidence_rejected_before")) {
2927
+ return "\u672A\u81EA\u52A8\u8BA4\u8BC1\u539F\u56E0\uFF1A\u76F8\u540C\u8BA4\u8BC1\u5EFA\u8BAE\u6B64\u524D\u5DF2\u88AB\u4EBA\u5DE5\u9A73\u56DE\uFF0C\u81EA\u52A8\u8BA4\u8BC1\u4E0D\u4F1A\u8986\u76D6\u4EBA\u5DE5\u7ED3\u8BBA\u3002\u5982\u4E1A\u52A1\u53E3\u5F84\u5DF2\u8C03\u6574\uFF0C\u8BF7\u4EBA\u5DE5\u91CD\u65B0\u786E\u8BA4\u662F\u5426\u8BA4\u8BC1\u3002";
2928
+ }
2929
+ if (reason.includes("source_evidence_missing")) {
2930
+ return "\u81EA\u52A8\u8BA4\u8BC1\u672A\u901A\u8FC7\uFF1A\u7F3A\u5C11\u53EF\u6838\u9A8C\u7684\u6765\u6E90\u8D44\u4EA7\u8BC1\u636E\uFF0C\u8BF7\u4EBA\u5DE5\u786E\u8BA4\u8D44\u4EA7\u6765\u6E90\u3001\u5B9A\u4E49\u548C\u4F9D\u8D56\u5173\u7CFB\u540E\u518D\u8BA4\u8BC1\u3002";
2931
+ }
2932
+ if (reason.includes("signals_below_threshold")) {
2933
+ const heat = /heat_count90d=(\d+)/.exec(reason)?.[1];
2934
+ const users = /user_count90d=(\d+)/.exec(reason)?.[1];
2935
+ const impact = /impact_degree=([0-9.]+)/.exec(reason)?.[1];
2936
+ return `\u81EA\u52A8\u8BA4\u8BC1\u672A\u901A\u8FC7\uFF1A\u8BE5\u8D44\u4EA7\u8FD1 90 \u5929\u8BBF\u95EE/\u4F7F\u7528 ${heat ?? "\u672A\u77E5"} \u6B21\u3001\u4F7F\u7528\u4EBA\u6570 ${users ?? "\u672A\u77E5"}\uFF0C\u5F71\u54CD\u5EA6 ${impact ?? "\u672A\u77E5"}\uFF0C\u672A\u8FBE\u5230\u81EA\u52A8\u8BA4\u8BC1\u9608\u503C\uFF0C\u8BF7\u4EBA\u5DE5\u786E\u8BA4\u662F\u5426\u4ECD\u9700\u8981\u8BA4\u8BC1\u3002`;
2937
+ }
2938
+ if (reason.includes("matched=")) {
2939
+ const matched = [];
2940
+ if (reason.includes("heat_count90d>=")) matched.push("\u8BBF\u95EE/\u4F7F\u7528\u6B21\u6570\u8FBE\u5230\u9608\u503C");
2941
+ if (reason.includes("user_count90d>=")) matched.push("\u4F7F\u7528\u4EBA\u6570\u8FBE\u5230\u9608\u503C");
2942
+ if (reason.includes("impact_degree>")) matched.push("\u5F71\u54CD\u5EA6\u8FBE\u5230\u9608\u503C");
2943
+ const matchedText = matched.length ? matched.join("\u3001") : "\u547D\u4E2D\u81EA\u52A8\u8BA4\u8BC1\u89C4\u5219";
2944
+ return `Agent \u81EA\u52A8\u8BA4\u8BC1\u901A\u8FC7\uFF1A\u8BE5\u8D44\u4EA7\u8FD1 90 \u5929\u8BBF\u95EE/\u4F7F\u7528 ${displaySignal(context.heat_count90d)} \u6B21\u3001\u4F7F\u7528\u4EBA\u6570 ${displaySignal(context.user_count90d)}\uFF0C\u5F71\u54CD\u5EA6 ${displaySignal(context.impact_degree)}\uFF1B\u672A\u53D1\u73B0\u8BED\u4E49\u91CD\u590D\u98CE\u9669\uFF0C\u4E14${matchedText}\u3002`;
2945
+ }
2946
+ return reason.replace(/^source=AGENT_AUTO_REVIEW;\s*/, "").replace(/rule_version=[^;]+;\s*/, "").replace(/conflict_risks=\[[^\]]*]/, "\u81EA\u52A8\u8BA4\u8BC1\u672A\u901A\u8FC7\uFF1A\u5B58\u5728\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u7684\u5BA1\u6838\u98CE\u9669\u3002").replace(/conflict_risks=/, "\u81EA\u52A8\u8BA4\u8BC1\u672A\u901A\u8FC7\uFF1A\u5B58\u5728\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u7684\u5BA1\u6838\u98CE\u9669\u3002");
2947
+ }
2948
+ function displaySignal(value) {
2949
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
2950
+ if (typeof value === "string" && value.trim()) return value;
2951
+ return "\u672A\u77E5";
2952
+ }
2953
+ function record(value) {
2954
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2955
+ }
2956
+ function array(value) {
2957
+ return Array.isArray(value) ? value : [];
2958
+ }
2959
+ function numberValue(value) {
2960
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
2961
+ }
2962
+ function stringValue(value) {
2963
+ return typeof value === "string" && value.trim() ? value : void 0;
2964
+ }
2965
+
2241
2966
  // src/commands/te-analysis/meta/governance-recommendation/decisions.ts
2242
2967
  var runIdFlag2 = {
2243
2968
  name: "run-id",
@@ -2245,7 +2970,7 @@ var runIdFlag2 = {
2245
2970
  required: false,
2246
2971
  desc: "Optional run_id filter."
2247
2972
  };
2248
- var limitFlag4 = {
2973
+ var limitFlag5 = {
2249
2974
  name: "limit",
2250
2975
  type: "number",
2251
2976
  required: false,
@@ -2258,7 +2983,7 @@ var metadataGovernanceRecommendationDecisions = createAnalysisMetaCapabilityComm
2258
2983
  command: "decisions",
2259
2984
  capabilityId: "metadata.governance_recommendation.decisions",
2260
2985
  description: "List recent CLI Agent recommendation approval decisions.",
2261
- flags: [projectIdFlag, runIdFlag2, limitFlag4],
2986
+ flags: [projectIdFlag, runIdFlag2, limitFlag5],
2262
2987
  risk: "read",
2263
2988
  buildInput: (ctx) => compactInput({
2264
2989
  ...projectInput(ctx),
@@ -2271,6 +2996,7 @@ var metadataGovernanceRecommendationDecisions = createAnalysisMetaCapabilityComm
2271
2996
  var commands11 = [
2272
2997
  metadataGovernanceRecommendationExport,
2273
2998
  metadataGovernanceRecommendationSubmit,
2999
+ metadataGovernanceRecommendationAutoReview,
2274
3000
  metadataGovernanceRecommendationDecisions
2275
3001
  ];
2276
3002
  var governance_recommendation_default = commands11;
@@ -2322,7 +3048,7 @@ function object(value, name) {
2322
3048
  }
2323
3049
  return value;
2324
3050
  }
2325
- function array(value, name) {
3051
+ function array2(value, name) {
2326
3052
  if (!Array.isArray(value) || value.length < 1 || value.length > 5e3) {
2327
3053
  throw new Error(`${name} must contain 1 to 5000 items.`);
2328
3054
  }
@@ -2367,6 +3093,7 @@ var SUBMIT_TO_PAGE_HELP = [
2367
3093
  "- relations should let the page render dashboard -> report -> metadata with contains/uses edges. Use an empty relations array only when the package truly has no known parent-child evidence.",
2368
3094
  "- Each submitted item must keep source_link or evidence_links[].url so reviewers can jump to source evidence.",
2369
3095
  "- item.ai_summary.summary is the per-item recommendation reason. It must cite concrete item evidence, not generic review boilerplate. batch ai_summary.summary is only the batch overview.",
3096
+ "- When submitting manual_review_handoff leftovers, copy manual_review_handoff.auto_review_trace into source_metadata.auto_review_trace so page audit records include the assets already certified automatically.",
2370
3097
  "- For reports and SQL reports, item.ai_summary.analysis_explanation must explain reviewer-readable calculations or measures from packaged evidence_snapshot.analysis. A purpose-only summary is incomplete and is rejected locally before dispatch.",
2371
3098
  "- Reviewer-visible text should follow the project-KB source style: one business Chinese Agent summary that explains purpose, calculation caliber, filters, time scope, boundaries, and concrete reviewer questions. Keep raw SQL, parser labels, temporary aliases, parameter placeholders, hashes, and evidence paths in evidence_snapshot only; independent Agent preflight flags visible implementation terms as quality findings.",
2372
3099
  "- The command output includes review_page_url when review_url is present. Return review_page_url to the user; do not prepend the Common/API host to review_url yourself.",
@@ -2378,6 +3105,7 @@ var SUBMIT_TO_PAGE_HELP = [
2378
3105
  ' "title": "<review batch title>",',
2379
3106
  ' "source_run_id": "<export.run_id>",',
2380
3107
  ' "source_task_id": "<optional preauthorized task id>",',
3108
+ ' "source_metadata": { "auto_review_trace": "<optional trace from manual_review_handoff.auto_review_trace>" },',
2381
3109
  ' "ai_summary": { "summary": "<batch overview>" },',
2382
3110
  ' "presentation_snapshot": {',
2383
3111
  ' "project": { "id": 1, "name": "<project name>" },',
@@ -2465,7 +3193,7 @@ var agentReviewSubmitToPage = createAnalysisMetaCapabilityCommand({
2465
3193
  postProcess: (result, _input, ctx) => normalizeSubmitResult(result, resolvePublicWebHost(ctx.host()), ctx.host())
2466
3194
  });
2467
3195
  function validateInput(input) {
2468
- const items = array(input.items, "items").map((value) => {
3196
+ const items = array2(input.items, "items").map((value) => {
2469
3197
  const item = object(value, "items[]");
2470
3198
  fields(item, [
2471
3199
  "client_item_id",
@@ -2512,6 +3240,7 @@ function validateInput(input) {
2512
3240
  "title",
2513
3241
  "source_run_id",
2514
3242
  "source_task_id",
3243
+ "source_metadata",
2515
3244
  "ai_summary",
2516
3245
  "presentation_snapshot",
2517
3246
  "items"
@@ -2581,7 +3310,7 @@ function validateEvidenceLinks(item) {
2581
3310
  const sourceLink = typeof item.source_link === "string" && item.source_link.trim();
2582
3311
  let evidenceLink = false;
2583
3312
  if ("evidence_links" in item) {
2584
- evidenceLink = array(item.evidence_links, "items[].evidence_links").some((value) => {
3313
+ evidenceLink = array2(item.evidence_links, "items[].evidence_links").some((value) => {
2585
3314
  const link = object(value, "items[].evidence_links[]");
2586
3315
  return typeof link.url === "string" && !!link.url.trim();
2587
3316
  });
@@ -2592,17 +3321,17 @@ function validateEvidenceLinks(item) {
2592
3321
  }
2593
3322
  function validatePresentation(presentation, itemIds) {
2594
3323
  const displayed = /* @__PURE__ */ new Set();
2595
- for (const topicValue of array(presentation.topics, "presentation_snapshot.topics")) {
3324
+ for (const topicValue of array2(presentation.topics, "presentation_snapshot.topics")) {
2596
3325
  const topic = object(topicValue, "presentation_snapshot.topics[]");
2597
3326
  if ("items" in topic) {
2598
3327
  throw new Error("presentation_snapshot.topics[] must use groups[].items, not top-level items.");
2599
3328
  }
2600
- for (const groupValue of array(topic.groups, "presentation_snapshot.topics[].groups")) {
3329
+ for (const groupValue of array2(topic.groups, "presentation_snapshot.topics[].groups")) {
2601
3330
  const group = object(groupValue, "presentation_snapshot.topics[].groups[]");
2602
3331
  if (typeof group.id !== "string" || !group.id.trim() || typeof group.name !== "string" || !group.name.trim()) {
2603
3332
  throw new Error("presentation_snapshot.topics[].groups[] requires nonempty string id and name.");
2604
3333
  }
2605
- for (const itemValue of array(group.items, "presentation_snapshot.topics[].groups[].items")) {
3334
+ for (const itemValue of array2(group.items, "presentation_snapshot.topics[].groups[].items")) {
2606
3335
  const item = object(itemValue, "presentation_snapshot.topics[].groups[].items[]");
2607
3336
  if (typeof item.id !== "string" || !item.id.trim()) {
2608
3337
  throw new Error("presentation_snapshot display items require nonempty string id.");
@@ -2649,7 +3378,7 @@ var agentReviewReview = createAnalysisMetaCapabilityCommand({
2649
3378
  { name: "decisions", type: "json", required: true, desc: "Versioned decisions: item_id (string), version (integer), decision APPROVE/DEFER/REJECT, reason (required for DEFER/REJECT)." }
2650
3379
  ],
2651
3380
  buildInput: (ctx) => {
2652
- const decisions = array(ctx.json("decisions"), "--decisions").map((value) => {
3381
+ const decisions = array2(ctx.json("decisions"), "--decisions").map((value) => {
2653
3382
  const decision = object(value, "decisions[]");
2654
3383
  fields(decision, ["item_id", "version", "decision", "reason"], "decisions[]");
2655
3384
  decimalId(decision.item_id, "decisions[].item_id");
@@ -2686,7 +3415,7 @@ var agentReviewRetry = createAnalysisMetaCapabilityCommand({
2686
3415
  { name: "item-ids", type: "json", required: true, desc: "1 to 5000 failed item IDs as decimal strings. Successful items must not be retried." }
2687
3416
  ],
2688
3417
  buildInput: (ctx) => {
2689
- const ids = array(ctx.json("item-ids"), "--item-ids").map((id) => decimalId(id, "item_ids[]"));
3418
+ const ids = array2(ctx.json("item-ids"), "--item-ids").map((id) => decimalId(id, "item_ids[]"));
2690
3419
  unique(ids, "item_ids");
2691
3420
  return { ...batchInput(ctx), client_request_id: text(ctx, "client-request-id"), item_ids: ids };
2692
3421
  }
@@ -2770,9 +3499,9 @@ var analysisMetaAssetUsageList = createAnalysisGovernanceCapabilityCommand({
2770
3499
  command: "list",
2771
3500
  capabilityId: "governance.asset.list",
2772
3501
  description: "List assets for usage governance.",
2773
- flags: [projectIdFlag, queryFlag, searchsFlag, ruleFlag, operationTypeFlag, directoryLimitFlag, directoryOffsetFlag, payloadFlag],
3502
+ flags: [projectIdFlag, nodeIdFlag, queryFlag, searchsFlag, ruleFlag, operationTypeFlag, directoryLimitFlag, directoryOffsetFlag, payloadFlag],
2774
3503
  risk: "read",
2775
- buildInput: (ctx) => assetGovernanceInput(ctx, ["query", "searchs", "rule", "operation_type", "limit", "offset"])
3504
+ buildInput: (ctx) => assetGovernanceInput(ctx, ["node_id", "query", "searchs", "rule", "operation_type", "limit", "offset"])
2776
3505
  });
2777
3506
 
2778
3507
  // src/commands/te-analysis/meta/asset/usage-export.ts
@@ -2780,10 +3509,11 @@ var analysisMetaAssetUsageExport = createAnalysisGovernanceCapabilityCommand({
2780
3509
  resource: "asset",
2781
3510
  command: "export",
2782
3511
  capabilityId: "governance.asset.export",
2783
- description: "Export asset usage governance rows as an inline gateway result.",
2784
- flags: [projectIdFlag, queryFlag, searchsFlag, ruleFlag, operationTypeFlag, limitFlag, offsetFlag, payloadFlag],
3512
+ description: "Export asset usage governance rows as an asynchronous artifact; optionally wait and download.",
3513
+ flags: [projectIdFlag, nodeIdFlag, queryFlag, searchsFlag, ruleFlag, operationTypeFlag, limitFlag, offsetFlag, payloadFlag, ...governanceExportFlags("jsonl")],
2785
3514
  risk: "read",
2786
- buildInput: (ctx) => assetGovernanceInput(ctx, ["query", "searchs", "rule", "operation_type", "limit", "offset"])
3515
+ asyncArtifact: true,
3516
+ buildInput: (ctx) => assetGovernanceInput(ctx, [...governanceExportFields, "node_id", "query", "searchs", "rule", "operation_type", "limit", "offset"])
2787
3517
  });
2788
3518
 
2789
3519
  // src/commands/te-analysis/meta/asset/lineage-get.ts
@@ -2891,9 +3621,10 @@ var analysisMetaAssetBatchInfoExport = createAnalysisGovernanceCapabilityCommand
2891
3621
  command: "batch-info-export",
2892
3622
  capabilityId: "governance.asset.batch_export_info",
2893
3623
  description: "Batch export asset information.",
2894
- flags: [projectIdFlag, nodeIdsFlag, payloadFlag],
3624
+ flags: [projectIdFlag, nodeIdsFlag, payloadFlag, ...governanceExportFlags("xlsx")],
2895
3625
  risk: "read",
2896
- buildInput: (ctx) => assetGovernanceInput(ctx, ["node_ids"])
3626
+ asyncArtifact: true,
3627
+ buildInput: (ctx) => assetGovernanceInput(ctx, [...governanceExportFields, "node_ids"])
2897
3628
  });
2898
3629
 
2899
3630
  // src/commands/te-analysis/meta/asset/batch-sql-export.ts
@@ -2902,9 +3633,10 @@ var analysisMetaAssetBatchSqlExport = createAnalysisGovernanceCapabilityCommand(
2902
3633
  command: "batch-sql-export",
2903
3634
  capabilityId: "governance.asset.batch_export_sql",
2904
3635
  description: "Batch export asset SQL definitions.",
2905
- flags: [projectIdFlag, nodeIdsFlag, payloadFlag],
3636
+ flags: [projectIdFlag, nodeIdsFlag, payloadFlag, ...governanceExportFlags("xlsx")],
2906
3637
  risk: "read",
2907
- buildInput: (ctx) => assetGovernanceInput(ctx, ["node_ids"])
3638
+ asyncArtifact: true,
3639
+ buildInput: (ctx) => assetGovernanceInput(ctx, [...governanceExportFields, "node_ids"])
2908
3640
  });
2909
3641
 
2910
3642
  // src/commands/te-analysis/meta/asset/batch-delete.ts
@@ -2979,9 +3711,10 @@ var analysisMetaAssetOperationRecordExport = createAnalysisGovernanceCapabilityC
2979
3711
  command: "export",
2980
3712
  capabilityId: "governance.operation_record.export",
2981
3713
  description: "Export one asset batch operation record result.",
2982
- flags: [projectIdFlag, recordIdFlag, payloadFlag],
3714
+ flags: [projectIdFlag, recordIdFlag, payloadFlag, ...governanceExportFlags("xlsx")],
2983
3715
  risk: "read",
2984
- buildInput: (ctx) => assetGovernanceInput(ctx, ["record_id"])
3716
+ asyncArtifact: true,
3717
+ buildInput: (ctx) => assetGovernanceInput(ctx, [...governanceExportFields, "record_id"])
2985
3718
  });
2986
3719
 
2987
3720
  // src/commands/te-analysis/governance/asset-authentication-shared.ts
@@ -3359,7 +4092,7 @@ var reportList = createAnalysisCapabilityCommand({
3359
4092
  resource: "report",
3360
4093
  command: "list",
3361
4094
  capabilityId: "analysis.report.list",
3362
- description: "List analysis reports visible to the current user through the capability gateway.",
4095
+ description: "List analysis reports the current user can edit or manage. Read-only reports are excluded; use analysis asset search for readable asset discovery.",
3363
4096
  flags: [
3364
4097
  projectIdFlag,
3365
4098
  queriesFlag,
@@ -3439,7 +4172,7 @@ var REPORT_WRITE_MODEL_TYPE_VALUES = [
3439
4172
  ];
3440
4173
  var REPORT_WRITE_MODEL_DESCRIPTION = `${AI_MODEL_DESCRIPTION} Report create/update also supports tag for saved tag report data; use tag as the AI-facing spelling.`;
3441
4174
  var AI_DEFINITION_DESCRIPTION = 'Event metrics: omit optional display_name unless the target command schema explicitly supports it. Funnel step filters use event_property_name (not field) and string-array values, including "true"/"false" for boolean properties. For tag/cluster filters, cluster_date_policy accepts LATEST (default), AUTO (match the computed result for each analysis date), or SPECIFIED (requires specified_cluster_date in yyyy-MM-dd format). AI-facing model definition JSON. Do not pass raw QP, events, event_view, visual_view, or analysis_query. Distribution filters must be attached to the corresponding distribution_metrics[].filters; do not use top-level filters or relation. For path definitions, global filters support user_property, cluster, and tag only; event_property is not supported. session_unit accepts second (1..999), minute (1..999), or hour (1..24). Do not use day; express one day as session_interval=24 and session_unit=hour. For SQL, a simple query is {"sql":"select ..."}; raw variables use ${name}, while typed params use ${Text:name}, ${Selector:name}, or ${PartDate:name}. PartDate expands to a complete predicate, so write WHERE ${PartDate:d}, not a column followed by the placeholder. A part_date parameter may set boolean use_timezone; it defaults to false and controls whether that parameter uses the query effective timezone. Selector value must match one options[].value. Trino identifiers containing #, $, @, spaces, punctuation, or a reserved word must be delimited with double quotes, for example SELECT "#user_id", "$part_event", "end" FROM ...; single quotes are string literals. For multiline SQL JSON, the decoded sql value must contain a real line break; do not submit a literal \\n sequence outside quoted SQL text. Queries against an event table must include a date-partition predicate on the quoted "$part_date" column, for example WHERE "$part_date" BETWEEN \'2026-07-01\' AND \'2026-07-07\'; the backend rejects event-table SQL without it. The CLI preserves SQL text and never auto-quotes identifiers.';
3442
- var REPORT_WRITE_DEFINITION_DESCRIPTION = `${AI_DEFINITION_DESCRIPTION} For model_type=tag, pass a tag report intent such as {"tag":{"tag_name":"vip_users","time_range":{"mode":"recent","unit":"day","value":7}}}. Tags are supported for report create/update and report data, not ad-hoc analysis.`;
4175
+ var REPORT_WRITE_DEFINITION_DESCRIPTION = `${AI_DEFINITION_DESCRIPTION} Saved-report filters may contain one compound group level only; every item inside that group must be a leaf without another items array. Historical deeper trees remain readable but cannot be written back as a definition. For model_type=tag, pass a tag report intent such as {"tag":{"tag_name":"vip_users","time_range":{"mode":"recent","unit":"day","value":7}}}. Tags are supported for report create/update and report data, not ad-hoc analysis.`;
3443
4176
  function aiModelTypeFlag(required) {
3444
4177
  return {
3445
4178
  name: "model-type",
@@ -3453,18 +4186,32 @@ function validateFunnelStepFilters(modelType, definition) {
3453
4186
  const steps = definition.funnel.steps;
3454
4187
  if (!Array.isArray(steps)) return;
3455
4188
  const errors = [];
3456
- steps.forEach((step, stepIndex) => {
3457
- if (!isRecord(step) || !Array.isArray(step.filters)) return;
3458
- step.filters.forEach((filter, filterIndex) => {
3459
- if (!isRecord(filter)) return;
3460
- const location = `definition.funnel.steps[${stepIndex}].filters[${filterIndex}]`;
3461
- if ("field" in filter || typeof filter.event_property_name !== "string" || !filter.event_property_name.trim()) {
3462
- errors.push(`${location}: use a non-empty event_property_name, not a generic field object.`);
4189
+ const visit = (filter, location) => {
4190
+ if (!isRecord(filter)) {
4191
+ errors.push(`${location}: use a filter object.`);
4192
+ return;
4193
+ }
4194
+ if ("items" in filter) {
4195
+ if (Object.keys(filter).some((key) => key !== "items" && key !== "relation")) {
4196
+ errors.push(`${location}: compound groups contain only items and relation; put event_property_name and operator on their leaf items.`);
3463
4197
  }
3464
- if ("values" in filter && (!Array.isArray(filter.values) || filter.values.some((value) => typeof value !== "string"))) {
3465
- errors.push(`${location}.values: use an array of strings (boolean properties use "true" or "false").`);
4198
+ if (!Array.isArray(filter.items) || filter.items.length === 0) {
4199
+ errors.push(`${location}.items: use a non-empty array of event-property filters or nested groups.`);
4200
+ } else {
4201
+ filter.items.forEach((item, index) => visit(item, `${location}.items[${index}]`));
3466
4202
  }
3467
- });
4203
+ return;
4204
+ }
4205
+ if ("field" in filter || typeof filter.event_property_name !== "string" || !filter.event_property_name.trim()) {
4206
+ errors.push(`${location}: use a non-empty event_property_name, not a generic field object.`);
4207
+ }
4208
+ if ("values" in filter && (!Array.isArray(filter.values) || filter.values.some((value) => typeof value !== "string"))) {
4209
+ errors.push(`${location}.values: use an array of strings (boolean properties use "true" or "false").`);
4210
+ }
4211
+ };
4212
+ steps.forEach((step, stepIndex) => {
4213
+ if (!isRecord(step) || !Array.isArray(step.filters)) return;
4214
+ step.filters.forEach((filter, filterIndex) => visit(filter, `definition.funnel.steps[${stepIndex}].filters[${filterIndex}]`));
3468
4215
  });
3469
4216
  if (errors.length) {
3470
4217
  throw new CliValidationError(errors.join("\n"), {
@@ -3554,7 +4301,7 @@ var reportCreate = createAnalysisCapabilityCommand({
3554
4301
  function validateReportUpdate(ctx) {
3555
4302
  const hasDefinition = ctx.str("definition") !== "";
3556
4303
  const hasResolutions = ctx.json("resolutions") !== void 0;
3557
- const hasMetadataUpdate = ctx.str("report-name") !== "" || ctx.str("report-desc") !== "";
4304
+ const hasMetadataUpdate = ctx.str("report-name") !== "" || optionalStringPreserveEmpty(ctx, "report-desc") !== void 0;
3558
4305
  if (!hasDefinition && !hasMetadataUpdate) {
3559
4306
  throw new Error("At least one of --report-name, --report-desc, or --definition is required.");
3560
4307
  }
@@ -3576,7 +4323,7 @@ var reportUpdate = createAnalysisCapabilityCommand({
3576
4323
  { name: "report-id", type: "number", required: true, desc: "Report ID to update." },
3577
4324
  { name: "report-version", type: "number", required: true, desc: "Current report version from report get." },
3578
4325
  { name: "report-name", type: "string", required: false, desc: "New report display name." },
3579
- { name: "report-desc", type: "string", required: false, desc: "New report description." },
4326
+ { name: "report-desc", type: "string", required: false, desc: "New report description. Pass an empty string to clear it; omit to preserve it." },
3580
4327
  reportWriteModelTypeFlag(false),
3581
4328
  reportWriteDefinitionFlag(false),
3582
4329
  modelDefinitionIntentSnapshotFlag,
@@ -3592,7 +4339,7 @@ var reportUpdate = createAnalysisCapabilityCommand({
3592
4339
  report_id: ctx.num("report-id"),
3593
4340
  version: ctx.num("report-version"),
3594
4341
  report_name: optionalString(ctx, "report-name"),
3595
- report_desc: optionalString(ctx, "report-desc"),
4342
+ report_desc: optionalStringPreserveEmpty(ctx, "report-desc"),
3596
4343
  model_type: optionalString(ctx, "model-type"),
3597
4344
  definition: optionalJson(ctx, "definition"),
3598
4345
  resolutions: optionalJson(ctx, "resolutions"),
@@ -3841,7 +4588,7 @@ var dashboardList = createAnalysisCapabilityCommand({
3841
4588
  resource: "dashboard",
3842
4589
  command: "list",
3843
4590
  capabilityId: "analysis.dashboard.list",
3844
- description: "List dashboards visible to the current user through the capability gateway.",
4591
+ description: "List dashboards the current user can update or manage. Read-only dashboards are excluded; use analysis asset search for readable asset discovery.",
3845
4592
  flags: [
3846
4593
  projectIdFlag,
3847
4594
  queriesFlag,
@@ -4176,14 +4923,13 @@ var dashboardReportDataExport = createAnalysisCapabilityCommand({
4176
4923
  clusterQueryScopeFlag,
4177
4924
  slaveClusterIdFlag,
4178
4925
  { name: "zone-offset", type: "number", required: false, desc: reportDataZoneOffsetDescription },
4179
- { name: "use-cache", type: "boolean", required: false, desc: "Whether to use cache. Default: true." },
4180
4926
  { name: "request-id", type: "string", required: false, desc: "Optional cli_<32 lowercase hex> request ID. Generated when omitted." },
4181
4927
  asyncTimeoutSecondsFlag,
4182
4928
  { name: "artifact-format", type: "string", required: false, desc: "Artifact format. Only jsonl is supported." }
4183
4929
  ],
4184
4930
  risk: "read",
4185
4931
  validate: validateClusterQueryRouting,
4186
- buildInput: dashboardReportDataInput
4932
+ buildInput: (ctx) => dashboardReportDataInput(ctx, false)
4187
4933
  });
4188
4934
 
4189
4935
  // src/commands/te-analysis/dashboard-report-data/index.ts
@@ -6592,7 +7338,7 @@ var sqlTableColumns = createAnalysisCapabilityCommand({
6592
7338
  name: "table-ref",
6593
7339
  type: "string",
6594
7340
  required: true,
6595
- desc: "Exact table_ref returned by analysis sql-table list; a unique table-only reference is also accepted."
7341
+ desc: "Exact table_ref or quoted sql_reference returned by analysis sql-table list; a unique table-only reference is also accepted."
6596
7342
  },
6597
7343
  sqlTableUsageFlag
6598
7344
  ],
@@ -6609,7 +7355,7 @@ var sqlTableList = createAnalysisCapabilityCommand({
6609
7355
  resource: "sql-table",
6610
7356
  command: "list",
6611
7357
  capabilityId: "analysis.sql_table.list",
6612
- description: "List server-authoritative SQL table references queryable by the current user in a project.",
7358
+ description: "List server-authoritative SQL table references queryable by the current user in a project, including authorized space tables. Use sql_reference when copying a table into SQL.",
6613
7359
  flags: [projectIdFlag, queriesFlag, directoryLimitFlag, directoryOffsetFlag, sqlTableUsageFlag],
6614
7360
  risk: "read",
6615
7361
  validate: validateQueriesFlag,