@thinkingai/ae-cli 6.1.27 → 6.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -6
- package/README.zh.md +52 -6
- package/bin/ae-cli.js +5 -0
- package/bin/runtime-capabilities.json +5 -0
- package/cli-installation-guide.md +24 -3
- package/dist/{auth-WA7YGGRT.js → auth-OBQ5KVQM.js} +175 -45
- package/dist/{automation-VAAQBM7Y.js → automation-NAR5DDGL.js} +94 -38
- package/dist/{capability-GAAYJ3IQ.js → capability-LFSVNI5B.js} +57 -28
- package/dist/chunk-2UW3Y463.js +2289 -0
- package/dist/{chunk-VZZNLV3I.js → chunk-3SDHJOCR.js} +68 -18
- package/dist/chunk-6HAUVWOQ.js +132 -0
- package/dist/{chunk-5XJYLEVC.js → chunk-72NJKS7O.js} +4 -4
- package/dist/{chunk-Q5US7JC5.js → chunk-7JAVT5TK.js} +3 -3
- package/dist/{chunk-7KU2WNQE.js → chunk-CVCTFLSO.js} +37 -15
- package/dist/{chunk-GHXZK54S.js → chunk-H3K5UGYC.js} +41 -13
- package/dist/{chunk-OP44TT4H.js → chunk-IN7KKL6K.js} +4 -2
- package/dist/{chunk-S6M42KND.js → chunk-JEUXDY3B.js} +1 -1
- package/dist/{chunk-ZL7SGNIA.js → chunk-JNVVJ273.js} +58 -46
- package/dist/chunk-N6XBLRB7.js +105 -0
- package/dist/{chunk-AP7YP2NX.js → chunk-OAWPT7VP.js} +2 -2
- package/dist/{chunk-AQJPNWZF.js → chunk-PWXDWMCW.js} +7 -7
- package/dist/{chunk-CT7QDJS6.js → chunk-QVRIDN42.js} +2 -2
- package/dist/chunk-QZLRHGVH.js +222 -0
- package/dist/{chunk-RPBOAQQQ.js → chunk-QZUVLFNR.js} +1 -1
- package/dist/{chunk-3YVEAZUN.js → chunk-RJHGDWSC.js} +32 -21
- package/dist/{chunk-MWIA43UJ.js → chunk-VCZE3IWO.js} +10 -8
- package/dist/chunk-YVG6QD3V.js +233 -0
- package/dist/{chunk-WNGFSIOB.js → chunk-Z3Q2AEIN.js} +237 -86
- package/dist/{community-report-client-PEERQCX2.js → community-report-client-TJVCLZ3K.js} +1 -1
- package/dist/compat-check-CII6B74E.js +32 -0
- package/dist/{config-JOD5DOPE.js → config-YZ6O2UQ2.js} +40 -7
- package/dist/{context-NGVFPCBK.js → context-F6FCGTF3.js} +4 -4
- package/dist/{data-integration-BCLQFTGU.js → data-integration-6UAZO4GG.js} +207 -24
- package/dist/evaluation-4LO2O4AF.js +3714 -0
- package/dist/index.js +352 -81
- package/dist/{local-data-upload-client-TWMHFLUA.js → local-data-upload-client-KCMG6J62.js} +1 -1
- package/dist/{memory-CQKH2JAV.js → memory-HHPX3NF2.js} +6 -6
- package/dist/{metadata-NR3PHR23.js → metadata-TIFY24LW.js} +17 -9
- package/dist/{model-VX5M2CJ6.js → model-7UHGWYVR.js} +4 -4
- package/dist/{personal-semantic-preference-2RAI36P3.js → personal-semantic-preference-WKJEGPTM.js} +7 -6
- package/dist/{project-semantic-3BUGP3ZM.js → project-semantic-EIQVZNHW.js} +11 -10
- package/dist/{sync-RZIAV37C.js → sync-RV3OPCTI.js} +8 -7
- package/dist/{te-agent-AORRE2FM.js → te-agent-5ENVZJUH.js} +757 -94
- package/dist/{te-analysis-GP7DSQ7Q.js → te-analysis-USIK6ZZG.js} +584 -100
- package/dist/{te-community-42M7ICDO.js → te-community-WE2UU4XV.js} +722 -228
- package/dist/{te-dataops-BNKNZ4F2.js → te-dataops-XNMO7MBF.js} +9 -6
- package/dist/{te-engage-2CO6LRCX.js → te-engage-6C3HCFQR.js} +13 -12
- package/dist/{te-experiment-652CLXMU.js → te-experiment-LVTRI3QK.js} +55 -21
- package/dist/{te-kb-ULATOTQ3.js → te-kb-TUPGMVBY.js} +409 -85
- package/dist/{te-system-UGHVKBVV.js → te-system-YM657FKO.js} +6 -6
- package/dist/{te-team-YA6SNOJ3.js → te-team-G4VEJJNL.js} +64 -41
- package/dist/{things-DTCOQIQB.js → things-22KQWC23.js} +7 -7
- package/dist/{update-ESNYKUVR.js → update-XX4ILU7X.js} +11 -7
- package/dist/{workspace-JEOHKFPX.js → workspace-LYDAP647.js} +6 -6
- package/package.json +26 -11
- package/skills/ae-agent/SKILL.md +22 -3
- package/skills/ae-agent/references/command_index.md +8 -0
- package/skills/ae-agent/references/resource-package.md +57 -0
- package/skills/ae-analysis/SKILL.md +23 -10
- package/skills/ae-analysis/references/adhoc_export.md +1 -1
- package/skills/ae-analysis/references/agent_review_preflight.md +55 -10
- package/skills/ae-analysis/references/agent_review_priorities_comparisons.md +5 -3
- package/skills/ae-analysis/references/agent_review_submit_to_page.md +60 -24
- package/skills/ae-analysis/references/ai_models/event.md +58 -2
- package/skills/ae-analysis/references/ai_models/rank_list.md +1 -0
- package/skills/ae-analysis/references/ai_models/retention.md +2 -0
- package/skills/ae-analysis/references/ai_models/revenue.md +2 -0
- package/skills/ae-analysis/references/analysis_data_export.md +11 -1
- package/skills/ae-analysis/references/asset_batch_info_export.md +2 -2
- package/skills/ae-analysis/references/asset_batch_sql_export.md +2 -2
- package/skills/ae-analysis/references/asset_export.md +1 -1
- package/skills/ae-analysis/references/bi_panel_page_data_export.md +1 -1
- package/skills/ae-analysis/references/check_export.md +1 -1
- package/skills/ae-analysis/references/command_index.md +39 -26
- package/skills/ae-analysis/references/dashboard_report_data_export.md +2 -0
- package/skills/ae-analysis/references/dashboard_report_get.md +6 -2
- package/skills/ae-analysis/references/dashboard_report_update.md +24 -5
- package/skills/ae-analysis/references/feedback_submit.md +1 -1
- package/skills/ae-analysis/references/governance_recommendation_export.md +40 -18
- package/skills/ae-analysis/references/governance_recommendation_run_update.md +13 -0
- package/skills/ae-analysis/references/live_data_export.md +1 -1
- package/skills/ae-analysis/references/metric_create.md +29 -0
- package/skills/ae-analysis/references/metric_get.md +2 -0
- package/skills/ae-analysis/references/metric_update.md +2 -0
- package/skills/ae-analysis/references/operation_record_export.md +1 -1
- package/skills/ae-analysis/references/plan_change_log_export.md +1 -1
- package/skills/ae-analysis/references/plan_export.md +1 -1
- package/skills/ae-analysis/references/project_info_get.md +4 -0
- package/skills/ae-analysis/references/project_member_group_add_members.md +35 -0
- package/skills/ae-analysis/references/project_member_group_create.md +25 -0
- package/skills/ae-analysis/references/project_member_group_delete.md +34 -0
- package/skills/ae-analysis/references/project_member_group_list.md +29 -0
- package/skills/ae-analysis/references/project_member_group_members.md +28 -0
- package/skills/ae-analysis/references/project_member_group_remove_members.md +35 -0
- package/skills/ae-analysis/references/project_member_group_rename.md +26 -0
- package/skills/ae-analysis/references/project_semantic_knowledge_wiki.md +9 -1
- package/skills/ae-analysis/references/project_semantic_knowledge_wiki_plan_schema.md +6 -1
- package/skills/ae-analysis/references/report_data_export.md +1 -1
- package/skills/ae-analysis/references/scenario_append.md +31 -0
- package/skills/ae-analysis/references/scenario_begin.md +11 -0
- package/skills/ae-analysis/references/scenario_collect.md +43 -0
- package/skills/ae-analysis/references/scenario_evaluation.md +18 -0
- package/skills/ae-analysis/references/scenario_get.md +15 -0
- package/skills/ae-analysis/references/scenario_list.md +64 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-project-semantic-wiki.mjs +73 -4
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/company-kb.mjs +14 -1
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-schema.md +5 -1
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/execution-contract.mjs +173 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/generate-build-ir.mjs +109 -64
- package/skills/ae-automation/SKILL.md +2 -2
- package/skills/ae-automation/references/cli.md +18 -1
- package/skills/ae-capability/SKILL.md +7 -3
- package/skills/ae-community/SKILL.md +77 -27
- package/skills/ae-community/references/community-analyzing-theme-comment.md +10 -8
- package/skills/ae-community/references/community-data-quality.md +68 -0
- package/skills/ae-community/references/community-query-evidence.md +85 -0
- package/skills/ae-community/references/community-scenario-capabilities.md +137 -0
- package/skills/ae-community/references/community-weekly-report.md +6 -3
- package/skills/ae-community/references/get_channel_info.md +7 -4
- package/skills/ae-community/references/get_comment_tag_analysis.md +3 -3
- package/skills/ae-community/references/get_comments_summary.md +4 -4
- package/skills/ae-community/references/get_corpus_tags.md +3 -3
- package/skills/ae-community/references/get_daily_summary.md +3 -3
- package/skills/ae-community/references/get_hot_topics.md +3 -3
- package/skills/ae-community/references/get_livestream_analysis.md +3 -3
- package/skills/ae-community/references/get_livestream_detail.md +4 -4
- package/skills/ae-community/references/get_livestream_list.md +3 -3
- package/skills/ae-community/references/get_livestream_overview.md +3 -3
- package/skills/ae-community/references/get_livestream_room_metrics.md +3 -3
- package/skills/ae-community/references/get_livestream_rooms.md +3 -3
- package/skills/ae-community/references/get_overview_metrics.md +3 -3
- package/skills/ae-community/references/get_post_detail.md +4 -4
- package/skills/ae-community/references/get_risk_content.md +5 -5
- package/skills/ae-community/references/get_sentiment_overview.md +4 -4
- package/skills/ae-community/references/get_tag_trends.md +3 -3
- package/skills/ae-community/references/get_topic_detail.md +3 -3
- package/skills/ae-community/references/list_sources.md +28 -0
- package/skills/ae-community/references/list_spaces.md +39 -0
- package/skills/ae-community/references/search_posts.md +21 -7
- package/skills/ae-data-integration/SKILL.md +6 -4
- package/skills/ae-data-integration/references/error-handling.md +1 -0
- package/skills/ae-data-integration/references/handoff.md +1 -1
- package/skills/ae-data-integration/references/sink-upload.md +11 -0
- package/skills/ae-data-integration/references/source-inspect.md +2 -1
- package/skills/ae-data-integration/references/transform.md +1 -1
- package/skills/ae-data-integration/references/ue-mapping.md +1 -1
- package/skills/ae-evaluation/SKILL.md +83 -0
- package/skills/ae-evaluation/references/access-and-members.md +55 -0
- package/skills/ae-evaluation/references/connections.md +78 -0
- package/skills/ae-evaluation/references/datasets.md +57 -0
- package/skills/ae-evaluation/references/evaluators.md +81 -0
- package/skills/ae-evaluation/references/files.md +33 -0
- package/skills/ae-evaluation/references/integrations.md +39 -0
- package/skills/ae-evaluation/references/runs.md +160 -0
- package/skills/ae-evaluation/references/schedules.md +31 -0
- package/skills/ae-experiment/SKILL.md +7 -1
- package/skills/ae-experiment/references/manage_experiment.md +18 -1
- package/skills/ae-experiment/references/query_experiment_detail.md +7 -0
- package/skills/ae-experiment/references/save_experiment.md +76 -2
- package/skills/ae-experiment/references/save_submit_experiment.md +7 -0
- package/skills/ae-kb/SKILL.md +33 -7
- package/skills/ae-kb/references/publication.md +81 -0
- package/skills/ae-kb/references/query-workflow.md +14 -1
- package/skills/ae-metadata/SKILL.md +3 -2
- package/skills/ae-metadata/references/metadata_data_table_download.md +7 -1
- package/skills/ae-metadata/references/metadata_data_table_get.md +37 -0
- package/skills/ae-system/SKILL.md +1 -1
- package/skills/ae-team/SKILL.md +2 -2
- package/skills/ae-team/references/run-start.md +1 -0
- package/skills/ae-team/references/run-watch.md +7 -5
- package/skills/ae-things/SKILL.md +53 -51
- package/skills/ae-things/references/cli.md +56 -54
- package/skills/ae-things-progress/SKILL.md +20 -20
- package/skills/ae-things-review/SKILL.md +19 -19
- package/skills/ae-workspace/SKILL.md +1 -1
- package/dist/chunk-6VNIR4AP.js +0 -1422
- package/dist/chunk-CI24BDR2.js +0 -43
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Structural facts only. Business meanings and asset routes remain Agent-authored.
|
|
2
|
+
const obj = value => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
3
|
+
const list = value => Array.isArray(value) ? value : [];
|
|
4
|
+
const valueOf = value => {
|
|
5
|
+
if (typeof value !== 'string') return value;
|
|
6
|
+
try { return JSON.parse(value); } catch { return value; }
|
|
7
|
+
};
|
|
8
|
+
const present = value => value !== undefined && value !== null && value !== '';
|
|
9
|
+
const pick = (source, keys) => Object.fromEntries(keys.filter(key => present(source[key])).map(key => [key, source[key]]));
|
|
10
|
+
const displayKeys = new Set(['display_name', 'eventNameDisplay', 'eventDesc', 'analysisDesc', 'quotaDesc', 'columnDesc', 'eventUuid', 'uuid']);
|
|
11
|
+
|
|
12
|
+
export function semanticValue(value) {
|
|
13
|
+
if (Array.isArray(value)) return value.map(semanticValue);
|
|
14
|
+
if (!value || typeof value !== 'object') return value;
|
|
15
|
+
return Object.fromEntries(Object.keys(value).filter(key => !displayKeys.has(key)).sort()
|
|
16
|
+
.map(key => [key, semanticValue(value[key])]));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function semanticTime(value) {
|
|
20
|
+
const time = { ...obj(value) };
|
|
21
|
+
if (present(time.recentDay ?? time.recent_day)) {
|
|
22
|
+
for (const key of ['startTime', 'endTime', 'start_time', 'end_time']) delete time[key];
|
|
23
|
+
}
|
|
24
|
+
return time;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function logicalRelation(value) {
|
|
28
|
+
const relation = String(value ?? '').toLowerCase();
|
|
29
|
+
return ({ '1': 'AND', '0': 'OR', and: 'AND', or: 'OR' })[relation] || `UNKNOWN(${relation || 'unspecified'})`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function conditionTree(value, relation) {
|
|
33
|
+
if (Array.isArray(value)) return { relation: logicalRelation(relation), items: value.map(item => conditionTree(item)) };
|
|
34
|
+
const source = obj(value);
|
|
35
|
+
const nested = source.filts ?? source.items ?? source.filters;
|
|
36
|
+
if (nested != null) {
|
|
37
|
+
if (!Array.isArray(nested)) return { unknown: true, raw: source };
|
|
38
|
+
return { relation: logicalRelation(source.relation ?? source.junctionKind ?? relation), items: nested.map(item => conditionTree(item)) };
|
|
39
|
+
}
|
|
40
|
+
if (!Object.keys(source).length) return { unknown: true };
|
|
41
|
+
return { predicate: source };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function renderConditionTree(value, leaf) {
|
|
45
|
+
if (value.predicate) return leaf(value.predicate);
|
|
46
|
+
if (value.unknown) return 'Unknown predicate; verify the source definition';
|
|
47
|
+
if (!value.items.length) return 'No conditions in this scope';
|
|
48
|
+
if (value.items.length === 1) return renderConditionTree(value.items[0], leaf);
|
|
49
|
+
return `(${value.items.map(item => renderConditionTree(item, leaf)).join(` ${value.relation} `)})`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function metricContract(item) {
|
|
53
|
+
const formula = obj(item.formula_definition ?? item.formula);
|
|
54
|
+
return {
|
|
55
|
+
...pick(item, ['event_name', 'aggregation_code', 'measure_key', 'formula_expression', 'entity_grain', 'aggregation_parameters', 'time_scope']),
|
|
56
|
+
filters: item.filters == null ? null : conditionTree(item.filters, item.relation),
|
|
57
|
+
operand_filters: list(item.custom_filters).map(filter => ({ index: filter.index, tree: conditionTree(filter, filter.relation) })),
|
|
58
|
+
operands: list(formula.formulationDeps).map((dependency, index) => ({ index,
|
|
59
|
+
...pick(obj(dependency), ['event', 'quota', 'property', 'date', 'timeRange', 'time_scope', 'quotaTimeRange']),
|
|
60
|
+
})),
|
|
61
|
+
operand_time_ranges: item.quota_time_ranges ?? item.quotaTimeRanges ?? null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const modelConfigKeys = [
|
|
66
|
+
'windowsGapValue', 'windowsGapUnit', 'percentConfig', 'userCrowds', 'retentionType',
|
|
67
|
+
'rtnRateOrNum', 'statType', 'retentionCollectSet', 'relationProps', 'relationPropSplit',
|
|
68
|
+
'unitNum', 'eventModel', 'taIdMeasureVo',
|
|
69
|
+
];
|
|
70
|
+
const timeKeys = ['recentDay', 'startTime', 'endTime', 'startToNow', 'startToYesterday',
|
|
71
|
+
'timeParticleSize', 'firstDayOfWeek', 'zoneOffset', 'timezone', 'completeIncompletePeriod', 'hideIncompleteData'];
|
|
72
|
+
const interpretedViewKeys = new Set([...timeKeys, ...modelConfigKeys, 'filts', 'groupBy', 'group_by',
|
|
73
|
+
'relation', 'sqlViewParams', 'graphShape', 'projectId', 'displayQuotas', 'displayGroups']);
|
|
74
|
+
|
|
75
|
+
export function reportContract(report, context = report.calculation_context || {}) {
|
|
76
|
+
const view = obj(valueOf(context.event_view));
|
|
77
|
+
const savedParameters = context.saved_parameters ?? view.sqlViewParams ?? null;
|
|
78
|
+
const parameters = list(context.sql_params ?? context.parameters).map(parameter => ({ ...parameter,
|
|
79
|
+
name: parameter.name ?? parameter.paramName,
|
|
80
|
+
}));
|
|
81
|
+
return {
|
|
82
|
+
model: report.report_model,
|
|
83
|
+
groups: context.group_by ?? view.groupBy ?? view.group_by ?? [],
|
|
84
|
+
time: semanticTime({ ...pick(view, timeKeys), ...obj(context.time_scope) }),
|
|
85
|
+
filters: conditionTree(context.filters ?? view.filts ?? [], context.relation ?? view.relation),
|
|
86
|
+
metrics: list(report.metric_occurrences).map(metricContract),
|
|
87
|
+
model_config: { ...pick(view, modelConfigKeys), ...obj(context.model_config) },
|
|
88
|
+
unparsed_model_config: Object.fromEntries(Object.entries(view).filter(([key]) => !interpretedViewKeys.has(key))),
|
|
89
|
+
model_events: list(valueOf(context.source_events)).map(event => pick(obj(event), ['eventName', 'type', 'filts', 'relation', 'analysis', 'analysisParams', 'quota', 'quotaEntities'])),
|
|
90
|
+
parameters,
|
|
91
|
+
saved_parameters: Array.isArray(savedParameters) ? savedParameters.map(parameter => Object.keys(obj(parameter)).length ? semanticTime(parameter) : parameter) : savedParameters,
|
|
92
|
+
sql_projections: report.definition_kind === 'SQL' ? list(obj(context.sql_semantic_facts).final_select?.fields) : [],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function reportSignature(report, context = report.calculation_context || {}, includeSavedSettings = true) {
|
|
97
|
+
const contract = reportContract(report, context);
|
|
98
|
+
contract.time = semanticTime(contract.time);
|
|
99
|
+
if (!includeSavedSettings) contract.unparsed_model_config = {};
|
|
100
|
+
if (report.definition_kind === 'SQL') {
|
|
101
|
+
const definitions = contract.parameters.map(parameter => pick(parameter, ['name', 'paramType', 'type', 'options', 'selectorItems', 'required', 'useTimezone', 'use_timezone']));
|
|
102
|
+
return JSON.stringify(semanticValue({ sql: String(context.sql || '').replace(/\r\n?/g, '\n').trim(),
|
|
103
|
+
parameters: definitions, ...(includeSavedSettings ? { saved_parameters: contract.saved_parameters, time: contract.time } : {}) }));
|
|
104
|
+
}
|
|
105
|
+
return JSON.stringify(semanticValue(contract));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function enrichMetricEvidence(metrics, context) {
|
|
109
|
+
const source = list(valueOf(context.source_events));
|
|
110
|
+
return metrics.map((metric, index) => {
|
|
111
|
+
const raw = obj(source[index]);
|
|
112
|
+
if (source.length !== metrics.length || raw.eventName !== metric.event_name || String(raw.analysis ?? '') !== String(metric.aggregation_code ?? '')) return metric;
|
|
113
|
+
return { ...metric,
|
|
114
|
+
...(raw.analysisParams != null ? { aggregation_parameters: raw.analysisParams } : {}),
|
|
115
|
+
...(raw.quotaEntities != null ? { entity_grain: raw.quotaEntities } : {}),
|
|
116
|
+
...(raw.quotaTimeRanges != null ? { quota_time_ranges: raw.quotaTimeRanges } : {}),
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const inline = value => `\`${JSON.stringify(value).replaceAll('`', '\\`').replaceAll('\n', ' ')}\``;
|
|
122
|
+
|
|
123
|
+
export function renderExecutionContract(contract) {
|
|
124
|
+
const rows = [
|
|
125
|
+
'- These are saved definition settings. Execution overrides and inherited dashboard settings require live verification.',
|
|
126
|
+
'- Saved result grouping is listed below. SQL output grain and inherited settings require the asset explanation; referenced metadata is not a saved group.',
|
|
127
|
+
...contract.groups.map((group, index) => `- Group ${index + 1}: ${inline(group)}`),
|
|
128
|
+
`- Saved population filter structure: ${inline(contract.filters)}`,
|
|
129
|
+
`- Saved time settings: ${Object.keys(contract.time).length ? inline(contract.time) : 'unknown; verify the current definition'}`,
|
|
130
|
+
'- State timing: a historical behavior window does not establish historical values for user or dimension tables. Source refresh times and historical reconstruction require source evidence.',
|
|
131
|
+
'- Empty/missing rows, null, empty arrays, false and zero are distinct. Do not fill missing rows with zero without coverage evidence.',
|
|
132
|
+
];
|
|
133
|
+
if (contract.groups.some(group => /array/i.test(JSON.stringify(group)))) rows.push('- Array grouping can expand an entity into several rows. Distinct counts, ratios and percentiles are not additive across those rows.');
|
|
134
|
+
if (Object.keys(contract.model_config).length) rows.push(`- Model-specific settings: ${inline(contract.model_config)}`);
|
|
135
|
+
if (Object.keys(contract.unparsed_model_config).length) rows.push(`- Uninterpreted saved configuration; semantic support is unknown: ${inline(contract.unparsed_model_config)}`);
|
|
136
|
+
if ((contract.model_config.windowsGapValue != null || list(contract.model_config.userCrowds).length) && contract.model_events.length) rows.push(`- Model event roles and measures: ${inline(contract.model_events)}`);
|
|
137
|
+
if (contract.parameters.length) rows.push(`- Parameter definitions: ${inline(contract.parameters)}`);
|
|
138
|
+
if (contract.saved_parameters != null) rows.push(`- Saved parameter choices (separate from definitions and call-time overrides): ${inline(contract.saved_parameters)}`);
|
|
139
|
+
if (contract.sql_projections.length) {
|
|
140
|
+
const projections = contract.sql_projections.filter(field => !/\$\{/.test(field.expression || ''));
|
|
141
|
+
rows.push(`- SQL outer projection evidence: ${inline(projections)}`);
|
|
142
|
+
rows.push('- SQL lineage completeness: unknown for nested scopes, CTEs, wildcard fields and dynamic expressions. Output names alone do not prove source meanings; verify unresolved columns.');
|
|
143
|
+
}
|
|
144
|
+
for (const [index, metric] of contract.metrics.entries()) {
|
|
145
|
+
if (metric.operands.length || metric.operand_filters.length || metric.aggregation_parameters != null || metric.entity_grain != null) {
|
|
146
|
+
rows.push(`- Measure ${index + 1} calculation scopes: ${inline(metric)}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return `\n\n## Execution contract\n\n${rows.join('\n')}\n`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function validateSqlExplanations(summary, sql) {
|
|
153
|
+
const normalize = value => String(value).replace(/\s+/g, ' ').trim();
|
|
154
|
+
for (const key of ['calculation_rules', 'time_semantics', 'coverage_gaps']) {
|
|
155
|
+
if (summary[key] == null) continue;
|
|
156
|
+
if (!Array.isArray(summary[key])) throw new Error(`${key} must be an array.`);
|
|
157
|
+
for (const item of summary[key]) {
|
|
158
|
+
if (!item?.meaning || !item?.evidence || !normalize(sql).includes(normalize(item.evidence))) {
|
|
159
|
+
throw new Error(`${key} requires a meaning and exact SQL evidence from the current definition.`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const field of list(summary.output_fields)) {
|
|
164
|
+
if (field.evidence != null && !normalize(sql).includes(normalize(field.evidence))) {
|
|
165
|
+
throw new Error('Output field lineage evidence must occur in the current SQL.');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function renderSqlExplanations(summary) {
|
|
171
|
+
return ['calculation_rules', 'time_semantics', 'coverage_gaps'].flatMap(key => list(summary?.[key])
|
|
172
|
+
.map(item => `- ${key}: ${item.meaning} (evidence: ${inline(item.evidence)})`)).join('\n');
|
|
173
|
+
}
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
import { createHash, randomBytes } from 'node:crypto';
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
+
import { conditionTree, renderConditionTree, reportContract, reportSignature,
|
|
7
|
+
semanticValue, enrichMetricEvidence, renderExecutionContract, validateSqlExplanations,
|
|
8
|
+
renderSqlExplanations } from './execution-contract.mjs';
|
|
6
9
|
|
|
7
10
|
const args = parseArgs(process.argv.slice(2));
|
|
8
11
|
const packageRoot = path.resolve(requiredArg(args, 'asset-package'));
|
|
@@ -99,13 +102,19 @@ const sqlReportSemanticsById = new Map(array(domainPlan.sql_report_semantics)
|
|
|
99
102
|
.map((value) => object(value))
|
|
100
103
|
.map((value) => [text(value.report_id), value])
|
|
101
104
|
.filter(([id]) => id));
|
|
105
|
+
for (const report of reports) {
|
|
106
|
+
report.calculation_context = calculationContextForKb(report);
|
|
107
|
+
report.metric_occurrences = enrichMetricEvidence(array(report.metric_occurrences), report.calculation_context);
|
|
108
|
+
}
|
|
102
109
|
const semanticConflicts = detectSemanticConflicts();
|
|
103
110
|
const reportConflicts = semanticConflicts.reportConflicts;
|
|
111
|
+
const reportSettingVariants = semanticConflicts.executionVariants;
|
|
104
112
|
const metadataSemanticConflicts = semanticConflicts.metadataConflicts;
|
|
105
113
|
const metadataMissingTitles = metadata.filter((item) => !text(item.title));
|
|
106
114
|
const reportsInBusinessDomains = reports.filter((report) => domainsForReport(report).length);
|
|
107
115
|
const reportsInTechnicalAppendices = reports.filter((report) => appendicesForReport(report).length);
|
|
108
116
|
const warnings = [
|
|
117
|
+
...[...reportSettingVariants.keys()].map(id => `Report ${id} has matching calculation evidence but different saved or uninterpreted settings; verify explicit overrides before reuse.`),
|
|
109
118
|
...(text(descriptor.asset_scope ?? manifest.asset_scope) !== 'governed'
|
|
110
119
|
? [`The package scope is ${text(descriptor.asset_scope ?? manifest.asset_scope) || 'unknown'}; unauthenticated assets may be present.`]
|
|
111
120
|
: []),
|
|
@@ -533,6 +542,9 @@ function validateSqlSemanticPlan(plan) {
|
|
|
533
542
|
const summary = object(summaryValue);
|
|
534
543
|
const reportId = text(summary.report_id);
|
|
535
544
|
if (!sqlReportsById.has(reportId)) continue;
|
|
545
|
+
try {
|
|
546
|
+
validateSqlExplanations(summary, sqlTextFromCalculationContext(calculationContextForKb(sqlReportsById.get(reportId))));
|
|
547
|
+
} catch (error) { fail(`SQL report ${reportId}: ${error.message}`); }
|
|
536
548
|
if (text(summary.definition_state) !== 'valid') continue;
|
|
537
549
|
const issues = sqlSemanticQualityIssues(summary);
|
|
538
550
|
if (issues.length) {
|
|
@@ -1036,13 +1048,29 @@ function renderDashboardAgentUsageSummary(dashboard, reportRows, notes, domain,
|
|
|
1036
1048
|
}
|
|
1037
1049
|
|
|
1038
1050
|
function renderReportAgentUsageSummary(report, metadataRefs, calculationContext, sqlSummary) {
|
|
1051
|
+
const explanation = renderSqlExplanations(sqlSummary);
|
|
1052
|
+
const coverage = text(report.definition_kind) === 'SQL' && !array(sqlSummary?.calculation_rules).length
|
|
1053
|
+
? '\n\n- Calculation coverage: unknown for this legacy summary. Verify the current SQL for operand filters, denominator, units, rounding, time baselines and excluded rows before execution.' : '';
|
|
1054
|
+
return renderReportAuthoredSummary(report, metadataRefs, calculationContext, sqlSummary)
|
|
1055
|
+
+ (explanation ? `\n\n### Calculation evidence\n\n${explanation}` : '')
|
|
1056
|
+
+ coverage
|
|
1057
|
+
+ renderExecutionContract(reportContract(report, calculationContext))
|
|
1058
|
+
+ (reportSettingVariants.has(text(report.resource_key)) ? '\n- Saved-setting variant: matching calculation evidence does not establish identical defaults. Verify or explicitly override the required settings before reuse.\n' : '');
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
function renderReportAuthoredSummary(report, metadataRefs, calculationContext, sqlSummary) {
|
|
1039
1062
|
const isSql = text(report.definition_kind) === 'SQL';
|
|
1040
1063
|
if (isSql) return renderSqlReportAgentUsageSummary(report, sqlSummary);
|
|
1064
|
+
const modelConfig = reportContract(report, calculationContext).model_config;
|
|
1065
|
+
if (array(modelConfig.userCrowds).length || modelConfig.windowsGapValue != null) {
|
|
1066
|
+
const model = array(modelConfig.userCrowds).length ? 'Saved population analysis' : 'Saved interval analysis';
|
|
1067
|
+
return `- Model: ${model}.\n- Purpose: ${text(report.description || report.summary) || 'Verify the business question against the saved model settings below.'}\n- Result semantics: use the complete population conditions or ordered event roles, window and selected statistics in the execution contract.\n- Missing configuration, entity scope and historical state remain unknown; do not infer ordinary event metrics from model endpoints.`;
|
|
1068
|
+
}
|
|
1041
1069
|
const purpose = usefulSemanticText(sqlSummary?.business_purpose) || reportPurpose(report);
|
|
1042
1070
|
const questions = usefulSemanticList(sqlSummary?.applicable_questions)
|
|
1043
1071
|
? array(sqlSummary.applicable_questions).map(renderPlainSemanticItem)
|
|
1044
1072
|
: reportApplicableQuestions(report);
|
|
1045
|
-
const metrics =
|
|
1073
|
+
const metrics = array(report.metric_occurrences).map(occurrenceSentence);
|
|
1046
1074
|
const parameters = reportParameterSummaryItems(calculationContext);
|
|
1047
1075
|
const dimensions = reportDimensionSummaryItems(report, sqlSummary, calculationContext, metadataRefs);
|
|
1048
1076
|
const filters = reportFilterSummaryItems(report, calculationContext);
|
|
@@ -1083,18 +1111,29 @@ function renderSqlReportAgentUsageSummary(report, summary) {
|
|
|
1083
1111
|
}
|
|
1084
1112
|
|
|
1085
1113
|
function renderSqlSemanticItem(value, summary) {
|
|
1114
|
+
const item = object(value);
|
|
1115
|
+
if (Object.keys(item).length && item.required == null && item.default_value == null && !Array.isArray(item.options)) {
|
|
1116
|
+
return [text(item.name) ? `\`${text(item.name)}\`` : '', sanitizeVisibleSqlSemanticText(item.meaning)].filter(Boolean).join(':')
|
|
1117
|
+
+ (item.evidence ? ` [source: ${jsonInline(item.evidence)}]` : '')
|
|
1118
|
+
+ (item.identity_state === 'unknown' ? ' [output identity unverified; match the current SQL projection before use]' : '');
|
|
1119
|
+
}
|
|
1086
1120
|
return replaceSqlParameterMentions(renderSemanticItem(value), summary);
|
|
1087
1121
|
}
|
|
1088
1122
|
|
|
1089
1123
|
function renderSqlInputParameterItem(value) {
|
|
1090
|
-
return sanitizeVisibleSqlSemanticText(
|
|
1124
|
+
if (!value || typeof value !== 'object') return sanitizeVisibleSqlSemanticText(value);
|
|
1125
|
+
return renderSemanticItem({ ...value, meaning: sanitizeVisibleSqlSemanticText(value.meaning) });
|
|
1091
1126
|
}
|
|
1092
1127
|
|
|
1093
1128
|
function replaceSqlParameterMentions(value, summary) {
|
|
1094
|
-
let result =
|
|
1129
|
+
let result = text(value);
|
|
1095
1130
|
for (const [name, meaning] of sqlParameterMeaningEntries(summary)) {
|
|
1096
|
-
const pattern = new RegExp(
|
|
1131
|
+
const pattern = new RegExp(`\\$\\{(?:[A-Za-z]+:)?${escapeRegExp(name)}\\}`, 'gu');
|
|
1097
1132
|
result = result.replace(pattern, meaning);
|
|
1133
|
+
if (/^(?:selector|variable|text|number|date)\d+$/i.test(name)) {
|
|
1134
|
+
const generatedReference = new RegExp(`(?<![A-Za-z0-9_#$\x60])${escapeRegExp(name)}(?![A-Za-z0-9_#$\x60])`, 'gu');
|
|
1135
|
+
result = result.replace(generatedReference, meaning);
|
|
1136
|
+
}
|
|
1098
1137
|
}
|
|
1099
1138
|
return sanitizeVisibleSqlSemanticText(result)
|
|
1100
1139
|
.replace(/\bLIMIT\s+([^,;。]+?)限制/g, '按$1限制')
|
|
@@ -1135,11 +1174,6 @@ function genericSqlParameterMeaning(name) {
|
|
|
1135
1174
|
return normalized;
|
|
1136
1175
|
}
|
|
1137
1176
|
|
|
1138
|
-
function isGeneratedSqlParameterName(name) {
|
|
1139
|
-
return /^(?:selector|variable|text|number|partdate)\d*$/i.test(text(name))
|
|
1140
|
-
|| /^date\d+$/i.test(text(name));
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
1177
|
function escapeRegExp(value) {
|
|
1144
1178
|
return text(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1145
1179
|
}
|
|
@@ -1161,7 +1195,7 @@ function renderPlainSemanticItem(value) {
|
|
|
1161
1195
|
if (item.default_value != null && text(item.default_value)) extras.push(`default=${text(item.default_value)}`);
|
|
1162
1196
|
const expressionKind = text(item.expression_kind);
|
|
1163
1197
|
if (expressionKind) extras.push(expressionKind);
|
|
1164
|
-
if (isInputParameterSemanticItem(item) && meaning && name
|
|
1198
|
+
if (isInputParameterSemanticItem(item) && meaning && name) {
|
|
1165
1199
|
return [`${meaning}(\`${name}\`)`, extras.join(',')].filter(Boolean).join(':');
|
|
1166
1200
|
}
|
|
1167
1201
|
if (isInputParameterSemanticItem(item) && meaning) {
|
|
@@ -1190,6 +1224,12 @@ function reportDimensionSummaryItems(report, summary = null, calculationContext
|
|
|
1190
1224
|
const context = calculationContext || calculationContextForKb(report);
|
|
1191
1225
|
const facts = object(context.sql_semantic_facts);
|
|
1192
1226
|
const refs = metadataRefs || metadataRefsForReport(report);
|
|
1227
|
+
if (text(report.definition_kind) !== 'SQL') {
|
|
1228
|
+
return reportContract(report, context).groups.map(group => {
|
|
1229
|
+
const row = object(group);
|
|
1230
|
+
return namedCodeFact(row.columnDesc || row.columnName || text(group), row.columnName || text(group));
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1193
1233
|
const candidates = [
|
|
1194
1234
|
...array(summary?.output_fields)
|
|
1195
1235
|
.filter((item) => text(object(item).semantic_role) === 'dimension')
|
|
@@ -1207,14 +1247,18 @@ function reportDimensionSummaryItems(report, summary = null, calculationContext
|
|
|
1207
1247
|
function reportParameterSummaryItems(calculationContext) {
|
|
1208
1248
|
const context = object(calculationContext);
|
|
1209
1249
|
return [
|
|
1250
|
+
...(Object.keys(object(context.time_scope)).length ? [`Saved time settings: ${JSON.stringify(context.time_scope)}`] : []),
|
|
1210
1251
|
...array(context.sql_params).map((item) => renderPlainSemanticItem({ name: object(item).name || object(item).paramName, meaning: object(item).displayName || object(item).placeholder })),
|
|
1211
1252
|
].filter(Boolean).slice(0, 12);
|
|
1212
1253
|
}
|
|
1213
1254
|
|
|
1214
1255
|
function reportFilterSummaryItems(report, calculationContext) {
|
|
1215
1256
|
const items = [
|
|
1216
|
-
...array(report.metric_occurrences).flatMap((item) =>
|
|
1217
|
-
|
|
1257
|
+
...array(report.metric_occurrences).flatMap((item) => [
|
|
1258
|
+
...filterSummaryLines(item.filters, item.relation),
|
|
1259
|
+
...array(item.custom_filters).flatMap((filter) => filterSummaryLines(filter).map(line => `Operand ${filter.index}: ${line}`)),
|
|
1260
|
+
]),
|
|
1261
|
+
...filterSummaryLines(object(calculationContext).filters, object(calculationContext).relation),
|
|
1218
1262
|
...array(object(calculationContext).event_view?.filts).flatMap((item) => filterSummaryLines(item)),
|
|
1219
1263
|
...array(object(calculationContext).visual_view?.filters).flatMap((item) => filterSummaryLines(item)),
|
|
1220
1264
|
...array(object(calculationContext).key_predicate_fragments).map(compactSqlFragment),
|
|
@@ -1224,7 +1268,17 @@ function reportFilterSummaryItems(report, calculationContext) {
|
|
|
1224
1268
|
|
|
1225
1269
|
function completeSqlAgentSummary(report, planSummary, facts) {
|
|
1226
1270
|
const base = object(planSummary);
|
|
1227
|
-
if (Object.keys(base).length)
|
|
1271
|
+
if (Object.keys(base).length) {
|
|
1272
|
+
const names = new Set(array(object(facts.final_select).fields).map(field => {
|
|
1273
|
+
if (field.name) return field.name;
|
|
1274
|
+
const expression = text(field.expression);
|
|
1275
|
+
return /^(?:[A-Za-z_][\w$#@.]*|"[^"]+")$/.test(expression) ? unquoteIdentifier(expression) : null;
|
|
1276
|
+
}).filter(Boolean));
|
|
1277
|
+
return { summary_source: 'semantic_plan', ...base,
|
|
1278
|
+
output_fields: array(base.output_fields).map(field => typeof field === 'object' && !names.has(field.name)
|
|
1279
|
+
? { ...field, identity_state: 'unknown' } : field),
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1228
1282
|
return null;
|
|
1229
1283
|
}
|
|
1230
1284
|
|
|
@@ -1380,7 +1434,7 @@ function renderSemanticItem(value) {
|
|
|
1380
1434
|
.filter(([key]) => !['name', 'field', 'key', 'column', 'title', 'meaning', 'description', 'remark', 'purpose'].includes(key))
|
|
1381
1435
|
.map(([key, itemValue]) => `${key}=${inlineSemanticValue(itemValue)}`)
|
|
1382
1436
|
.join(', ');
|
|
1383
|
-
if (isInputParameterSemanticItem(item) && meaning && name
|
|
1437
|
+
if (isInputParameterSemanticItem(item) && meaning && name) {
|
|
1384
1438
|
return [`${meaning}(\`${name}\`)`, details].filter(Boolean).join(':') || jsonInline(item);
|
|
1385
1439
|
}
|
|
1386
1440
|
if (isInputParameterSemanticItem(item) && meaning) {
|
|
@@ -1391,12 +1445,9 @@ function renderSemanticItem(value) {
|
|
|
1391
1445
|
|
|
1392
1446
|
function isInputParameterSemanticItem(item) {
|
|
1393
1447
|
const row = object(item);
|
|
1394
|
-
const name = text(row.name ?? row.field ?? row.key ?? row.column ?? row.title);
|
|
1395
|
-
const meaning = text(row.meaning ?? row.description ?? row.remark ?? row.purpose);
|
|
1396
1448
|
return row.required != null
|
|
1397
1449
|
|| row.default_value != null
|
|
1398
|
-
|| Array.isArray(row.options)
|
|
1399
|
-
|| (Boolean(meaning) && /^(?:selector|variable|date|partdate|text|number)\d*$/i.test(name));
|
|
1450
|
+
|| Array.isArray(row.options);
|
|
1400
1451
|
}
|
|
1401
1452
|
|
|
1402
1453
|
function inlineSemanticValue(value) {
|
|
@@ -1434,6 +1485,11 @@ function reportDetailCalculationContext(report) {
|
|
|
1434
1485
|
if (Object.keys(eventView).length) result.event_view = eventView;
|
|
1435
1486
|
const events = jsonObject(rawDefinition.events);
|
|
1436
1487
|
if (array(events.sqlVoParams).length) result.sql_params = events.sqlVoParams;
|
|
1488
|
+
if (rawDefinition.events != null) {
|
|
1489
|
+
try { result.source_events = typeof rawDefinition.events === 'string' ? JSON.parse(rawDefinition.events) : rawDefinition.events; }
|
|
1490
|
+
catch { result.source_events = null; }
|
|
1491
|
+
}
|
|
1492
|
+
if (eventView.sqlViewParams != null) result.saved_parameters = eventView.sqlViewParams;
|
|
1437
1493
|
return result;
|
|
1438
1494
|
}
|
|
1439
1495
|
|
|
@@ -1857,6 +1913,9 @@ function metadataDefinitionLines(item) {
|
|
|
1857
1913
|
if (text(definition.entity_mapping_scope)) lines.push(`实体映射范围: ${text(definition.entity_mapping_scope)}`);
|
|
1858
1914
|
const sourceVersion = text(object(item.raw).sourceVersion ?? object(item.raw).source_version);
|
|
1859
1915
|
if (sourceVersion) lines.push(`来源版本: ${sourceVersion}`);
|
|
1916
|
+
for (const key of ['rule', 'rules', 'expression', 'dependent_events', 'dependent_properties']) {
|
|
1917
|
+
if (definition[key] != null) lines.push(`Rule evidence (${key}): ${jsonInline(definition[key])}`);
|
|
1918
|
+
}
|
|
1860
1919
|
return lines;
|
|
1861
1920
|
}
|
|
1862
1921
|
|
|
@@ -2020,11 +2079,17 @@ function eventBasisLabel(value) {
|
|
|
2020
2079
|
|
|
2021
2080
|
function detectSemanticConflicts() {
|
|
2022
2081
|
const reportConflicts = new Map();
|
|
2082
|
+
const executionVariants = new Map();
|
|
2023
2083
|
const metadataConflicts = new Map();
|
|
2024
2084
|
for (const [key, values] of groupBySemanticKey(reports, reportSemanticKey)) {
|
|
2025
2085
|
if (values.length < 2) continue;
|
|
2026
|
-
const signatures = new Set(values.map(
|
|
2027
|
-
if (signatures.size <= 1)
|
|
2086
|
+
const signatures = new Set(values.map(report => reportSignature(report, report.calculation_context, false)));
|
|
2087
|
+
if (signatures.size <= 1) {
|
|
2088
|
+
if (new Set(values.map(reportSemanticSignature)).size > 1) {
|
|
2089
|
+
for (const report of values) executionVariants.set(text(report.resource_key), true);
|
|
2090
|
+
}
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
2028
2093
|
const summary = semanticConflictSummary('报表', key, values, (row) => reportSemanticSignatureLabel(row));
|
|
2029
2094
|
for (const report of values) appendConflict(reportConflicts, text(report.resource_key), summary);
|
|
2030
2095
|
}
|
|
@@ -2036,7 +2101,7 @@ function detectSemanticConflicts() {
|
|
|
2036
2101
|
const summary = semanticConflictSummary('元数据', key, values, (row) => metadataSemanticSignatureLabel(row));
|
|
2037
2102
|
for (const item of values) appendConflict(metadataConflicts, text(item.evidence_id), summary);
|
|
2038
2103
|
}
|
|
2039
|
-
return { reportConflicts, metadataConflicts };
|
|
2104
|
+
return { reportConflicts, metadataConflicts, executionVariants };
|
|
2040
2105
|
}
|
|
2041
2106
|
|
|
2042
2107
|
function groupBySemanticKey(values, keyFn) {
|
|
@@ -2105,25 +2170,7 @@ function normalizeSemanticIntent(value) {
|
|
|
2105
2170
|
}
|
|
2106
2171
|
|
|
2107
2172
|
function reportSemanticSignature(report) {
|
|
2108
|
-
|
|
2109
|
-
return JSON.stringify({ kind: 'SQL', sql: normalizeSql(text(object(report.calculation_context).sql)) });
|
|
2110
|
-
}
|
|
2111
|
-
return JSON.stringify({
|
|
2112
|
-
kind: text(report.definition_kind) || text(report.report_model),
|
|
2113
|
-
metrics: array(report.metric_occurrences).map(metricSemanticSignature).sort(),
|
|
2114
|
-
filters: filterSummaryLines(object(report.calculation_context).filters).sort(),
|
|
2115
|
-
groups: array(object(report.calculation_context).group_by).map(groupLabel).sort(),
|
|
2116
|
-
});
|
|
2117
|
-
}
|
|
2118
|
-
|
|
2119
|
-
function metricSemanticSignature(item) {
|
|
2120
|
-
return JSON.stringify({
|
|
2121
|
-
event_name: text(item.event_name),
|
|
2122
|
-
aggregation_code: text(item.aggregation_code),
|
|
2123
|
-
measure_key: text(item.measure_key),
|
|
2124
|
-
formula_expression: text(item.formula_expression),
|
|
2125
|
-
filters: filterSummaryLines(item.filters ?? item.custom_filters).sort(),
|
|
2126
|
-
});
|
|
2173
|
+
return reportSignature(report);
|
|
2127
2174
|
}
|
|
2128
2175
|
|
|
2129
2176
|
function reportSemanticSignatureLabel(report) {
|
|
@@ -2146,6 +2193,7 @@ function metadataSemanticSignature(item) {
|
|
|
2146
2193
|
select_type: text(definition.select_type),
|
|
2147
2194
|
source_type: text(definition.source_type),
|
|
2148
2195
|
tag_type: text(definition.tag_type || definition.tag_sub_type),
|
|
2196
|
+
rules: semanticValue(Object.fromEntries(['rule', 'rules', 'expression', 'dependent_events', 'dependent_properties'].filter(key => definition[key] != null).map(key => [key, definition[key]]))),
|
|
2149
2197
|
});
|
|
2150
2198
|
}
|
|
2151
2199
|
|
|
@@ -2173,7 +2221,10 @@ function collectFilterValues(value) {
|
|
|
2173
2221
|
function renderOccurrence(item, index) {
|
|
2174
2222
|
const expression = formulaExpression(item);
|
|
2175
2223
|
const formulaSummary = formulaDefinitionSummary(item);
|
|
2176
|
-
const filterSummary =
|
|
2224
|
+
const filterSummary = [
|
|
2225
|
+
...filterSummaryLines(item.filters, item.relation),
|
|
2226
|
+
...array(item.custom_filters).flatMap(filter => filterSummaryLines(filter).map(line => `Operand ${filter.index}: ${line}`)),
|
|
2227
|
+
];
|
|
2177
2228
|
const event = metricEventLabel(item) || 'unknown';
|
|
2178
2229
|
return `### ${index + 1}. ${heading(occurrenceLabel(item) || `Metric ${index + 1}`)}
|
|
2179
2230
|
|
|
@@ -2219,15 +2270,17 @@ function formulaDefinition(item) {
|
|
|
2219
2270
|
}
|
|
2220
2271
|
|
|
2221
2272
|
function formulaDependencies(item) {
|
|
2222
|
-
return array(formulaDefinition(item).formulationDeps).map((dependency) => {
|
|
2273
|
+
return array(formulaDefinition(item).formulationDeps).map((dependency, index) => {
|
|
2223
2274
|
const event = object(object(dependency).event);
|
|
2224
2275
|
const quota = object(object(dependency).quota);
|
|
2225
2276
|
const eventName = text(event.eventDesc || event.eventName) || '未知事件';
|
|
2226
2277
|
const aggregation = text(quota.quotaDesc || quota.quotaName) || '资产包未暴露聚合方式';
|
|
2227
|
-
const filters =
|
|
2228
|
-
|
|
2229
|
-
.filter(
|
|
2230
|
-
|
|
2278
|
+
const filters = [
|
|
2279
|
+
...filterSummaryLines(event.filter),
|
|
2280
|
+
...array(item.custom_filters).filter(filter => Number(filter.index) === index).flatMap(filter => filterSummaryLines(filter)),
|
|
2281
|
+
];
|
|
2282
|
+
const time = object(dependency).date ?? array(item.quota_time_ranges)[index];
|
|
2283
|
+
return `“${eventName}”的“${aggregation}”${filters.length ? `(过滤:${filters.join('、')})` : ''}${time != null ? ` [operand ${index}, time=${JSON.stringify(time)}]` : ''}`;
|
|
2231
2284
|
});
|
|
2232
2285
|
}
|
|
2233
2286
|
|
|
@@ -2249,6 +2302,10 @@ function formulaDefinitionSummary(item) {
|
|
|
2249
2302
|
}
|
|
2250
2303
|
|
|
2251
2304
|
function aggregationLabel(item) {
|
|
2305
|
+
const entity = array(item.entity_grain).map(row => object(row.taIdMeasure ?? row.ta_id_measure ?? row)).find(row => row.custom === true || text(row.entityType ?? row.entity_type).toUpperCase() === 'CUSTOM');
|
|
2306
|
+
if (text(item.aggregation_code) === 'A101' && entity) {
|
|
2307
|
+
return `Distinct entities (${text(entity.columnName ?? entity.column_name) || 'verify entity key'})`;
|
|
2308
|
+
}
|
|
2252
2309
|
const name = text(item.aggregation_name);
|
|
2253
2310
|
const code = text(item.aggregation_code);
|
|
2254
2311
|
const codeLabel = aggregationCodeLabel(code);
|
|
@@ -2312,31 +2369,19 @@ function isAggregationCode(value) {
|
|
|
2312
2369
|
return /^A\d{3}(?:_\d+)?$/.test(text(value));
|
|
2313
2370
|
}
|
|
2314
2371
|
|
|
2315
|
-
function filterSummaryLines(value) {
|
|
2316
|
-
|
|
2317
|
-
return [
|
|
2318
|
-
}
|
|
2319
|
-
|
|
2320
|
-
function collectFilterConditions(value) {
|
|
2321
|
-
if (Array.isArray(value)) return value.flatMap(collectFilterConditions);
|
|
2322
|
-
if (!value || typeof value !== 'object') return [];
|
|
2323
|
-
const source = object(value);
|
|
2324
|
-
const nested = Object.entries(source)
|
|
2325
|
-
.filter(([key]) => !['ftv', 'columnDesc', 'columnName', 'quotDesc', 'prop', 'calcuSymbol', 'calcu_symbol', 'relation', 'junctionKind', 'junction_kind'].includes(key))
|
|
2326
|
-
.flatMap(([, item]) => collectFilterConditions(item));
|
|
2327
|
-
const hasConditionIdentity = ['columnDesc', 'columnName', 'quotDesc', 'prop', 'propertyName', 'tagName'].some((key) => text(source[key]))
|
|
2328
|
-
|| array(source.ftv).some((item) => text(item));
|
|
2329
|
-
return hasConditionIdentity ? [source, ...nested] : nested;
|
|
2372
|
+
function filterSummaryLines(value, relation) {
|
|
2373
|
+
if (value == null || (Array.isArray(value) && !value.length) || (!Array.isArray(value) && !Object.keys(object(value)).length)) return [];
|
|
2374
|
+
return [renderConditionTree(conditionTree(value, relation), filterConditionSentence)];
|
|
2330
2375
|
}
|
|
2331
2376
|
|
|
2332
2377
|
function filterConditionSentence(condition) {
|
|
2333
2378
|
const field = text(condition.columnDesc || condition.quotDesc || condition.columnName || condition.prop || condition.propertyName || condition.tagName) || '未命名字段';
|
|
2334
|
-
const values = array(condition.ftv).map(
|
|
2379
|
+
const values = array(condition.ftv).map(value => typeof value === 'string' ? `“${value}”` : JSON.stringify(value));
|
|
2335
2380
|
const code = text(condition.calcuSymbol ?? condition.calcu_symbol);
|
|
2336
2381
|
const operator = filterOperatorLabel(code);
|
|
2337
2382
|
const relation = text(condition.relation || condition.junctionKind || condition.junction_kind);
|
|
2338
2383
|
const needsValue = filterOperatorNeedsValue(code);
|
|
2339
|
-
const valueText = values.length ? values.
|
|
2384
|
+
const valueText = values.length ? values.join('、') : needsValue ? '未给出固定值' : '';
|
|
2340
2385
|
const valuePart = valueText ? ` ${valueText}` : '';
|
|
2341
2386
|
const relationText = relation ? `(关系:${relation})` : '';
|
|
2342
2387
|
return `${field} ${operator}${valuePart}${relationText}`;
|
|
@@ -2381,6 +2426,7 @@ function filterOperatorNeedsValue(value) {
|
|
|
2381
2426
|
}
|
|
2382
2427
|
|
|
2383
2428
|
function occurrenceLabel(item) { return readableMetricText(item.display_name || item.event_title || item.event_name); }
|
|
2429
|
+
function groupLabel(group) { return text(object(group).columnDesc || object(group).columnName || group); }
|
|
2384
2430
|
|
|
2385
2431
|
function metricEventLabel(item) {
|
|
2386
2432
|
const title = readableMetricText(item.event_title);
|
|
@@ -2411,7 +2457,6 @@ function readableMetricText(value) {
|
|
|
2411
2457
|
.replace(/^、|、$/g, '')
|
|
2412
2458
|
.trim();
|
|
2413
2459
|
}
|
|
2414
|
-
function groupLabel(group) { return text(object(group).columnDesc || object(group).columnName || group); }
|
|
2415
2460
|
|
|
2416
2461
|
function renderSemanticConflictList() {
|
|
2417
2462
|
const reportLines = [...reportConflicts.entries()]
|
|
@@ -7,9 +7,9 @@ description: "Manage AE Agent and Agent team automations with ae-cli: create and
|
|
|
7
7
|
|
|
8
8
|
See [command risks](references/command_index.md). Read [the command contract](references/cli.md) before writing or executing an automation. Use `ae-cli automation --help` and leaf help to discover flags.
|
|
9
9
|
|
|
10
|
-
1. Resolve the host and space. A local user signs in with `ae-cli auth login`; a sandbox uses
|
|
10
|
+
1. Resolve the host and space. A local user signs in with `ae-cli auth login`; a sandbox uses the current conversation user's injected CLI token, never a shared sandbox's user binding. Pass `--agent-space-id` when the user identifies a space. Without an explicit space, the CLI inherits the current AgentSpace from its runtime context, working directory or verified current conversation; only a local invocation without space context uses the personal default. Show the resolved space when it matters.
|
|
11
11
|
2. Discover the automation with `automation list` and inspect `automation get`. Use the returned IDs; names are not unique.
|
|
12
|
-
3. For creation, select an Agent or team through the corresponding discovery commands. Match configuration to the target type. Preview writes with `--dry-run` when useful; the CLI shows a redacted request and asks the server to validate authentication, space, permissions, target state, fields, and idempotency without applying the operation.
|
|
12
|
+
3. For creation, select an Agent or team through the corresponding discovery commands. Match configuration to the target type. Use `--weekdays` or `--days-of-month` JSON arrays for multiple days with one time; skip dates absent in a month. For the actual last day of every month use `--days-of-month '["L"]'`, or combine it with dates, e.g. `'[1,15,"L"]'`; overlapping selections execute only once. Both Agent and Agent Team accept result notifications. Confirm the complete configuration before creation; save only the recurring task instructions as `--message`, never the automation-creation guide. Preview writes with `--dry-run` when useful; the CLI shows a redacted request and asks the server to validate authentication, space, permissions, target state, fields, and idempotency without applying the operation.
|
|
13
13
|
4. Execute the requested operation. Deleting an automation or stopping an execution requires confirmation; use `--yes` only when the user already authorized that action. In a non-interactive shell, these operations fail immediately unless global `--yes` is present.
|
|
14
14
|
5. Read the returned run ID, then `automation runs result`. Report acceptance separately from completion. Use `--wait` only when the user needs the final outcome now.
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ Exit condition: equivalent gateway capabilities preserve permissions, idempotenc
|
|
|
17
17
|
- `ae-cli automation enable|disable --id ID`
|
|
18
18
|
- `ae-cli automation delete --id ID --yes`
|
|
19
19
|
|
|
20
|
-
Scheduled creation needs `--schedule-kind` or `--cron-expression`. Frequencies: hourly with `--minute 0..59`; daily with `--time HH:mm`; weekly also `--
|
|
20
|
+
Scheduled creation needs `--schedule-kind` or `--cron-expression`. Frequencies: hourly with `--minute 0..59`; daily with `--time HH:mm`; weekly also `--weekdays '[1,3,5]'` (0=Sunday through 6=Saturday); monthly also `--days-of-month '[1,15,31]'` (integers 1–31 and `"L"` for the actual last day of the month). Existing single-day flags `--weekday 0..6` and `--day-of-month 1..31` remain supported; do not combine a single-day flag with its plural array. Arrays must be non-empty; duplicates are removed and dates sorted. Dates absent in the current month are skipped, never shifted into the next month. `"L"` follows the actual month end in the server timezone (including leap years). It can be selected alone or with numeric dates; overlapping selections at the same time run only once. Any minute 00–59 is valid in `--time`. Times use the server timezone. Manual creation does not require a schedule. `--trigger-immediately` executes after creation; `--wait` then waits for that execution. Creation and immediate execution have separate results: do not create another automation if only its execution failed.
|
|
21
21
|
|
|
22
22
|
On update, omitted fields retain their values. `--model null`, `--scope null`, destination IDs set to `null`, and `--save-to-knowledge-base-id null` clear nullable fields. `--channels '[]'` clears notifications; `--clear-message` clears instructions; `--reuse-conversation false` disables reuse. An automation's target is immutable on update. Team fields are rejected for Agent targets and vice versa.
|
|
23
23
|
|
|
@@ -51,3 +51,20 @@ Without `--wait`, success means accepted. With `--wait`, output includes complet
|
|
|
51
51
|
## Compatibility
|
|
52
52
|
|
|
53
53
|
The retired Agent-domain automation commands are unavailable. Use the `automation` domain for the complete lifecycle, including teams.
|
|
54
|
+
|
|
55
|
+
## Shared result notifications and identity
|
|
56
|
+
|
|
57
|
+
Agent and Agent Team both accept `--channels '["feishu","slack"]'`, `--feishu-chat-id`, `--lark-chat-id` and `--slack-channel-id`. Supported channels: `feishu`, `lark`, `slack`, `discord`, `dingtalk`, `wecom`, `mattermost`, `google_chat`, `zoom_chat`, `whatsapp`. Availability and destination permissions are validated by the server. Omitted update fields keep the existing values; `--channels '[]'` disables external delivery, and a receiver value of `null` clears that override. Results remain available in the application.
|
|
58
|
+
|
|
59
|
+
Automation requests use the current user's `cli-token`, including in a shared sandbox. They do not infer an owner from `SandboxUser` or fall back to a different member. `--agent-space-id` takes precedence over `TE_AGENT_CURRENT_AGENT_SPACE_ID` or the current AgentSpace working directory. When the shell has changed directories, `TE_AGENT_CONVERSATION_ID` resolves the space through the existing authenticated workspace target API; a resolution failure never falls back to the personal space. Membership and write permissions are enforced by the server.
|
|
60
|
+
|
|
61
|
+
For conversational creation, keep the supplied prompt editable until the user sends it. Resolve missing or ambiguous targets, summarize the configuration and obtain confirmation before creating. Agent runs inherit its model unless explicitly overridden; Agent Team retains its own configuration. Omit `--trigger-immediately` unless the user requests an immediate run.
|
|
62
|
+
|
|
63
|
+
## Multi-date examples
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ae-cli automation create --target-type agent --agent-id ID --name "Weekly report" --message "Summarize the latest metrics" --schedule-kind weekly --weekdays '[1,3,5]' --time 09:17
|
|
67
|
+
ae-cli automation update --id ID --target-type team --schedule-kind monthly --days-of-month '[1,15,"L"]' --time 18:59
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Each schedule has one execution time. Select `[1,2,3,4,5]` for Monday–Friday or `[0,6]` for weekends; these are weekdays, not a regional holiday calendar. Five-field cron accepts equivalent comma-separated weekday or month-day lists, such as `17 9 * * 1,3,5` and `59 18 1,15,L * *`. `L` is supported only in the month-day field. Ranges, steps and multiple execution times are not supported. `get` returns the saved `cronExpression` and notification configuration. On a scheduled active task, `nextRunAt`, `scheduleTimeZone` and `serverUtcOffset` describe the server's next execution.
|