@thinkingai/ae-cli 6.1.9 → 6.1.11

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 (232) hide show
  1. package/README.md +25 -3
  2. package/README.zh.md +25 -3
  3. package/dist/{auth-56Z45UVR.js → auth-GDV3H5I4.js} +18 -8
  4. package/dist/{auth-ENM3FE6L.js → auth-NN55553T.js} +3 -3
  5. package/dist/{capability-2H6PAOA3.js → capability-TPORIKRQ.js} +19 -15
  6. package/dist/{capability-YPOQX6PL.js → capability-VQNC5CF7.js} +19 -15
  7. package/dist/chunk-4NN5IWVN.js +26 -0
  8. package/dist/{chunk-DB4Q3ANU.js → chunk-6A2FUCIS.js} +3 -3
  9. package/dist/{chunk-PVBYJWC2.js → chunk-7KQWSBSL.js} +4 -4
  10. package/dist/{chunk-24BAVOX3.js → chunk-AXDXJTPC.js} +3 -1
  11. package/dist/{chunk-CPVTECJ3.js → chunk-DT6Y3TD7.js} +5 -5
  12. package/dist/{chunk-EGEIXA2Z.js → chunk-DWO43OIB.js} +0 -248
  13. package/dist/{chunk-V6FR6WTW.js → chunk-GJJA4CQZ.js} +34 -2
  14. package/dist/{chunk-RGXCNC4N.js → chunk-GS2P7LFD.js} +50 -17
  15. package/dist/{chunk-LYVNONC4.js → chunk-JHENBQ5B.js} +35 -0
  16. package/dist/{chunk-3P3562ZX.js → chunk-MVDZ7DBQ.js} +5 -5
  17. package/dist/{chunk-LCXU3AAT.js → chunk-NBPKWKRA.js} +2 -2
  18. package/dist/{chunk-HQ2A7ITL.js → chunk-RBNKI5ZW.js} +50 -17
  19. package/dist/{chunk-LHVM35J4.js → chunk-TS6BUGUY.js} +4 -4
  20. package/dist/chunk-TUKQZTMI.js +250 -0
  21. package/dist/{chunk-J7MZHDHQ.js → chunk-VKD5WQKN.js} +5 -5
  22. package/dist/{chunk-3KI3RRXX.js → chunk-VTXHDCBW.js} +3 -3
  23. package/dist/{chunk-KTYR3U6D.js → chunk-YTG6Q75E.js} +10 -6
  24. package/dist/{chunk-IR4ZLVPW.js → chunk-YV52FB5G.js} +25 -3
  25. package/dist/{chunk-E7UXXHO3.js → chunk-Z3OXWCIA.js} +3 -3
  26. package/dist/{cli-token-4SBMXAUK.js → cli-token-GL5MS5FK.js} +4 -4
  27. package/dist/{client-PP5FETMW.js → client-DAIPF7XN.js} +4 -4
  28. package/dist/{community-report-client-KK2QBANO.js → community-report-client-M2RW4MXD.js} +2 -2
  29. package/dist/config-4VZNLBKF.js +489 -0
  30. package/dist/index.js +94 -35
  31. package/dist/memory-RWJW4XFO.js +892 -0
  32. package/dist/memory-VO2ZJCRT.js +892 -0
  33. package/dist/{metadata-JIQ77HFY.js → metadata-YGTHR2XJ.js} +8 -8
  34. package/dist/{metadata-AN3YFZEV.js → metadata-ZRN2GHPN.js} +8 -8
  35. package/dist/{model-JASQVOFD.js → model-CLUIK3K5.js} +7 -5
  36. package/dist/{raw-XJCAT3HX.js → raw-52B4UKO4.js} +8 -7
  37. package/dist/sync-3REDHGY6.js +10259 -0
  38. package/dist/{te-agent-JHUG6DVV.js → te-agent-XNPELAKX.js} +580 -84
  39. package/dist/{te-analysis-IWQZO423.js → te-analysis-GJI5FZUL.js} +1611 -25
  40. package/dist/{te-analysis-X222IRYR.js → te-analysis-N2BRDJZ5.js} +1611 -25
  41. package/dist/{te-community-QOYIYEJI.js → te-community-SQXKE5OO.js} +7 -7
  42. package/dist/{te-community-UFKI6ONP.js → te-community-TYSNU3NQ.js} +7 -7
  43. package/dist/{te-dataops-PZQ5NQLY.js → te-dataops-5TM7WZDI.js} +370 -189
  44. package/dist/{te-dataops-XTWVTJCA.js → te-dataops-OWIADNSM.js} +370 -189
  45. package/dist/{te-engage-NLZUPSBK.js → te-engage-F7V55KVW.js} +333 -105
  46. package/dist/{te-engage-E7F4HTXU.js → te-engage-FMYAYCNV.js} +333 -105
  47. package/dist/{te-experiment-N63WF7XA.js → te-experiment-JWXOYJ3W.js} +83 -10
  48. package/dist/{te-experiment-D32TB6ZB.js → te-experiment-UPDMHCAJ.js} +83 -10
  49. package/dist/{te-kb-E7NSCBRB.js → te-kb-VRMEY3D4.js} +6 -6
  50. package/dist/{te-meta-GBDTMPEL.js → te-meta-53BVXPFI.js} +7 -7
  51. package/dist/{te-meta-ZTLTSHXC.js → te-meta-TOCBPBXI.js} +7 -7
  52. package/dist/{te-system-AH7DMCAQ.js → te-system-XGS5EQIQ.js} +4 -4
  53. package/dist/{te-team-BQ3SKSZV.js → te-team-BZRDV2CM.js} +7 -7
  54. package/dist/{update-DKG6UXEM.js → update-HEDXGOJH.js} +8 -6
  55. package/package.json +5 -2
  56. package/skills/ae-agent/SKILL.md +178 -16
  57. package/skills/ae-agent/references/add-skill.md +22 -10
  58. package/skills/ae-agent/references/edit-skill.md +24 -14
  59. package/skills/ae-agent/references/find-archived-conversations.md +82 -0
  60. package/skills/ae-agent/references/restore-conversation.md +54 -0
  61. package/skills/ae-agent/references/upload-skill.md +24 -14
  62. package/skills/ae-analysis/SKILL.md +1 -1
  63. package/skills/ae-analysis/references/command_index.md +109 -42
  64. package/skills/ae-analysis/references/debug_data_list.md +28 -0
  65. package/skills/ae-analysis/references/debug_device_add.md +23 -0
  66. package/skills/ae-analysis/references/debug_device_list.md +19 -0
  67. package/skills/ae-analysis/references/debug_device_select.md +22 -0
  68. package/skills/ae-analysis/references/entity_id_import_options.md +1 -1
  69. package/skills/ae-analysis/references/plan_sync_display_names.md +28 -0
  70. package/skills/ae-analysis/references/project_access_detail_get.md +3 -3
  71. package/skills/ae-analysis/references/project_data_power_delete.md +3 -3
  72. package/skills/ae-analysis/references/project_data_power_get.md +3 -3
  73. package/skills/ae-analysis/references/project_data_power_list.md +3 -3
  74. package/skills/ae-analysis/references/project_data_power_upsert.md +3 -3
  75. package/skills/ae-analysis/references/project_entity_create.md +3 -3
  76. package/skills/ae-analysis/references/project_entity_delete.md +3 -3
  77. package/skills/ae-analysis/references/project_entity_event_list.md +3 -3
  78. package/skills/ae-analysis/references/project_entity_get.md +3 -3
  79. package/skills/ae-analysis/references/project_entity_list.md +3 -3
  80. package/skills/ae-analysis/references/project_entity_update.md +3 -3
  81. package/skills/ae-analysis/references/project_function_list.md +3 -3
  82. package/skills/ae-analysis/references/project_info_create.md +25 -0
  83. package/skills/ae-analysis/references/project_info_delete.md +24 -0
  84. package/skills/ae-analysis/references/project_info_get.md +3 -3
  85. package/skills/ae-analysis/references/project_info_list.md +3 -3
  86. package/skills/ae-analysis/references/project_info_update.md +3 -3
  87. package/skills/ae-analysis/references/project_mark_time_create.md +3 -3
  88. package/skills/ae-analysis/references/project_mark_time_delete.md +3 -3
  89. package/skills/ae-analysis/references/project_mark_time_list.md +3 -3
  90. package/skills/ae-analysis/references/project_mark_time_update.md +3 -3
  91. package/skills/ae-analysis/references/project_member_add.md +3 -3
  92. package/skills/ae-analysis/references/project_member_batch_update.md +3 -3
  93. package/skills/ae-analysis/references/project_member_candidate_list.md +3 -3
  94. package/skills/ae-analysis/references/project_member_handover_export.md +3 -3
  95. package/skills/ae-analysis/references/project_member_handover_run.md +3 -3
  96. package/skills/ae-analysis/references/project_member_import.md +3 -3
  97. package/skills/ae-analysis/references/project_member_list.md +3 -3
  98. package/skills/ae-analysis/references/project_member_receiver_list.md +3 -3
  99. package/skills/ae-analysis/references/project_member_remove.md +3 -3
  100. package/skills/ae-analysis/references/project_member_update.md +3 -3
  101. package/skills/ae-analysis/references/project_owner_update.md +3 -3
  102. package/skills/ae-analysis/references/project_permission_binding_list.md +3 -3
  103. package/skills/ae-analysis/references/project_receive_status_update.md +3 -3
  104. package/skills/ae-analysis/references/project_role_delete.md +3 -3
  105. package/skills/ae-analysis/references/project_role_function_list.md +3 -3
  106. package/skills/ae-analysis/references/project_role_get.md +3 -3
  107. package/skills/ae-analysis/references/project_role_list.md +3 -3
  108. package/skills/ae-analysis/references/project_role_upsert.md +3 -3
  109. package/skills/ae-analysis/references/project_role_user_list.md +3 -3
  110. package/skills/ae-analysis/references/project_space_list.md +1 -1
  111. package/skills/ae-analysis/references/project_timezone_get.md +3 -3
  112. package/skills/ae-analysis/references/project_timezone_overview.md +3 -3
  113. package/skills/ae-analysis/references/project_timezone_update.md +3 -3
  114. package/skills/ae-analysis/references/project_user_id_items_update.md +3 -3
  115. package/skills/ae-analysis/references/system_admin_function_list.md +22 -0
  116. package/skills/ae-analysis/references/system_admin_function_update.md +26 -0
  117. package/skills/ae-analysis/references/system_admin_list.md +21 -0
  118. package/skills/ae-analysis/references/system_admin_remove.md +25 -0
  119. package/skills/ae-analysis/references/system_admin_upsert.md +25 -0
  120. package/skills/ae-analysis/references/system_function_list.md +21 -0
  121. package/skills/ae-analysis/references/system_member_add.md +25 -0
  122. package/skills/ae-analysis/references/system_member_candidate_list.md +22 -0
  123. package/skills/ae-analysis/references/system_member_delete.md +25 -0
  124. package/skills/ae-analysis/references/system_member_list.md +24 -0
  125. package/skills/ae-analysis/references/system_member_mfa_unbind.md +25 -0
  126. package/skills/ae-analysis/references/system_member_password_reset.md +32 -0
  127. package/skills/ae-analysis/references/system_member_project_batch_update.md +27 -0
  128. package/skills/ae-analysis/references/system_member_status_update.md +26 -0
  129. package/skills/ae-analysis/references/system_member_update.md +23 -0
  130. package/skills/ae-analysis/references/system_mfa_get.md +21 -0
  131. package/skills/ae-analysis/references/system_mfa_update.md +25 -0
  132. package/skills/ae-analysis/references/system_node_monitor_list.md +24 -0
  133. package/skills/ae-analysis/references/system_oauth2_update.md +22 -0
  134. package/skills/ae-analysis/references/system_ops_alert_contact_delete.md +25 -0
  135. package/skills/ae-analysis/references/system_ops_alert_contact_list.md +23 -0
  136. package/skills/ae-analysis/references/system_ops_alert_contact_test.md +29 -0
  137. package/skills/ae-analysis/references/system_ops_alert_contact_upsert.md +39 -0
  138. package/skills/ae-analysis/references/system_preference_get.md +21 -0
  139. package/skills/ae-analysis/references/system_preference_update.md +22 -0
  140. package/skills/ae-analysis/references/system_project_usage_list.md +28 -0
  141. package/skills/ae-analysis/references/system_query_alert_rule_list.md +21 -0
  142. package/skills/ae-analysis/references/system_query_alert_rule_update.md +26 -0
  143. package/skills/ae-analysis/references/system_query_monitor_overview.md +26 -0
  144. package/skills/ae-analysis/references/system_query_task_cancel.md +25 -0
  145. package/skills/ae-analysis/references/system_query_task_export.md +49 -0
  146. package/skills/ae-analysis/references/system_query_task_get.md +23 -0
  147. package/skills/ae-analysis/references/system_query_task_list.md +35 -0
  148. package/skills/ae-analysis/references/system_query_task_options.md +27 -0
  149. package/skills/ae-analysis/references/system_receiver_address_delete.md +26 -0
  150. package/skills/ae-analysis/references/system_receiver_address_overview.md +21 -0
  151. package/skills/ae-analysis/references/system_receiver_address_project_list.md +21 -0
  152. package/skills/ae-analysis/references/system_receiver_address_promote.md +24 -0
  153. package/skills/ae-analysis/references/system_receiver_address_upsert.md +27 -0
  154. package/skills/ae-analysis/references/system_receiver_detection_get.md +22 -0
  155. package/skills/ae-analysis/references/system_receiver_detection_run.md +22 -0
  156. package/skills/ae-analysis/references/system_receiver_detection_update.md +25 -0
  157. package/skills/ae-analysis/references/system_role_delete.md +26 -0
  158. package/skills/ae-analysis/references/system_role_function_list.md +22 -0
  159. package/skills/ae-analysis/references/system_role_get.md +22 -0
  160. package/skills/ae-analysis/references/system_role_list.md +24 -0
  161. package/skills/ae-analysis/references/system_role_upsert.md +27 -0
  162. package/skills/ae-analysis/references/system_role_user_list.md +22 -0
  163. package/skills/ae-analysis/references/system_seat_list.md +25 -0
  164. package/skills/ae-analysis/references/system_seat_update.md +26 -0
  165. package/skills/ae-analysis/references/system_smtp_delete.md +24 -0
  166. package/skills/ae-analysis/references/system_smtp_get.md +21 -0
  167. package/skills/ae-analysis/references/system_smtp_test.md +22 -0
  168. package/skills/ae-analysis/references/system_smtp_upsert.md +31 -0
  169. package/skills/ae-analysis/references/system_third_party_login_disable.md +25 -0
  170. package/skills/ae-analysis/references/system_third_party_login_list.md +21 -0
  171. package/skills/ae-analysis/references/system_third_party_login_upsert.md +33 -0
  172. package/skills/ae-analysis/references/system_usage_overview.md +21 -0
  173. package/skills/ae-analysis/references/system_usage_trend_export.md +44 -0
  174. package/skills/ae-analysis/references/system_usage_trend_query.md +28 -0
  175. package/skills/ae-dataops/SKILL.md +1 -1
  176. package/skills/ae-dataops/references/dataops-flow-create.md +51 -16
  177. package/skills/ae-engage/SKILL.md +56 -26
  178. package/skills/ae-engage/references/activity-activity.md +3 -0
  179. package/skills/ae-engage/references/activity-approval.md +12 -4
  180. package/skills/ae-engage/references/activity-task.md +21 -6
  181. package/skills/ae-engage/references/activity-topic.md +31 -13
  182. package/skills/ae-engage/references/add-channel.md +170 -41
  183. package/skills/ae-engage/references/build-task-save-guide.md +40 -27
  184. package/skills/ae-engage/references/channel-update-config.md +3 -2
  185. package/skills/ae-engage/references/common-metric.md +48 -120
  186. package/skills/ae-engage/references/flow-detail.md +13 -0
  187. package/skills/ae-engage/references/preset-event.md +12 -32
  188. package/skills/ae-engage/references/save-flow.md +127 -64
  189. package/skills/ae-engage/references/save-task.md +197 -59
  190. package/skills/ae-engage/references/scene-config-metric.md +3 -0
  191. package/skills/ae-engage/references/scene-preset-metric.md +8 -37
  192. package/skills/ae-engage/references/scene-strategy-audience.md +51 -643
  193. package/skills/ae-engage/references/scene-strategy.md +6 -6
  194. package/skills/ae-engage/references/task-detail.md +10 -0
  195. package/skills/ae-engage/references/task-submit-approval.md +45 -0
  196. package/skills/ae-engage/references/validate-flow-node-config.md +1 -1
  197. package/skills/ae-experiment/SKILL.md +21 -1
  198. package/skills/ae-experiment/references/check_experiment_ready.md +2 -0
  199. package/skills/ae-experiment/references/query_experiment_detail.md +6 -0
  200. package/skills/ae-experiment/references/query_experiment_list.md +4 -0
  201. package/skills/ae-experiment/references/query_experiment_list_archived.md +3 -0
  202. package/skills/ae-experiment/references/query_metric_detail.md +5 -0
  203. package/skills/ae-experiment/references/query_metric_list.md +3 -0
  204. package/skills/ae-experiment/references/save_experiment.md +46 -3
  205. package/skills/ae-experiment/references/save_metric.md +62 -0
  206. package/skills/ae-experiment/references/save_submit_experiment.md +3 -0
  207. package/skills/ae-experiment-design/SKILL.md +149 -0
  208. package/skills/ae-experiment-design/agents/openai.yaml +4 -0
  209. package/skills/ae-experiment-design/references/client-experiment-sdk.md +147 -0
  210. package/skills/ae-experiment-design/references/experiment-creation.md +108 -0
  211. package/skills/ae-experiment-design/references/experiment-sdk-contract.md +100 -0
  212. package/skills/ae-experiment-design/references/exposure-contract.md +91 -0
  213. package/skills/ae-experiment-design/references/hybrid-experiment-sdk.md +74 -0
  214. package/skills/ae-experiment-design/references/metric-readiness.md +143 -0
  215. package/skills/ae-experiment-design/references/platform-operations.md +99 -0
  216. package/skills/ae-experiment-design/references/sdk-index.md +76 -0
  217. package/skills/ae-experiment-design/references/sdk-integration.md +114 -0
  218. package/skills/ae-experiment-design/references/sdk-troubleshooting.md +139 -0
  219. package/skills/ae-experiment-design/references/server-experiment-sdk.md +78 -0
  220. package/skills/ae-experiment-design/scripts/calculate_experiment_plan.py +450 -0
  221. package/skills/ae-experiment-insight/SKILL.md +149 -0
  222. package/skills/ae-experiment-insight/agents/openai.yaml +4 -0
  223. package/skills/ae-experiment-insight/references/decision-framework.md +69 -0
  224. package/skills/ae-experiment-insight/references/diagnostic-playbook.md +225 -0
  225. package/skills/ae-experiment-insight/references/platform-operations.md +76 -0
  226. package/skills/ae-experiment-insight/scripts/analyze_experiment.py +478 -0
  227. package/skills/ae-generate-tracking-code/SKILL.md +24 -8
  228. package/skills/ae-generate-tracking-code/references/debug-script.md +46 -37
  229. package/skills/ae-generate-tracking-plan/SKILL.md +26 -1
  230. package/skills/ae-metadata/SKILL.md +1 -1
  231. package/dist/config-BSSALXEN.js +0 -128
  232. package/dist/sync-QFP4XFN3.js +0 -485
@@ -0,0 +1,478 @@
1
+ #!/usr/bin/env python3
2
+ """Calculate deterministic SRM and treatment-versus-control comparisons."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import math
8
+ import sys
9
+ from typing import Any
10
+
11
+
12
+ def normal_ppf(probability: float) -> float:
13
+ """Return the inverse standard-normal CDF using Acklam's approximation."""
14
+ if not 0.0 < probability < 1.0:
15
+ raise ValueError("probability must be between 0 and 1")
16
+ a = (
17
+ -3.969683028665376e01,
18
+ 2.209460984245205e02,
19
+ -2.759285104469687e02,
20
+ 1.383577518672690e02,
21
+ -3.066479806614716e01,
22
+ 2.506628277459239e00,
23
+ )
24
+ b = (
25
+ -5.447609879822406e01,
26
+ 1.615858368580409e02,
27
+ -1.556989798598866e02,
28
+ 6.680131188771972e01,
29
+ -1.328068155288572e01,
30
+ )
31
+ c = (
32
+ -7.784894002430293e-03,
33
+ -3.223964580411365e-01,
34
+ -2.400758277161838e00,
35
+ -2.549732539343734e00,
36
+ 4.374664141464968e00,
37
+ 2.938163982698783e00,
38
+ )
39
+ d = (
40
+ 7.784695709041462e-03,
41
+ 3.224671290700398e-01,
42
+ 2.445134137142996e00,
43
+ 3.754408661907416e00,
44
+ )
45
+ low = 0.02425
46
+ high = 1.0 - low
47
+ if probability < low:
48
+ q = math.sqrt(-2.0 * math.log(probability))
49
+ return (
50
+ (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5])
51
+ / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1.0)
52
+ )
53
+ if probability <= high:
54
+ q = probability - 0.5
55
+ r = q * q
56
+ return (
57
+ (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5])
58
+ * q
59
+ / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1.0)
60
+ )
61
+ q = math.sqrt(-2.0 * math.log(1.0 - probability))
62
+ return -(
63
+ (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5])
64
+ / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1.0)
65
+ )
66
+
67
+
68
+ def regularized_gamma_q(shape: float, value: float) -> float:
69
+ """Return the regularized upper incomplete gamma function Q(shape, value)."""
70
+ if shape <= 0.0 or value < 0.0:
71
+ raise ValueError("invalid gamma arguments")
72
+ if value == 0.0:
73
+ return 1.0
74
+ epsilon = 3e-14
75
+ minimum = 1e-300
76
+ max_iterations = 10000
77
+ log_gamma = math.lgamma(shape)
78
+
79
+ if value < shape + 1.0:
80
+ term = 1.0 / shape
81
+ total = term
82
+ current = shape
83
+ for _ in range(max_iterations):
84
+ current += 1.0
85
+ term *= value / current
86
+ total += term
87
+ if abs(term) < abs(total) * epsilon:
88
+ lower = total * math.exp(-value + shape * math.log(value) - log_gamma)
89
+ return max(0.0, min(1.0, 1.0 - lower))
90
+ raise ValueError("gamma series did not converge")
91
+
92
+ b = value + 1.0 - shape
93
+ c = 1.0 / minimum
94
+ d = 1.0 / b
95
+ h = d
96
+ for iteration in range(1, max_iterations + 1):
97
+ coefficient = -iteration * (iteration - shape)
98
+ b += 2.0
99
+ d = coefficient * d + b
100
+ if abs(d) < minimum:
101
+ d = minimum
102
+ c = b + coefficient / c
103
+ if abs(c) < minimum:
104
+ c = minimum
105
+ d = 1.0 / d
106
+ delta = d * c
107
+ h *= delta
108
+ if abs(delta - 1.0) < epsilon:
109
+ result = math.exp(-value + shape * math.log(value) - log_gamma) * h
110
+ return max(0.0, min(1.0, result))
111
+ raise ValueError("gamma continued fraction did not converge")
112
+
113
+
114
+ def beta_continued_fraction(a: float, b: float, x: float) -> float:
115
+ max_iterations = 10000
116
+ epsilon = 3e-14
117
+ minimum = 1e-300
118
+ qab = a + b
119
+ qap = a + 1.0
120
+ qam = a - 1.0
121
+ c = 1.0
122
+ d = 1.0 - qab * x / qap
123
+ if abs(d) < minimum:
124
+ d = minimum
125
+ d = 1.0 / d
126
+ h = d
127
+ for iteration in range(1, max_iterations + 1):
128
+ even = 2 * iteration
129
+ coefficient = iteration * (b - iteration) * x / ((qam + even) * (a + even))
130
+ d = 1.0 + coefficient * d
131
+ if abs(d) < minimum:
132
+ d = minimum
133
+ c = 1.0 + coefficient / c
134
+ if abs(c) < minimum:
135
+ c = minimum
136
+ d = 1.0 / d
137
+ h *= d * c
138
+
139
+ coefficient = -(a + iteration) * (qab + iteration) * x / (
140
+ (a + even) * (qap + even)
141
+ )
142
+ d = 1.0 + coefficient * d
143
+ if abs(d) < minimum:
144
+ d = minimum
145
+ c = 1.0 + coefficient / c
146
+ if abs(c) < minimum:
147
+ c = minimum
148
+ d = 1.0 / d
149
+ delta = d * c
150
+ h *= delta
151
+ if abs(delta - 1.0) < epsilon:
152
+ return h
153
+ raise ValueError("beta continued fraction did not converge")
154
+
155
+
156
+ def regularized_beta(a: float, b: float, x: float) -> float:
157
+ if a <= 0.0 or b <= 0.0 or not 0.0 <= x <= 1.0:
158
+ raise ValueError("invalid beta arguments")
159
+ if x == 0.0:
160
+ return 0.0
161
+ if x == 1.0:
162
+ return 1.0
163
+ front = math.exp(
164
+ math.lgamma(a + b) - math.lgamma(a) - math.lgamma(b)
165
+ + a * math.log(x) + b * math.log(1.0 - x)
166
+ )
167
+ if x < (a + 1.0) / (a + b + 2.0):
168
+ return front * beta_continued_fraction(a, b, x) / a
169
+ return 1.0 - front * beta_continued_fraction(b, a, 1.0 - x) / b
170
+
171
+
172
+ def student_t_two_sided_p(t_value: float, degrees_freedom: float) -> float:
173
+ x = degrees_freedom / (degrees_freedom + t_value * t_value)
174
+ return regularized_beta(degrees_freedom / 2.0, 0.5, x)
175
+
176
+
177
+ def student_t_critical(alpha: float, degrees_freedom: float) -> float:
178
+ low = 0.0
179
+ high = 1.0
180
+ while student_t_two_sided_p(high, degrees_freedom) > alpha:
181
+ high *= 2.0
182
+ if high > 1e6:
183
+ raise ValueError("could not bracket t critical value")
184
+ for _ in range(100):
185
+ middle = (low + high) / 2.0
186
+ if student_t_two_sided_p(middle, degrees_freedom) > alpha:
187
+ low = middle
188
+ else:
189
+ high = middle
190
+ return (low + high) / 2.0
191
+
192
+
193
+ def validate_groups(params: dict[str, Any]) -> list[dict[str, Any]]:
194
+ groups = params.get("groups")
195
+ if not isinstance(groups, list) or len(groups) < 2:
196
+ raise ValueError("groups must contain at least two groups")
197
+ names: set[str] = set()
198
+ for group in groups:
199
+ if not isinstance(group, dict):
200
+ raise ValueError("every group must be an object")
201
+ name = group.get("name")
202
+ if not isinstance(name, str) or not name:
203
+ raise ValueError("every group must have a non-empty name")
204
+ if name in names:
205
+ raise ValueError(f"duplicate group name: {name}")
206
+ names.add(name)
207
+ return groups
208
+
209
+
210
+ def calculate_srm(
211
+ groups: list[dict[str, Any]],
212
+ expected_allocations: dict[str, Any],
213
+ alpha_srm: float,
214
+ ) -> dict[str, Any]:
215
+ names = [group["name"] for group in groups]
216
+ if set(expected_allocations) != set(names):
217
+ raise ValueError("expected_allocations keys must exactly match group names")
218
+ allocations = {name: float(expected_allocations[name]) for name in names}
219
+ if any(value <= 0.0 for value in allocations.values()):
220
+ raise ValueError("expected allocations must be positive")
221
+ if not math.isclose(sum(allocations.values()), 1.0, rel_tol=0.0, abs_tol=1e-9):
222
+ raise ValueError("expected allocations must sum to 1.0")
223
+
224
+ observed: dict[str, int] = {}
225
+ for group in groups:
226
+ assigned = group.get("assigned")
227
+ if isinstance(assigned, bool) or not isinstance(assigned, int) or assigned < 0:
228
+ raise ValueError("every group must have a non-negative integer assigned count")
229
+ observed[group["name"]] = assigned
230
+ total = sum(observed.values())
231
+ if total == 0:
232
+ raise ValueError("total assigned count must be greater than 0")
233
+
234
+ expected = {name: total * allocations[name] for name in names}
235
+ chi_square = sum(
236
+ (observed[name] - expected[name]) ** 2 / expected[name] for name in names
237
+ )
238
+ degrees_freedom = len(names) - 1
239
+ p_value = regularized_gamma_q(degrees_freedom / 2.0, chi_square / 2.0)
240
+ return {
241
+ "chi_square": chi_square,
242
+ "degrees_freedom": degrees_freedom,
243
+ "p_value": p_value,
244
+ "alpha": alpha_srm,
245
+ "srm_detected": p_value < alpha_srm,
246
+ "observed": observed,
247
+ "expected": expected,
248
+ }
249
+
250
+
251
+ def binary_comparison(
252
+ control: dict[str, Any],
253
+ treatment: dict[str, Any],
254
+ alpha: float,
255
+ ) -> dict[str, Any]:
256
+ n0 = int(control.get("sample_size", 0))
257
+ n1 = int(treatment.get("sample_size", 0))
258
+ x0 = int(control.get("successes", -1))
259
+ x1 = int(treatment.get("successes", -1))
260
+ if n0 <= 0 or n1 <= 0:
261
+ raise ValueError("binary sample_size must be greater than 0")
262
+ if not 0 <= x0 <= n0 or not 0 <= x1 <= n1:
263
+ raise ValueError("binary successes must be between 0 and sample_size")
264
+ p0 = x0 / n0
265
+ p1 = x1 / n1
266
+ difference = p1 - p0
267
+ pooled = (x0 + x1) / (n0 + n1)
268
+ pooled_se = math.sqrt(pooled * (1.0 - pooled) * (1.0 / n0 + 1.0 / n1))
269
+ if pooled_se == 0.0:
270
+ z_value = 0.0 if difference == 0.0 else math.copysign(math.inf, difference)
271
+ p_value = 1.0 if difference == 0.0 else 0.0
272
+ else:
273
+ z_value = difference / pooled_se
274
+ p_value = math.erfc(abs(z_value) / math.sqrt(2.0))
275
+ interval_se = math.sqrt(
276
+ p0 * (1.0 - p0) / n0 + p1 * (1.0 - p1) / n1
277
+ )
278
+ critical = normal_ppf(1.0 - alpha / 2.0)
279
+ relative_lift = None if p0 == 0.0 else difference / p0
280
+ return {
281
+ "control_value": p0,
282
+ "treatment_value": p1,
283
+ "absolute_difference": difference,
284
+ "relative_lift": relative_lift,
285
+ "test": "two_sided_pooled_two_proportion_z",
286
+ "statistic": z_value,
287
+ "p_value": p_value,
288
+ "confidence_interval_absolute": [
289
+ difference - critical * interval_se,
290
+ difference + critical * interval_se,
291
+ ],
292
+ }
293
+
294
+
295
+ def continuous_comparison(
296
+ control: dict[str, Any],
297
+ treatment: dict[str, Any],
298
+ alpha: float,
299
+ ) -> dict[str, Any]:
300
+ n0 = int(control.get("sample_size", 0))
301
+ n1 = int(treatment.get("sample_size", 0))
302
+ mean0 = float(control.get("mean"))
303
+ mean1 = float(treatment.get("mean"))
304
+ sd0 = float(control.get("standard_deviation"))
305
+ sd1 = float(treatment.get("standard_deviation"))
306
+ if n0 < 2 or n1 < 2:
307
+ raise ValueError("continuous sample_size must be at least 2")
308
+ if sd0 < 0.0 or sd1 < 0.0:
309
+ raise ValueError("standard_deviation must be non-negative")
310
+ variance0 = sd0 * sd0 / n0
311
+ variance1 = sd1 * sd1 / n1
312
+ standard_error = math.sqrt(variance0 + variance1)
313
+ difference = mean1 - mean0
314
+ if standard_error == 0.0:
315
+ t_value = 0.0 if difference == 0.0 else math.copysign(math.inf, difference)
316
+ p_value = 1.0 if difference == 0.0 else 0.0
317
+ degrees_freedom = math.inf
318
+ critical = normal_ppf(1.0 - alpha / 2.0)
319
+ else:
320
+ t_value = difference / standard_error
321
+ denominator = (
322
+ variance0 * variance0 / (n0 - 1)
323
+ + variance1 * variance1 / (n1 - 1)
324
+ )
325
+ degrees_freedom = (
326
+ math.inf if denominator == 0.0 else (variance0 + variance1) ** 2 / denominator
327
+ )
328
+ if math.isinf(degrees_freedom):
329
+ p_value = math.erfc(abs(t_value) / math.sqrt(2.0))
330
+ critical = normal_ppf(1.0 - alpha / 2.0)
331
+ else:
332
+ p_value = student_t_two_sided_p(t_value, degrees_freedom)
333
+ critical = student_t_critical(alpha, degrees_freedom)
334
+ relative_lift = None if mean0 == 0.0 else difference / mean0
335
+ return {
336
+ "control_value": mean0,
337
+ "treatment_value": mean1,
338
+ "absolute_difference": difference,
339
+ "relative_lift": relative_lift,
340
+ "test": "two_sided_welch_t",
341
+ "statistic": t_value,
342
+ "degrees_freedom": degrees_freedom,
343
+ "p_value": p_value,
344
+ "confidence_interval_absolute": [
345
+ difference - critical * standard_error,
346
+ difference + critical * standard_error,
347
+ ],
348
+ }
349
+
350
+
351
+ def calculate(params: dict[str, Any]) -> dict[str, Any]:
352
+ groups = validate_groups(params)
353
+ metric_type = params.get("metric_type")
354
+ if metric_type not in {"binary", "continuous"}:
355
+ raise ValueError("metric_type must be binary or continuous")
356
+ control_name = params.get("control")
357
+ by_name = {group["name"]: group for group in groups}
358
+ if control_name not in by_name:
359
+ raise ValueError("control must match one group name")
360
+
361
+ alpha = float(params.get("alpha", 0.05))
362
+ alpha_srm = float(params.get("alpha_srm", 0.01))
363
+ if not 0.0 < alpha < 0.5 or not 0.0 < alpha_srm < 0.5:
364
+ raise ValueError("alpha and alpha_srm must be between 0 and 0.5")
365
+ multiple_testing = params.get("multiple_testing", "bonferroni")
366
+ if multiple_testing not in {"bonferroni", "none"}:
367
+ raise ValueError("multiple_testing must be bonferroni or none")
368
+
369
+ srm = None
370
+ expected_allocations = params.get("expected_allocations")
371
+ if expected_allocations is not None:
372
+ if not isinstance(expected_allocations, dict):
373
+ raise ValueError("expected_allocations must be an object")
374
+ srm = calculate_srm(groups, expected_allocations, alpha_srm)
375
+
376
+ control = by_name[control_name]
377
+ comparisons: list[dict[str, Any]] = []
378
+ number_of_comparisons = len(groups) - 1
379
+ comparison_alpha = (
380
+ alpha / number_of_comparisons
381
+ if multiple_testing == "bonferroni"
382
+ else alpha
383
+ )
384
+ for treatment in groups:
385
+ if treatment["name"] == control_name:
386
+ continue
387
+ result = (
388
+ binary_comparison(control, treatment, comparison_alpha)
389
+ if metric_type == "binary"
390
+ else continuous_comparison(control, treatment, comparison_alpha)
391
+ )
392
+ difference = result["absolute_difference"]
393
+ significant = result["p_value"] < comparison_alpha
394
+ if significant and difference > 0:
395
+ classification = "statistically_significant_positive"
396
+ elif significant and difference < 0:
397
+ classification = "statistically_significant_negative"
398
+ else:
399
+ classification = "inconclusive_at_selected_alpha"
400
+ result.update(
401
+ {
402
+ "control": control_name,
403
+ "treatment": treatment["name"],
404
+ "comparison_alpha": comparison_alpha,
405
+ "statistically_significant": significant,
406
+ "classification": classification,
407
+ }
408
+ )
409
+ comparisons.append(result)
410
+
411
+ exposure_coverage: dict[str, float] = {}
412
+ for group in groups:
413
+ assigned = group.get("assigned")
414
+ sample_size = group.get("sample_size")
415
+ if (
416
+ isinstance(assigned, int)
417
+ and assigned > 0
418
+ and isinstance(sample_size, int)
419
+ and sample_size >= 0
420
+ ):
421
+ exposure_coverage[group["name"]] = sample_size / assigned
422
+
423
+ warnings: list[str] = []
424
+ if len(comparisons) > 1 and multiple_testing == "none":
425
+ warnings.append(
426
+ "P-values are unadjusted across treatments; confirm that this matches the pre-registered policy."
427
+ )
428
+ if srm and srm["srm_detected"]:
429
+ warnings.append(
430
+ "SRM was detected from assignment counts; diagnose allocation before using causal comparisons."
431
+ )
432
+
433
+ return {
434
+ "ok": True,
435
+ "data": {
436
+ "metric_type": metric_type,
437
+ "control": control_name,
438
+ "familywise_alpha": alpha,
439
+ "multiple_testing": multiple_testing,
440
+ "comparison_alpha": comparison_alpha,
441
+ "srm": srm,
442
+ "exposure_coverage": exposure_coverage,
443
+ "comparisons": comparisons,
444
+ "warnings": warnings,
445
+ },
446
+ }
447
+
448
+
449
+ def main() -> int:
450
+ try:
451
+ if len(sys.argv) != 2:
452
+ raise ValueError("usage: analyze_experiment.py '<json-object>'")
453
+ raw = sys.argv[1]
454
+ params = json.load(sys.stdin) if raw == "-" else json.loads(raw)
455
+ if not isinstance(params, dict):
456
+ raise ValueError("input must be a JSON object")
457
+ print(json.dumps(calculate(params), ensure_ascii=False, indent=2, allow_nan=False))
458
+ return 0
459
+ except (ValueError, TypeError, KeyError, json.JSONDecodeError) as error:
460
+ print(
461
+ json.dumps(
462
+ {
463
+ "ok": False,
464
+ "error": {
465
+ "type": "validation",
466
+ "code": "INVALID_INPUT",
467
+ "message": str(error),
468
+ },
469
+ },
470
+ ensure_ascii=False,
471
+ indent=2,
472
+ )
473
+ )
474
+ return 2
475
+
476
+
477
+ if __name__ == "__main__":
478
+ raise SystemExit(main())
@@ -91,12 +91,12 @@ First, check if `.ae-cli/draft.json` exists and read existing configuration:
91
91
  **⚠️ Key: SERVER_URL and APP_ID are independent config items. Even if draft.json has project_id and host, you MUST resolve and confirm them. `project_id` ≠ `APP_ID`, `host` ≠ `SERVER_URL`.**
92
92
 
93
93
  1. **APP_ID** — Prefer automatic lookup before asking:
94
- - If `meta.project_id` is known, run `ae-cli analysis project info list` once for the current host.
94
+ - If `meta.project_id` is known, run `ae-cli project info list` once for the current host.
95
95
  - Find the project whose `projectId` matches `meta.project_id`.
96
96
  - If the matched project has `appId`, ask: **"I found APP_ID `<appId>` for project `<projectId>`. Use it? yes / enter new value"**
97
97
  - If the project is missing, ambiguous, or has no `appId`, ask the user to copy APP_ID from AE Admin → "Project Settings" → "Integration Config".
98
98
  2. **SERVER_URL** — Data ingestion endpoint (**different from web URL**; go to AE Admin → "Project Settings" → "Integration Config" → fill in "Public URL")
99
- - If `meta.project_id` is known, you may try `ae-cli analysis project info get --project-id <project_id>` once.
99
+ - If `meta.project_id` is known, you may try `ae-cli project info get --project-id <project_id>` once.
100
100
  - Use the returned value only if the response explicitly contains a receiver URL field such as `serverUrl`, `pushUrl`, `push_url`, `receiverUrl`, `publicUrl`, `publicReceiverAddress`, `privateReceiverAddress`, or equivalent ingestion endpoint field.
101
101
  - If both `publicReceiverAddress` and `privateReceiverAddress` are present, prefer `publicReceiverAddress` as `SERVER_URL` for generated snippets unless the user explicitly needs an internal/private-network receiver.
102
102
  - If a value is found, ask: **"I found SERVER_URL `<url>` for project `<projectId>`. Use it? yes / enter new value"**
@@ -105,7 +105,9 @@ First, check if `.ae-cli/draft.json` exists and read existing configuration:
105
105
 
106
106
  **host handling** (optional):
107
107
  - If you need to fetch plan from AE (no local draft.json) → ask for host
108
- - If local draft.json exists → host is not required; only hint user to open AE Debug page during validation phase
108
+ - If local draft.json exists → host is not required for code generation
109
+ - Before Debug validation, run `ae-cli config current` and confirm the active host matches the target AE environment; if it does not, run `ae-cli config set-host <AE_HOST>`
110
+ - Complete Debug validation with `ae-cli tracking debug-device` and `ae-cli tracking debug-data` by default; only hint the user to open the AE Debug page when those CLI capabilities are unavailable
109
111
 
110
112
  ### If only xlsx file exists (no draft.json)
111
113
 
@@ -605,16 +607,30 @@ Provide validation guidance based on selected platforms:
605
607
 
606
608
  ```
607
609
  Validation steps:
608
- 1. Run validation script:
610
+ 1. Confirm the active AE environment:
611
+ ae-cli config current
612
+
613
+ 2. List existing Debug devices:
614
+ ae-cli tracking debug-device list --project-id <project_id>
615
+
616
+ 3. Create the script's stable device ID if it is missing, then select it:
617
+ ae-cli tracking debug-device add --project-id <project_id> --device-id <device_id> --device-name <name>
618
+ ae-cli tracking debug-device select --project-id <project_id> --device-id <device_id>
619
+
620
+ 4. Run validation script:
609
621
  - Client: run .ae-cli/output/te-debug-client.<ext>
610
622
  - Server: run .ae-cli/output/te-debug-server.<ext>
611
623
 
612
- 2. Open AE Debug page:
613
- https://<host>/#/data/debug
624
+ 5. Query the most recent hour of Debug data:
625
+ ae-cli tracking debug-data list --project-id <project_id> --device-id <device_id>
626
+
627
+ 6. Confirm has_data=true, then inspect event names, property structures, and error fields.
628
+ If needed, add --event-name <event_name> or --start-time "YYYY-MM-DD HH:mm:ss".
614
629
 
615
- 3. Filter by distinct_id=claude-test to view uploaded data
630
+ 7. Only if the CLI capability is unavailable, open the AE Debug page:
631
+ https://<host>/#/data/debug
616
632
 
617
- 4. For LogBus2:
633
+ 8. For LogBus2:
618
634
  - Copy daemon.json to LogBus2 conf/ directory
619
635
  - Start: ./logbus start
620
636
  - Official docs: https://docs-v2.thinkingdata.cn/?version=latest&code=logbus2_installation&lan=en-US