@lumenflow/cli 3.18.1 → 3.19.0

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 (159) hide show
  1. package/dist/docs-sync.js +123 -6
  2. package/dist/docs-sync.js.map +1 -1
  3. package/dist/gate-co-change.js +23 -4
  4. package/dist/gate-co-change.js.map +1 -1
  5. package/dist/gates-runners.js +108 -12
  6. package/dist/gates-runners.js.map +1 -1
  7. package/dist/lumenflow-upgrade.js +1 -0
  8. package/dist/lumenflow-upgrade.js.map +1 -1
  9. package/dist/public-manifest.js +1 -1
  10. package/dist/public-manifest.js.map +1 -1
  11. package/dist/sync-templates.js +13 -0
  12. package/dist/sync-templates.js.map +1 -1
  13. package/dist/wu-block.js +10 -0
  14. package/dist/wu-block.js.map +1 -1
  15. package/dist/wu-claim-validation.js +3 -1
  16. package/dist/wu-claim-validation.js.map +1 -1
  17. package/dist/wu-claim.js +3 -1
  18. package/dist/wu-claim.js.map +1 -1
  19. package/dist/wu-done-memory-telemetry.js +5 -1
  20. package/dist/wu-done-memory-telemetry.js.map +1 -1
  21. package/dist/wu-done-ownership.js +6 -0
  22. package/dist/wu-done-ownership.js.map +1 -1
  23. package/dist/wu-edit-operations.js +4 -4
  24. package/dist/wu-edit-operations.js.map +1 -1
  25. package/dist/wu-prep.js +88 -13
  26. package/dist/wu-prep.js.map +1 -1
  27. package/dist/wu-recover.js +15 -0
  28. package/dist/wu-recover.js.map +1 -1
  29. package/dist/wu-release.js +10 -1
  30. package/dist/wu-release.js.map +1 -1
  31. package/dist/wu-spawn-prompt-builders.js +27 -2
  32. package/dist/wu-spawn-prompt-builders.js.map +1 -1
  33. package/dist/wu-state-mutation-ownership.js +136 -0
  34. package/dist/wu-state-mutation-ownership.js.map +1 -0
  35. package/dist/wu-unblock.js +10 -0
  36. package/dist/wu-unblock.js.map +1 -1
  37. package/package.json +111 -110
  38. package/packs/agent-runtime/.turbo/turbo-build.log +1 -1
  39. package/packs/agent-runtime/package.json +1 -1
  40. package/packs/sidekick/.turbo/turbo-build.log +1 -1
  41. package/packs/sidekick/package.json +1 -1
  42. package/packs/software-delivery/.turbo/turbo-build.log +1 -1
  43. package/packs/software-delivery/package.json +1 -1
  44. package/templates/core/AGENTS.md.template +157 -32
  45. package/templates/core/LUMENFLOW.md.template +44 -29
  46. package/templates/core/_frameworks/lumenflow/wu-sizing-guide.md.template +644 -0
  47. package/templates/core/ai/onboarding/agent-invocation-guide.md.template +5 -5
  48. package/templates/core/ai/onboarding/agent-safety-card.md.template +1 -0
  49. package/templates/core/ai/onboarding/docs-generation.md.template +94 -4
  50. package/templates/core/ai/onboarding/first-15-mins.md.template +1 -1
  51. package/templates/core/ai/onboarding/first-wu-mistakes.md.template +2 -1
  52. package/templates/core/ai/onboarding/initiative-orchestration.md.template +21 -21
  53. package/templates/core/ai/onboarding/quick-ref-commands.md.template +102 -95
  54. package/templates/core/ai/onboarding/release-process.md.template +12 -12
  55. package/templates/core/ai/onboarding/starting-prompt.md.template +31 -31
  56. package/templates/vendors/claude/.claude/skills/initiative-management/SKILL.md.template +2 -2
  57. package/templates/vendors/claude/.claude/skills/multi-agent-coordination/SKILL.md.template +2 -2
  58. package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +3 -3
  59. package/dist/chunk-2D2VOCA4.js +0 -37
  60. package/dist/chunk-2D5KFYGX.js +0 -284
  61. package/dist/chunk-2GXVIN57.js +0 -14072
  62. package/dist/chunk-2MQ7HZWZ.js +0 -26
  63. package/dist/chunk-2UFQ3A3C.js +0 -643
  64. package/dist/chunk-3RG5ZIWI.js +0 -10
  65. package/dist/chunk-4N74J3UT.js +0 -15
  66. package/dist/chunk-5GTOXFYR.js +0 -392
  67. package/dist/chunk-5VY6MQMC.js +0 -240
  68. package/dist/chunk-67XVPMRY.js +0 -1297
  69. package/dist/chunk-6HO4GWJE.js +0 -164
  70. package/dist/chunk-6W5XHWYV.js +0 -1890
  71. package/dist/chunk-6X4EMYJQ.js +0 -64
  72. package/dist/chunk-6XYXI2NQ.js +0 -772
  73. package/dist/chunk-7ANSOV6Q.js +0 -285
  74. package/dist/chunk-A624LFLB.js +0 -1380
  75. package/dist/chunk-ADN5NHG4.js +0 -126
  76. package/dist/chunk-B7YJYJKG.js +0 -33
  77. package/dist/chunk-CCLHCPKG.js +0 -210
  78. package/dist/chunk-CK36VROC.js +0 -1584
  79. package/dist/chunk-D3UOFRSB.js +0 -81
  80. package/dist/chunk-DFR4DJBM.js +0 -230
  81. package/dist/chunk-DSYBDHYH.js +0 -79
  82. package/dist/chunk-DWMLTXKQ.js +0 -1176
  83. package/dist/chunk-E3REJTAJ.js +0 -28
  84. package/dist/chunk-EA3IVO64.js +0 -633
  85. package/dist/chunk-EK2AKZKD.js +0 -55
  86. package/dist/chunk-ELD7JTTT.js +0 -343
  87. package/dist/chunk-EX6TT2XI.js +0 -195
  88. package/dist/chunk-EXINSFZE.js +0 -82
  89. package/dist/chunk-EZ6ZBYBM.js +0 -510
  90. package/dist/chunk-FBKAPTJ2.js +0 -16
  91. package/dist/chunk-FVLV5RYH.js +0 -1118
  92. package/dist/chunk-GDNSBQVK.js +0 -2485
  93. package/dist/chunk-GPQHMBNN.js +0 -278
  94. package/dist/chunk-GTFJB67L.js +0 -68
  95. package/dist/chunk-HANJXVKW.js +0 -1127
  96. package/dist/chunk-HEVS5YLD.js +0 -269
  97. package/dist/chunk-HMEVZKPQ.js +0 -9
  98. package/dist/chunk-HRGSYNLM.js +0 -3511
  99. package/dist/chunk-ISZR5N4K.js +0 -60
  100. package/dist/chunk-J6SUPR2C.js +0 -226
  101. package/dist/chunk-JERYVEIZ.js +0 -244
  102. package/dist/chunk-JHHWGL2N.js +0 -87
  103. package/dist/chunk-JONWQUB5.js +0 -775
  104. package/dist/chunk-K2DIWWDM.js +0 -1766
  105. package/dist/chunk-KY4PGL5V.js +0 -969
  106. package/dist/chunk-L737LQ4C.js +0 -1285
  107. package/dist/chunk-LFTWYIB2.js +0 -497
  108. package/dist/chunk-LV47RFNJ.js +0 -41
  109. package/dist/chunk-MKSAITI7.js +0 -15
  110. package/dist/chunk-MZ7RKIX4.js +0 -212
  111. package/dist/chunk-NAP6CFSO.js +0 -84
  112. package/dist/chunk-ND6MY37M.js +0 -16
  113. package/dist/chunk-NMG736UR.js +0 -683
  114. package/dist/chunk-NRAXROED.js +0 -32
  115. package/dist/chunk-NRIZR3A7.js +0 -690
  116. package/dist/chunk-NX43BG3M.js +0 -233
  117. package/dist/chunk-O645XLSI.js +0 -297
  118. package/dist/chunk-OMJD6A3S.js +0 -235
  119. package/dist/chunk-QB6SJD4T.js +0 -430
  120. package/dist/chunk-QFSTL4J3.js +0 -276
  121. package/dist/chunk-QLGDFMFX.js +0 -212
  122. package/dist/chunk-RIAAGL2E.js +0 -13
  123. package/dist/chunk-RWO5XMZ6.js +0 -86
  124. package/dist/chunk-RXRKBBSM.js +0 -149
  125. package/dist/chunk-RZOZMML6.js +0 -363
  126. package/dist/chunk-U7I7FS7T.js +0 -113
  127. package/dist/chunk-UI42RODY.js +0 -717
  128. package/dist/chunk-UTVMVSCO.js +0 -519
  129. package/dist/chunk-V6OJGLBA.js +0 -1746
  130. package/dist/chunk-W2JHVH7D.js +0 -152
  131. package/dist/chunk-WD3Y7VQN.js +0 -280
  132. package/dist/chunk-WOCTQ5MS.js +0 -303
  133. package/dist/chunk-WZR3ZUNN.js +0 -696
  134. package/dist/chunk-XGI665H7.js +0 -150
  135. package/dist/chunk-XKY65P2T.js +0 -304
  136. package/dist/chunk-Y4CQZY65.js +0 -57
  137. package/dist/chunk-YFEXKLVE.js +0 -194
  138. package/dist/chunk-YHO3HS5X.js +0 -287
  139. package/dist/chunk-YLS7AZSX.js +0 -738
  140. package/dist/chunk-ZE473AO6.js +0 -49
  141. package/dist/chunk-ZF747T3O.js +0 -644
  142. package/dist/chunk-ZHCZHZH3.js +0 -43
  143. package/dist/chunk-ZZNZX2XY.js +0 -87
  144. package/dist/constants-7QAP3VQ4.js +0 -23
  145. package/dist/dist-IY3UUMWK.js +0 -33
  146. package/dist/invariants-runner-W5RGHCSU.js +0 -27
  147. package/dist/lane-lock-6J36HD5O.js +0 -35
  148. package/dist/mem-checkpoint-core-EANG2GVN.js +0 -14
  149. package/dist/mem-signal-core-2LZ2WYHW.js +0 -19
  150. package/dist/memory-store-OLB5FO7K.js +0 -18
  151. package/dist/service-6BYCOCO5.js +0 -13
  152. package/dist/spawn-policy-resolver-NTSZYQ6R.js +0 -17
  153. package/dist/spawn-task-builder-R4E2BHSW.js +0 -22
  154. package/dist/wu-done-pr-WLFFFEPJ.js +0 -25
  155. package/dist/wu-done-validation-3J5E36FE.js +0 -30
  156. package/dist/wu-duplicate-id-detector-5S7JHELK.js +0 -232
  157. package/packs/sidekick/.turbo/turbo-test.log +0 -12
  158. package/packs/sidekick/.turbo/turbo-typecheck.log +0 -4
  159. package/packs/software-delivery/.turbo/turbo-typecheck.log +0 -4
@@ -1,772 +0,0 @@
1
- import {
2
- CONFIG_FILES,
3
- FILE_EXTENSIONS,
4
- LUMENFLOW_PATHS,
5
- STDIO,
6
- STRING_LITERALS
7
- } from "./chunk-DWMLTXKQ.js";
8
-
9
- // ../core/dist/telemetry.js
10
- import { appendFileSync, mkdirSync, existsSync, readFileSync, writeFileSync } from "fs";
11
- import { execSync } from "child_process";
12
- import path from "path";
13
- import YAML from "yaml";
14
- var TELEMETRY_DIR = LUMENFLOW_PATHS.TELEMETRY;
15
- var GATES_LOG = `${TELEMETRY_DIR}/gates${FILE_EXTENSIONS.NDJSON}`;
16
- var LLM_CLASSIFICATION_LOG = `${TELEMETRY_DIR}/llm-classification${FILE_EXTENSIONS.NDJSON}`;
17
- var COSTS_LOG = `${TELEMETRY_DIR}/costs${FILE_EXTENSIONS.NDJSON}`;
18
- var FLOW_LOG = LUMENFLOW_PATHS.FLOW_LOG;
19
- var DORA_LOG = `${TELEMETRY_DIR}/dora${FILE_EXTENSIONS.NDJSON}`;
20
- var WORKSPACE_FILE = CONFIG_FILES.WORKSPACE_CONFIG;
21
- var CLOUD_SYNC_STATE_FILE = `${TELEMETRY_DIR}/cloud-sync-state${FILE_EXTENSIONS.JSON}`;
22
- var CLOUD_SYNC_LOG_PREFIX = "[telemetry:cloud-sync]";
23
- var PACK_KEY_SOFTWARE_DELIVERY = "software_delivery";
24
- var CONTROL_PLANE_FIELD = "control_plane";
25
- var CONTROL_PLANE_AUTH_FIELD = "auth";
26
- var CONTROL_PLANE_ENDPOINT_FIELD = "endpoint";
27
- var CONTROL_PLANE_SYNC_INTERVAL_FIELD = "sync_interval";
28
- var CONTROL_PLANE_BATCH_SIZE_FIELD = "batch_size";
29
- var CONTROL_PLANE_TIMEOUT_MS_FIELD = "timeout_ms";
30
- var CONTROL_PLANE_TOKEN_ENV_FIELD = "token_env";
31
- var WORKSPACE_ID_FIELD = "id";
32
- var HTTP = {
33
- METHOD_POST: "POST",
34
- HEADER_AUTHORIZATION: "authorization",
35
- HEADER_CONTENT_TYPE: "content-type",
36
- CONTENT_TYPE_JSON: "application/json"
37
- };
38
- var CONTROL_PLANE_API_PATH = {
39
- TELEMETRY: "/api/v1/telemetry"
40
- };
41
- var DEFAULT_BATCH_SIZE = 100;
42
- var DEFAULT_TIMEOUT_MS = 1e4;
43
- var MS_PER_SECOND = 1e3;
44
- var ERROR_NAME = {
45
- ABORT: "AbortError"
46
- };
47
- var CONTROL_PLANE_TOKEN_ENV_PATTERN = /^[A-Z][A-Z0-9_]*$/;
48
- var METRIC_NAME = {
49
- GATES_DURATION_MS: "gates.duration_ms",
50
- FLOW_EVENT: "flow.event",
51
- DORA_METRIC: "dora",
52
- COST_USD: "cost.usd",
53
- RAW_GATES: "telemetry.raw.gates",
54
- RAW_FLOW: "telemetry.raw.flow",
55
- RAW_DORA: "telemetry.raw.dora",
56
- RAW_COSTS: "telemetry.raw.costs"
57
- };
58
- var TELEMETRY_SOURCE = {
59
- GATES: "gates",
60
- FLOW: "flow",
61
- DORA: "dora",
62
- COSTS: "costs"
63
- };
64
- function ensureTelemetryDir() {
65
- try {
66
- mkdirSync(TELEMETRY_DIR, { recursive: true });
67
- } catch {
68
- }
69
- }
70
- function emit(filePath, event) {
71
- ensureTelemetryDir();
72
- const line = `${JSON.stringify(event)}${STRING_LITERALS.NEWLINE}`;
73
- try {
74
- appendFileSync(filePath, line, { encoding: "utf-8" });
75
- } catch (err) {
76
- const message = err instanceof Error ? err.message : String(err);
77
- console.error(`[telemetry] Failed to emit to ${filePath}:`, message);
78
- }
79
- }
80
- function emitGateEvent(data) {
81
- const event = {
82
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
83
- wu_id: data.wu_id || null,
84
- lane: data.lane || null,
85
- gate_name: data.gate_name,
86
- passed: data.passed,
87
- duration_ms: data.duration_ms
88
- };
89
- emit(GATES_LOG, event);
90
- }
91
- function getCurrentWU() {
92
- try {
93
- const branch = execSync("git rev-parse --abbrev-ref HEAD", {
94
- encoding: "utf-8",
95
- stdio: [STDIO.PIPE, STDIO.PIPE, STDIO.IGNORE]
96
- }).trim();
97
- const match = branch.match(/wu-(\d+)/i);
98
- if (match) {
99
- return `WU-${match[1]}`.toUpperCase();
100
- }
101
- } catch {
102
- }
103
- return null;
104
- }
105
- function getCurrentLane() {
106
- try {
107
- const branch = execSync("git rev-parse --abbrev-ref HEAD", {
108
- encoding: "utf-8",
109
- stdio: [STDIO.PIPE, STDIO.PIPE, STDIO.IGNORE]
110
- }).trim();
111
- const match = branch.match(/^lane\/([^/]+)\//i);
112
- const laneSegment = match?.[1];
113
- if (laneSegment) {
114
- return laneSegment.charAt(0).toUpperCase() + laneSegment.slice(1).toLowerCase();
115
- }
116
- } catch {
117
- }
118
- return null;
119
- }
120
- function emitWUFlowEvent(event, logPath = FLOW_LOG) {
121
- const logDir = path.dirname(logPath);
122
- if (!existsSync(logDir)) {
123
- mkdirSync(logDir, { recursive: true });
124
- }
125
- const line = JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...event });
126
- try {
127
- appendFileSync(logPath, `${line}${STRING_LITERALS.NEWLINE}`, { encoding: "utf-8" });
128
- } catch (err) {
129
- const message = err instanceof Error ? err.message : String(err);
130
- console.error(`[telemetry] Failed to emit flow event: ${message}`);
131
- }
132
- }
133
- function emitDoraTelemetry(record) {
134
- ensureTelemetryDir();
135
- const line = JSON.stringify({
136
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
137
- metric: record.metric,
138
- value: record.value,
139
- tier: record.tier
140
- });
141
- try {
142
- appendFileSync(DORA_LOG, `${line}${STRING_LITERALS.NEWLINE}`, { encoding: "utf-8" });
143
- } catch (err) {
144
- const message = err instanceof Error ? err.message : String(err);
145
- console.error(`[telemetry] Failed to emit DORA telemetry: ${message}`);
146
- }
147
- }
148
- function isRecord(value) {
149
- return typeof value === "object" && value !== null;
150
- }
151
- function asNonEmptyString(value) {
152
- if (typeof value !== "string") {
153
- return void 0;
154
- }
155
- const trimmed = value.trim();
156
- return trimmed.length > 0 ? trimmed : void 0;
157
- }
158
- function asPositiveInteger(value) {
159
- if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
160
- return void 0;
161
- }
162
- return value;
163
- }
164
- function asFiniteNumber(value) {
165
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
166
- }
167
- function asPrimitiveTagValue(value) {
168
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
169
- return value;
170
- }
171
- return void 0;
172
- }
173
- function normalizeEndpoint(endpoint) {
174
- return endpoint.endsWith("/") ? endpoint.slice(0, endpoint.length - 1) : endpoint;
175
- }
176
- function normalizeTimestamp(value, fallbackIso) {
177
- const rawTimestamp = asNonEmptyString(value);
178
- return rawTimestamp ?? fallbackIso;
179
- }
180
- function warnCloudSync(logger, message) {
181
- logger?.warn?.(`${CLOUD_SYNC_LOG_PREFIX} ${message}`);
182
- }
183
- function createDefaultCloudSyncState() {
184
- return {
185
- version: 1,
186
- lastSyncAtMs: 0,
187
- files: {
188
- gates: {
189
- offset: 0
190
- },
191
- flow: {
192
- offset: 0
193
- },
194
- dora: {
195
- offset: 0
196
- },
197
- costs: {
198
- offset: 0
199
- }
200
- }
201
- };
202
- }
203
- function normalizeStateOffset(value) {
204
- if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
205
- return 0;
206
- }
207
- return value;
208
- }
209
- function loadCloudSyncState(statePath) {
210
- if (!existsSync(statePath)) {
211
- return createDefaultCloudSyncState();
212
- }
213
- try {
214
- const raw = readFileSync(statePath, "utf-8");
215
- const parsed = JSON.parse(raw);
216
- if (!isRecord(parsed)) {
217
- return createDefaultCloudSyncState();
218
- }
219
- const filesRaw = Reflect.get(parsed, "files");
220
- const gatesRaw = isRecord(filesRaw) ? Reflect.get(filesRaw, TELEMETRY_SOURCE.GATES) : void 0;
221
- const flowRaw = isRecord(filesRaw) ? Reflect.get(filesRaw, TELEMETRY_SOURCE.FLOW) : void 0;
222
- const doraRaw = isRecord(filesRaw) ? Reflect.get(filesRaw, TELEMETRY_SOURCE.DORA) : void 0;
223
- const costsRaw = isRecord(filesRaw) ? Reflect.get(filesRaw, TELEMETRY_SOURCE.COSTS) : void 0;
224
- const gatesOffset = isRecord(gatesRaw) ? normalizeStateOffset(Reflect.get(gatesRaw, "offset")) : 0;
225
- const flowOffset = isRecord(flowRaw) ? normalizeStateOffset(Reflect.get(flowRaw, "offset")) : 0;
226
- const doraOffset = isRecord(doraRaw) ? normalizeStateOffset(Reflect.get(doraRaw, "offset")) : 0;
227
- const costsOffset = isRecord(costsRaw) ? normalizeStateOffset(Reflect.get(costsRaw, "offset")) : 0;
228
- const lastSyncAtMs = normalizeStateOffset(Reflect.get(parsed, "lastSyncAtMs"));
229
- return {
230
- version: 1,
231
- lastSyncAtMs,
232
- files: {
233
- gates: {
234
- offset: gatesOffset
235
- },
236
- flow: {
237
- offset: flowOffset
238
- },
239
- dora: {
240
- offset: doraOffset
241
- },
242
- costs: {
243
- offset: costsOffset
244
- }
245
- }
246
- };
247
- } catch {
248
- return createDefaultCloudSyncState();
249
- }
250
- }
251
- function saveCloudSyncState(statePath, state) {
252
- mkdirSync(path.dirname(statePath), { recursive: true });
253
- writeFileSync(statePath, `${JSON.stringify(state, null, 2)}${STRING_LITERALS.NEWLINE}`, {
254
- encoding: "utf-8"
255
- });
256
- }
257
- function getSourceOffset(state, source) {
258
- if (source === TELEMETRY_SOURCE.GATES) {
259
- return state.files.gates.offset;
260
- }
261
- if (source === TELEMETRY_SOURCE.DORA) {
262
- return state.files.dora.offset;
263
- }
264
- if (source === TELEMETRY_SOURCE.COSTS) {
265
- return state.files.costs.offset;
266
- }
267
- return state.files.flow.offset;
268
- }
269
- function setSourceOffset(state, source, offset) {
270
- if (source === TELEMETRY_SOURCE.GATES) {
271
- state.files.gates.offset = offset;
272
- return;
273
- }
274
- if (source === TELEMETRY_SOURCE.DORA) {
275
- state.files.dora.offset = offset;
276
- return;
277
- }
278
- if (source === TELEMETRY_SOURCE.COSTS) {
279
- state.files.costs.offset = offset;
280
- return;
281
- }
282
- state.files.flow.offset = offset;
283
- }
284
- function resolveTelemetryPath(workspaceRoot, source) {
285
- if (source === TELEMETRY_SOURCE.GATES) {
286
- return path.join(workspaceRoot, GATES_LOG);
287
- }
288
- if (source === TELEMETRY_SOURCE.DORA) {
289
- return path.join(workspaceRoot, DORA_LOG);
290
- }
291
- if (source === TELEMETRY_SOURCE.COSTS) {
292
- return path.join(workspaceRoot, COSTS_LOG);
293
- }
294
- return path.join(workspaceRoot, FLOW_LOG);
295
- }
296
- function mapGatesEventToTelemetryRecord(payload, fallbackIso) {
297
- const gateName = asNonEmptyString(Reflect.get(payload, "gate_name")) ?? "unknown";
298
- const passedRaw = Reflect.get(payload, "passed");
299
- const passed = typeof passedRaw === "boolean" ? passedRaw : false;
300
- const durationMs = asFiniteNumber(Reflect.get(payload, "duration_ms")) ?? 0;
301
- const wuId = asPrimitiveTagValue(Reflect.get(payload, "wu_id"));
302
- const lane = asPrimitiveTagValue(Reflect.get(payload, "lane"));
303
- const tags = {
304
- source: TELEMETRY_SOURCE.GATES,
305
- gate_name: gateName,
306
- passed
307
- };
308
- if (wuId !== void 0) {
309
- tags.wu_id = wuId;
310
- }
311
- if (lane !== void 0) {
312
- tags.lane = lane;
313
- }
314
- return {
315
- metric: METRIC_NAME.GATES_DURATION_MS,
316
- value: durationMs,
317
- timestamp: normalizeTimestamp(Reflect.get(payload, "timestamp"), fallbackIso),
318
- tags
319
- };
320
- }
321
- function mapFlowEventToTelemetryRecord(payload, fallbackIso) {
322
- const script = asNonEmptyString(Reflect.get(payload, "script")) ?? "unknown";
323
- const step = asNonEmptyString(Reflect.get(payload, "step")) ?? asNonEmptyString(Reflect.get(payload, "event_type")) ?? "event";
324
- const value = asFiniteNumber(Reflect.get(payload, "duration_ms")) ?? 1;
325
- const status = asPrimitiveTagValue(Reflect.get(payload, "status"));
326
- const wuId = asPrimitiveTagValue(Reflect.get(payload, "wu_id"));
327
- const lane = asPrimitiveTagValue(Reflect.get(payload, "lane"));
328
- const tags = {
329
- source: TELEMETRY_SOURCE.FLOW,
330
- script,
331
- step
332
- };
333
- if (status !== void 0) {
334
- tags.status = status;
335
- }
336
- if (wuId !== void 0) {
337
- tags.wu_id = wuId;
338
- }
339
- if (lane !== void 0) {
340
- tags.lane = lane;
341
- }
342
- return {
343
- metric: METRIC_NAME.FLOW_EVENT,
344
- value,
345
- timestamp: normalizeTimestamp(Reflect.get(payload, "timestamp"), fallbackIso),
346
- tags
347
- };
348
- }
349
- function mapDoraEventToTelemetryRecord(payload, fallbackIso) {
350
- const metric = asNonEmptyString(Reflect.get(payload, "metric")) ?? "dora.unknown";
351
- const value = asFiniteNumber(Reflect.get(payload, "value")) ?? 0;
352
- const tier = asNonEmptyString(Reflect.get(payload, "tier"));
353
- const tags = {
354
- source: TELEMETRY_SOURCE.DORA
355
- };
356
- if (tier !== void 0) {
357
- tags.tier = tier;
358
- }
359
- return {
360
- metric,
361
- value,
362
- timestamp: normalizeTimestamp(Reflect.get(payload, "timestamp"), fallbackIso),
363
- tags
364
- };
365
- }
366
- function mapCostEventToTelemetryRecord(payload, fallbackIso) {
367
- const costUsd = asFiniteNumber(Reflect.get(payload, "cost_usd")) ?? 0;
368
- const inputTokens = asFiniteNumber(Reflect.get(payload, "input_tokens"));
369
- const outputTokens = asFiniteNumber(Reflect.get(payload, "output_tokens"));
370
- const operation = asPrimitiveTagValue(Reflect.get(payload, "operation"));
371
- const model = asPrimitiveTagValue(Reflect.get(payload, "model"));
372
- const wuId = asPrimitiveTagValue(Reflect.get(payload, "wu_id"));
373
- const agentId = asPrimitiveTagValue(Reflect.get(payload, "agent_id"));
374
- const sessionId = asPrimitiveTagValue(Reflect.get(payload, "session_id"));
375
- const tags = {
376
- source: TELEMETRY_SOURCE.COSTS,
377
- source_type: "cost"
378
- };
379
- if (operation !== void 0) {
380
- tags.operation = operation;
381
- }
382
- if (model !== void 0) {
383
- tags.model = model;
384
- }
385
- if (wuId !== void 0) {
386
- tags.wu_id = wuId;
387
- }
388
- if (agentId !== void 0) {
389
- tags.agent_id = agentId;
390
- }
391
- if (sessionId !== void 0) {
392
- tags.session_id = sessionId;
393
- }
394
- if (inputTokens !== void 0) {
395
- tags.input_tokens = inputTokens;
396
- }
397
- if (outputTokens !== void 0) {
398
- tags.output_tokens = outputTokens;
399
- }
400
- return {
401
- metric: METRIC_NAME.COST_USD,
402
- value: costUsd,
403
- timestamp: normalizeTimestamp(Reflect.get(payload, "timestamp"), fallbackIso),
404
- tags
405
- };
406
- }
407
- function resolveRawMetricName(source) {
408
- if (source === TELEMETRY_SOURCE.GATES)
409
- return METRIC_NAME.RAW_GATES;
410
- if (source === TELEMETRY_SOURCE.DORA)
411
- return METRIC_NAME.RAW_DORA;
412
- if (source === TELEMETRY_SOURCE.COSTS)
413
- return METRIC_NAME.RAW_COSTS;
414
- return METRIC_NAME.RAW_FLOW;
415
- }
416
- function mapTelemetryLineToRecord(source, payload, fallbackIso) {
417
- if (!isRecord(payload)) {
418
- return {
419
- metric: resolveRawMetricName(source),
420
- value: 1,
421
- timestamp: fallbackIso,
422
- tags: {
423
- source
424
- }
425
- };
426
- }
427
- if (source === TELEMETRY_SOURCE.GATES) {
428
- return mapGatesEventToTelemetryRecord(payload, fallbackIso);
429
- }
430
- if (source === TELEMETRY_SOURCE.DORA) {
431
- return mapDoraEventToTelemetryRecord(payload, fallbackIso);
432
- }
433
- if (source === TELEMETRY_SOURCE.COSTS) {
434
- return mapCostEventToTelemetryRecord(payload, fallbackIso);
435
- }
436
- return mapFlowEventToTelemetryRecord(payload, fallbackIso);
437
- }
438
- function readTelemetryLinesFromOffset(input) {
439
- if (!existsSync(input.filePath)) {
440
- return {
441
- items: [],
442
- malformedLines: 0,
443
- effectiveOffset: 0
444
- };
445
- }
446
- const fileBuffer = readFileSync(input.filePath);
447
- const fileSize = fileBuffer.byteLength;
448
- const effectiveOffset = Math.min(input.initialOffset, fileSize);
449
- const sliceBuffer = fileBuffer.subarray(effectiveOffset);
450
- const content = sliceBuffer.toString("utf-8");
451
- let malformedLines = 0;
452
- const items = [];
453
- let cursor = 0;
454
- let runningOffset = effectiveOffset;
455
- while (cursor < content.length) {
456
- const newlineIndex = content.indexOf(STRING_LITERALS.NEWLINE, cursor);
457
- if (newlineIndex < 0) {
458
- break;
459
- }
460
- const lineWithNewline = content.slice(cursor, newlineIndex + 1);
461
- runningOffset += Buffer.byteLength(lineWithNewline, "utf8");
462
- const line = content.slice(cursor, newlineIndex).trim();
463
- cursor = newlineIndex + 1;
464
- if (line.length === 0) {
465
- items.push({
466
- record: null,
467
- offsetAfterLine: runningOffset
468
- });
469
- continue;
470
- }
471
- try {
472
- const parsed = JSON.parse(line);
473
- items.push({
474
- record: mapTelemetryLineToRecord(input.source, parsed, input.fallbackIso),
475
- offsetAfterLine: runningOffset
476
- });
477
- } catch {
478
- malformedLines += 1;
479
- warnCloudSync(input.logger, `Skipping malformed NDJSON line in ${input.filePath} at offset ${runningOffset}.`);
480
- items.push({
481
- record: null,
482
- offsetAfterLine: runningOffset
483
- });
484
- }
485
- }
486
- return {
487
- items,
488
- malformedLines,
489
- effectiveOffset
490
- };
491
- }
492
- function safeResponseText(response) {
493
- return response.text().catch(() => "");
494
- }
495
- async function pushTelemetryBatch(input) {
496
- const controller = new AbortController();
497
- const timeout = setTimeout(() => controller.abort(), input.timeoutMs);
498
- try {
499
- const response = await input.fetchFn(`${input.endpoint}${CONTROL_PLANE_API_PATH.TELEMETRY}`, {
500
- method: HTTP.METHOD_POST,
501
- headers: {
502
- [HTTP.HEADER_AUTHORIZATION]: `Bearer ${input.token}`,
503
- [HTTP.HEADER_CONTENT_TYPE]: HTTP.CONTENT_TYPE_JSON
504
- },
505
- body: JSON.stringify({
506
- workspace_id: input.workspaceId,
507
- records: input.records
508
- }),
509
- signal: controller.signal
510
- });
511
- if (!response.ok) {
512
- const responseText = await safeResponseText(response);
513
- const suffix = responseText.length > 0 ? `: ${responseText}` : "";
514
- throw new Error(`HTTP ${response.status}${suffix}`);
515
- }
516
- } catch (error) {
517
- if (error instanceof Error && error.name === ERROR_NAME.ABORT) {
518
- throw new Error(`request timed out after ${input.timeoutMs}ms`, { cause: error });
519
- }
520
- throw error;
521
- } finally {
522
- clearTimeout(timeout);
523
- }
524
- }
525
- function detectMisnestedControlPlane(workspace) {
526
- const rootControlPlane = Reflect.get(workspace, CONTROL_PLANE_FIELD);
527
- if (isRecord(rootControlPlane)) {
528
- return null;
529
- }
530
- const softwareDelivery = Reflect.get(workspace, PACK_KEY_SOFTWARE_DELIVERY);
531
- if (!isRecord(softwareDelivery)) {
532
- return null;
533
- }
534
- const nestedControlPlane = Reflect.get(softwareDelivery, CONTROL_PLANE_FIELD);
535
- if (!isRecord(nestedControlPlane)) {
536
- return null;
537
- }
538
- const detectedPath = `${PACK_KEY_SOFTWARE_DELIVERY}.${CONTROL_PLANE_FIELD}`;
539
- return {
540
- message: `${CONTROL_PLANE_FIELD} is misnested under ${PACK_KEY_SOFTWARE_DELIVERY}. It must be a root-level key in workspace.yaml.`,
541
- detectedPath,
542
- remediation: `Move ${CONTROL_PLANE_FIELD} to the workspace root. Run: pnpm config:set --key control_plane.<sub-key> --value <value>`
543
- };
544
- }
545
- function resolveCloudSyncConfig(input) {
546
- const workspacePath = path.join(input.workspaceRoot, WORKSPACE_FILE);
547
- if (!existsSync(workspacePath)) {
548
- return null;
549
- }
550
- let parsedWorkspace;
551
- try {
552
- parsedWorkspace = YAML.parse(readFileSync(workspacePath, "utf-8"));
553
- } catch (error) {
554
- const message = error instanceof Error ? error.message : String(error);
555
- warnCloudSync(input.logger, `Unable to parse workspace config: ${message}`);
556
- return null;
557
- }
558
- if (!isRecord(parsedWorkspace)) {
559
- return null;
560
- }
561
- const workspaceId = asNonEmptyString(Reflect.get(parsedWorkspace, WORKSPACE_ID_FIELD));
562
- if (!workspaceId) {
563
- warnCloudSync(input.logger, "Skipping sync: workspace id is missing in workspace.yaml.");
564
- return null;
565
- }
566
- const controlPlaneRaw = Reflect.get(parsedWorkspace, CONTROL_PLANE_FIELD);
567
- if (!isRecord(controlPlaneRaw)) {
568
- const misnesting = detectMisnestedControlPlane(parsedWorkspace);
569
- if (misnesting) {
570
- warnCloudSync(input.logger, misnesting.message);
571
- warnCloudSync(input.logger, misnesting.remediation);
572
- }
573
- return null;
574
- }
575
- const endpoint = asNonEmptyString(Reflect.get(controlPlaneRaw, CONTROL_PLANE_ENDPOINT_FIELD));
576
- const syncIntervalSeconds = asPositiveInteger(Reflect.get(controlPlaneRaw, CONTROL_PLANE_SYNC_INTERVAL_FIELD));
577
- const batchSize = asPositiveInteger(Reflect.get(controlPlaneRaw, CONTROL_PLANE_BATCH_SIZE_FIELD)) ?? DEFAULT_BATCH_SIZE;
578
- const timeoutMs = asPositiveInteger(Reflect.get(controlPlaneRaw, CONTROL_PLANE_TIMEOUT_MS_FIELD)) ?? DEFAULT_TIMEOUT_MS;
579
- if (!endpoint || !syncIntervalSeconds) {
580
- warnCloudSync(input.logger, "Skipping sync: control_plane endpoint and sync_interval must be configured.");
581
- return null;
582
- }
583
- const authRaw = Reflect.get(controlPlaneRaw, CONTROL_PLANE_AUTH_FIELD);
584
- if (!isRecord(authRaw)) {
585
- warnCloudSync(input.logger, "Skipping sync: control_plane.auth is missing.");
586
- return null;
587
- }
588
- const tokenEnv = asNonEmptyString(Reflect.get(authRaw, CONTROL_PLANE_TOKEN_ENV_FIELD));
589
- if (!tokenEnv || !CONTROL_PLANE_TOKEN_ENV_PATTERN.test(tokenEnv)) {
590
- warnCloudSync(input.logger, "Skipping sync: control_plane.auth.token_env is invalid.");
591
- return null;
592
- }
593
- const tokenValue = input.environment[tokenEnv];
594
- const token = typeof tokenValue === "string" ? tokenValue.trim() : "";
595
- if (token.length === 0) {
596
- warnCloudSync(input.logger, `Skipping sync: missing cloud auth token in env "${tokenEnv}".`);
597
- return null;
598
- }
599
- try {
600
- void new URL(endpoint);
601
- } catch {
602
- warnCloudSync(input.logger, `Skipping sync: invalid control_plane endpoint "${endpoint}".`);
603
- return null;
604
- }
605
- return {
606
- workspaceId,
607
- endpoint: normalizeEndpoint(endpoint),
608
- token,
609
- syncIntervalMs: syncIntervalSeconds * MS_PER_SECOND,
610
- batchSize,
611
- timeoutMs
612
- };
613
- }
614
- async function syncNdjsonTelemetryToCloud(options = {}) {
615
- const workspaceRoot = options.workspaceRoot ?? process.cwd();
616
- const logger = options.logger;
617
- const fetchFn = options.fetchFn ?? fetch;
618
- const now = options.now ?? Date.now;
619
- const environment = options.environment ?? process.env;
620
- const statePath = path.join(workspaceRoot, CLOUD_SYNC_STATE_FILE);
621
- const config = resolveCloudSyncConfig({
622
- workspaceRoot,
623
- environment,
624
- logger
625
- });
626
- if (config === null) {
627
- return {
628
- recordsRead: 0,
629
- recordsSent: 0,
630
- malformedLines: 0,
631
- batchesAttempted: 0,
632
- batchesSucceeded: 0,
633
- skippedReason: "control-plane-unavailable"
634
- };
635
- }
636
- const state = loadCloudSyncState(statePath);
637
- const nowMs = now();
638
- if (state.lastSyncAtMs > 0 && nowMs - state.lastSyncAtMs < config.syncIntervalMs) {
639
- return {
640
- recordsRead: 0,
641
- recordsSent: 0,
642
- malformedLines: 0,
643
- batchesAttempted: 0,
644
- batchesSucceeded: 0,
645
- skippedReason: "sync-interval-not-elapsed"
646
- };
647
- }
648
- const fallbackIso = new Date(nowMs).toISOString();
649
- const nextState = {
650
- version: 1,
651
- lastSyncAtMs: state.lastSyncAtMs,
652
- files: {
653
- gates: { offset: state.files.gates.offset },
654
- flow: { offset: state.files.flow.offset },
655
- dora: { offset: state.files.dora.offset },
656
- costs: { offset: state.files.costs.offset }
657
- }
658
- };
659
- let malformedLines = 0;
660
- let recordsRead = 0;
661
- let recordsSent = 0;
662
- let batchesAttempted = 0;
663
- let batchesSucceeded = 0;
664
- let syncFailed = false;
665
- let stateChanged = false;
666
- for (const source of [
667
- TELEMETRY_SOURCE.GATES,
668
- TELEMETRY_SOURCE.FLOW,
669
- TELEMETRY_SOURCE.DORA,
670
- TELEMETRY_SOURCE.COSTS
671
- ]) {
672
- const filePath = resolveTelemetryPath(workspaceRoot, source);
673
- const sourceLines = readTelemetryLinesFromOffset({
674
- filePath,
675
- source,
676
- initialOffset: getSourceOffset(nextState, source),
677
- logger,
678
- fallbackIso
679
- });
680
- malformedLines += sourceLines.malformedLines;
681
- let acknowledgedOffset = sourceLines.effectiveOffset;
682
- let index = 0;
683
- while (index < sourceLines.items.length) {
684
- const currentLine = sourceLines.items[index];
685
- if (currentLine?.record === null) {
686
- acknowledgedOffset = currentLine.offsetAfterLine;
687
- stateChanged = true;
688
- index += 1;
689
- continue;
690
- }
691
- const batch = [];
692
- let lastBatchOffset = acknowledgedOffset;
693
- while (index < sourceLines.items.length && sourceLines.items[index]?.record !== null && batch.length < config.batchSize) {
694
- const line = sourceLines.items[index];
695
- if (!line || line.record === null) {
696
- break;
697
- }
698
- batch.push(line.record);
699
- lastBatchOffset = line.offsetAfterLine;
700
- index += 1;
701
- }
702
- recordsRead += batch.length;
703
- batchesAttempted += 1;
704
- try {
705
- await pushTelemetryBatch({
706
- endpoint: config.endpoint,
707
- workspaceId: config.workspaceId,
708
- token: config.token,
709
- timeoutMs: config.timeoutMs,
710
- records: batch,
711
- fetchFn
712
- });
713
- recordsSent += batch.length;
714
- batchesSucceeded += 1;
715
- acknowledgedOffset = lastBatchOffset;
716
- stateChanged = true;
717
- } catch (error) {
718
- const message = error instanceof Error ? error.message : String(error);
719
- warnCloudSync(logger, `Telemetry sync failed: ${message}`);
720
- syncFailed = true;
721
- break;
722
- }
723
- }
724
- setSourceOffset(nextState, source, acknowledgedOffset);
725
- if (syncFailed) {
726
- break;
727
- }
728
- }
729
- if (stateChanged) {
730
- if (!syncFailed) {
731
- nextState.lastSyncAtMs = nowMs;
732
- }
733
- saveCloudSyncState(statePath, nextState);
734
- } else if (!syncFailed) {
735
- nextState.lastSyncAtMs = nowMs;
736
- saveCloudSyncState(statePath, nextState);
737
- }
738
- return {
739
- recordsRead,
740
- recordsSent,
741
- malformedLines,
742
- batchesAttempted,
743
- batchesSucceeded,
744
- ...syncFailed ? { skippedReason: "sync-failed" } : {}
745
- };
746
- }
747
-
748
- // ../core/dist/spawn-policy-resolver.js
749
- function emitMethodologyTelemetry(config, policy) {
750
- if (!config.telemetry?.methodology?.enabled) {
751
- return;
752
- }
753
- const event = {
754
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
755
- event_type: "methodology.selection",
756
- methodology_testing: policy.testing,
757
- methodology_architecture: policy.architecture,
758
- event_context: "spawn"
759
- };
760
- emit(LUMENFLOW_PATHS.METHODOLOGY_LOG, event);
761
- }
762
-
763
- export {
764
- emit,
765
- emitGateEvent,
766
- getCurrentWU,
767
- getCurrentLane,
768
- emitWUFlowEvent,
769
- emitDoraTelemetry,
770
- syncNdjsonTelemetryToCloud,
771
- emitMethodologyTelemetry
772
- };