agent-scenario-loop 0.1.3 → 0.1.4

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 (47) hide show
  1. package/app/profile-session.ts +263 -17
  2. package/dist/core/artifact-contract.d.ts +6 -4
  3. package/dist/core/artifact-contract.js +164 -15
  4. package/dist/core/schema-validator.d.ts +1 -0
  5. package/dist/core/schema-validator.js +1 -0
  6. package/dist/runner/android-adb-driver.d.ts +7 -2
  7. package/dist/runner/android-adb-driver.js +7 -1
  8. package/dist/runner/android-adb.d.ts +40 -5
  9. package/dist/runner/android-adb.js +1046 -664
  10. package/dist/runner/ios-simctl.d.ts +1 -0
  11. package/dist/runner/ios-simctl.js +1 -0
  12. package/dist/runner/profile-android.d.ts +11 -1
  13. package/dist/runner/profile-android.js +230 -16
  14. package/dist/runner/profile-ios.d.ts +3 -2
  15. package/dist/runner/profile-ios.js +223 -20
  16. package/dist/runner/profile-mobile.d.ts +31 -3
  17. package/dist/runner/profile-mobile.js +793 -20
  18. package/dist/runner/validate-project.js +3 -0
  19. package/dist/scripts/consumer-rehearsal.d.ts +119 -0
  20. package/dist/scripts/consumer-rehearsal.js +757 -0
  21. package/dist/scripts/downstream-local-package-gate.d.ts +2 -0
  22. package/dist/scripts/downstream-local-package-gate.js +264 -0
  23. package/dist/scripts/package-smoke.d.ts +96 -0
  24. package/dist/scripts/package-smoke.js +2282 -0
  25. package/dist/scripts/release-readiness.d.ts +2 -0
  26. package/dist/scripts/release-readiness.js +520 -0
  27. package/docs/adapters.md +3 -1
  28. package/docs/api.md +2 -2
  29. package/docs/authoring.md +34 -2
  30. package/docs/consumer-rehearsal.md +27 -1
  31. package/docs/contracts.md +16 -2
  32. package/docs/live-proofs.md +5 -3
  33. package/examples/mobile-app/runner-manifests/evidence-provider.json +3 -3
  34. package/examples/mobile-app/scripts/asl-capture-profiler-provider.mjs +25 -0
  35. package/examples/runners/README.md +3 -3
  36. package/examples/runners/axe-accessibility-provider.json +2 -2
  37. package/examples/runners/script-accessibility-provider.json +2 -2
  38. package/examples/runners/script-memory-provider.json +2 -2
  39. package/examples/runners/script-network-provider.json +2 -2
  40. package/examples/runners/script-profiler-provider.json +2 -2
  41. package/package.json +11 -3
  42. package/schemas/manifest.schema.json +73 -3
  43. package/schemas/profiler.schema.json +243 -0
  44. package/schemas/runner-capabilities.schema.json +8 -2
  45. package/schemas/scenario.schema.json +18 -2
  46. package/templates/evidence-provider.json +3 -3
  47. package/templates/scripts/asl-capture-profiler-provider.mjs +20 -0
@@ -28,7 +28,10 @@ const { runIosSimctlCapture } = require('./ios-simctl');
28
28
  const { runAgentDeviceCapture } = require('./agent-device');
29
29
  const { loadAslLocalEnv, readStringArgOrEnv } = require('./local-env');
30
30
  const PROFILE_SESSION_CAPTURE_BOOTSTRAP_MS = 1000;
31
- const PROFILE_SESSION_CAPTURE_MAX_MS = 30000;
31
+ const PROFILE_SESSION_CAPTURE_COMMAND_OVERHEAD_MS = 250;
32
+ const PROFILE_SESSION_CAPTURE_BUFFER_MIN_MS = 2000;
33
+ const PROFILE_SESSION_CAPTURE_BUFFER_RATIO = 0.2;
34
+ const PROFILE_SESSION_CAPTURE_MAX_MS = 10 * 60 * 1000;
32
35
  const DEFAULT_IOS_PROFILE_SESSION_STORAGE_KEY = 'agent-scenario-loop.profile-session.1';
33
36
  const DEFAULT_IOS_PROFILE_COMMAND_STORAGE_KEY = 'agent-scenario-loop.profile-commands.1';
34
37
  const DEFAULT_IOS_PROFILE_EVENT_STORAGE_KEY = 'agent-scenario-loop.profile-events.1';
@@ -171,7 +174,7 @@ function resolveIosConflictingBundleIds(config) {
171
174
  * @param {{config: Record<string, unknown>, action: 'start' | 'command', scenario: string, runId: string, command?: string}} options
172
175
  * @returns {string}
173
176
  */
174
- function buildProfileSessionUrl({ action, command, commandId, config, queueId, runId, scenario, sequence, waitForMilestone, waitTimeoutMs, }) {
177
+ function buildProfileSessionUrl({ action, command, commandId, config, queueId, runId, scenario, sequence, waitForMilestone, waitMs, waitTimeoutMs, }) {
175
178
  const scheme = typeof config.app?.profileSessionScheme === 'string'
176
179
  ? config.app.profileSessionScheme
177
180
  : typeof config.app?.scheme === 'string'
@@ -192,6 +195,9 @@ function buildProfileSessionUrl({ action, command, commandId, config, queueId, r
192
195
  if (waitForMilestone) {
193
196
  params.set('waitForMilestone', waitForMilestone);
194
197
  }
198
+ if (typeof waitMs === 'number') {
199
+ params.set('waitMs', String(waitMs));
200
+ }
195
201
  if (typeof waitTimeoutMs === 'number') {
196
202
  params.set('waitTimeoutMs', String(waitTimeoutMs));
197
203
  }
@@ -215,24 +221,49 @@ function readStepWaitMs(step) {
215
221
  return readPositiveInteger(step.timeoutMs, 0);
216
222
  }
217
223
  /**
218
- * Derives a storage-backed profile capture window from scenario waits and cycles.
224
+ * Reads wait time from a profile-session command.
225
+ *
226
+ * @param {IosSimctlProfileCommand} command
227
+ * @returns {number}
228
+ */
229
+ function readProfileCommandWindowMs(command) {
230
+ return readPositiveInteger(command.waitMs, 0) +
231
+ readPositiveInteger(command.waitTimeoutMs, 0) +
232
+ PROFILE_SESSION_CAPTURE_COMMAND_OVERHEAD_MS;
233
+ }
234
+ /**
235
+ * Reads execution-plan waits that are not already attached to a profile-session command.
219
236
  *
220
237
  * @param {Record<string, unknown>} scenario
221
238
  * @returns {number}
222
239
  */
223
- function deriveProfileSessionCaptureWaitMs(scenario) {
240
+ function readUnattachedExecutionWaitMs(scenario) {
224
241
  const executionPlan = buildScenarioExecutionPlan(scenario);
225
242
  const iterations = readScenarioIterationCount(scenario);
226
- const perIterationWaitMs = executionPlan.steps.reduce((total, step) => {
227
- if (step.kind === 'command') {
228
- return total + readStepWaitMs(step);
243
+ const perIterationWaitMs = executionPlan.steps.reduce((total, step, index) => {
244
+ if (step.portMethod !== 'waitForTruthEvent') {
245
+ return total;
229
246
  }
230
- if (step.portMethod === 'waitForTruthEvent') {
231
- return total + readPositiveInteger(step.timeoutMs, 0);
247
+ const previousStep = executionPlan.steps[index - 1];
248
+ if (previousStep?.portMethod === 'executeStep') {
249
+ return total;
232
250
  }
233
- return total;
251
+ return total + readPositiveInteger(step.timeoutMs, 0);
234
252
  }, 0);
235
- const derivedWaitMs = PROFILE_SESSION_CAPTURE_BOOTSTRAP_MS + (perIterationWaitMs * iterations);
253
+ return perIterationWaitMs * iterations;
254
+ }
255
+ /**
256
+ * Derives a storage-backed profile capture window from scenario waits, command gates, and cycles.
257
+ *
258
+ * @param {Record<string, unknown>} scenario
259
+ * @returns {number}
260
+ */
261
+ function deriveProfileSessionCaptureWaitMs(scenario) {
262
+ const commands = resolveIosSimctlProfileCommands(scenario);
263
+ const commandWindowMs = commands.reduce((total, command) => total + readProfileCommandWindowMs(command), 0);
264
+ const executionWindowMs = commandWindowMs + readUnattachedExecutionWaitMs(scenario);
265
+ const bufferMs = Math.max(PROFILE_SESSION_CAPTURE_BUFFER_MIN_MS, Math.ceil(executionWindowMs * PROFILE_SESSION_CAPTURE_BUFFER_RATIO));
266
+ const derivedWaitMs = PROFILE_SESSION_CAPTURE_BOOTSTRAP_MS + executionWindowMs + bufferMs;
236
267
  return Math.min(Math.max(derivedWaitMs, PROFILE_SESSION_CAPTURE_BOOTSTRAP_MS), PROFILE_SESSION_CAPTURE_MAX_MS);
237
268
  }
238
269
  /**
@@ -271,16 +302,182 @@ function resolveExecutionPlanProfileCommands(scenario) {
271
302
  waitMs: readStepWaitMs(step),
272
303
  ...(nextStep?.portMethod === 'waitForTruthEvent' && typeof nextStep.milestone === 'string'
273
304
  ? {
274
- waitForMilestone: nextStep.milestone,
305
+ waitForMilestone: resolveMilestoneEventName(scenario, nextStep.milestone),
275
306
  waitTimeoutMs: readPositiveInteger(nextStep.timeoutMs, 0),
276
307
  }
277
308
  : {}),
278
309
  });
279
310
  }
280
- return Array.from({ length: repeat }).flatMap((_, iteration) => commands.map((command, commandIndex) => ({
311
+ return expandProfileCommandCycles(scenario, commands, repeat);
312
+ }
313
+ /**
314
+ * Returns true when a command is part of the setup prefix that establishes app readiness before repeated cycle work.
315
+ *
316
+ * @param {Record<string, unknown>} scenario
317
+ * @param {IosSimctlProfileCommand} command
318
+ * @returns {boolean}
319
+ */
320
+ function isReadinessSetupProfileCommand(scenario, command) {
321
+ if (typeof command.waitForMilestone !== 'string') {
322
+ return false;
323
+ }
324
+ const readyEvent = resolveScenarioReadinessEvent(scenario);
325
+ return typeof readyEvent === 'string' && command.waitForMilestone === readyEvent;
326
+ }
327
+ /**
328
+ * Reads a string id list from scenario cycles metadata.
329
+ *
330
+ * @param {unknown} value
331
+ * @returns {Set<string>}
332
+ */
333
+ function readCycleStepIdSet(value) {
334
+ return new Set(Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : []);
335
+ }
336
+ /**
337
+ * Resolves the milestone ids that represent measured cycle boundaries.
338
+ *
339
+ * @param {Record<string, unknown>} scenario
340
+ * @returns {Set<string>}
341
+ */
342
+ function resolveMeasuredCycleMilestoneEvents(scenario) {
343
+ const milestones = new Set();
344
+ for (const budget of Array.isArray(scenario.budgets) ? scenario.budgets : []) {
345
+ if (!budget || typeof budget !== 'object' || budget.source !== 'milestone') {
346
+ continue;
347
+ }
348
+ if (typeof budget.fromMilestone === 'string') {
349
+ milestones.add(resolveMilestoneEventName(scenario, budget.fromMilestone));
350
+ }
351
+ if (typeof budget.toMilestone === 'string') {
352
+ milestones.add(resolveMilestoneEventName(scenario, budget.toMilestone));
353
+ }
354
+ }
355
+ return milestones;
356
+ }
357
+ /**
358
+ * Resolves how many leading commands are setup-only before repeated cycle work.
359
+ *
360
+ * @param {Record<string, unknown>} scenario
361
+ * @param {IosSimctlProfileCommand[]} commands
362
+ * @returns {number}
363
+ */
364
+ function resolveSetupCommandCount(scenario, commands) {
365
+ const explicitSetupStepIds = readCycleStepIdSet(scenario.cycles?.setupStepIds);
366
+ if (explicitSetupStepIds.size > 0) {
367
+ let count = 0;
368
+ for (const command of commands) {
369
+ if (!command.commandId || !explicitSetupStepIds.has(command.commandId)) {
370
+ break;
371
+ }
372
+ count += 1;
373
+ }
374
+ return count;
375
+ }
376
+ const explicitBodyStepIds = readCycleStepIdSet(scenario.cycles?.bodyStepIds);
377
+ if (explicitBodyStepIds.size > 0) {
378
+ const firstBodyIndex = commands.findIndex((command) => (typeof command.commandId === 'string' && explicitBodyStepIds.has(command.commandId)));
379
+ return firstBodyIndex > 0 ? firstBodyIndex : 0;
380
+ }
381
+ let readinessSetupCommandCount = 0;
382
+ for (const command of commands) {
383
+ if (!isReadinessSetupProfileCommand(scenario, command)) {
384
+ break;
385
+ }
386
+ readinessSetupCommandCount += 1;
387
+ }
388
+ if (readinessSetupCommandCount > 0) {
389
+ return readinessSetupCommandCount;
390
+ }
391
+ const measuredMilestones = resolveMeasuredCycleMilestoneEvents(scenario);
392
+ if (measuredMilestones.size === 0) {
393
+ return 0;
394
+ }
395
+ const firstMeasuredCommandIndex = commands.findIndex((command) => (typeof command.waitForMilestone === 'string' && measuredMilestones.has(command.waitForMilestone)));
396
+ return firstMeasuredCommandIndex > 0 ? firstMeasuredCommandIndex : 0;
397
+ }
398
+ /**
399
+ * Expands commands so setup/readiness commands execute once while cycle-body commands repeat.
400
+ *
401
+ * @param {Record<string, unknown>} scenario
402
+ * @param {IosSimctlProfileCommand[]} commands
403
+ * @param {number} repeat
404
+ * @returns {IosSimctlProfileCommand[]}
405
+ */
406
+ function expandProfileCommandCycles(scenario, commands, repeat) {
407
+ const setupCommandCount = resolveSetupCommandCount(scenario, commands);
408
+ const setupCommands = commands.slice(0, setupCommandCount);
409
+ const cycleCommands = commands.slice(setupCommandCount);
410
+ const expandedCommands = cycleCommands.length === 0
411
+ ? setupCommands
412
+ : [
413
+ ...setupCommands,
414
+ ...Array.from({ length: repeat }).flatMap(() => cycleCommands),
415
+ ];
416
+ return expandedCommands.map((command, index) => ({
281
417
  ...command,
282
- sequence: (iteration * commands.length) + commandIndex + 1,
283
- })));
418
+ sequence: index + 1,
419
+ }));
420
+ }
421
+ /**
422
+ * Resolves a portable milestone id to the app truth event that releases command sequencing.
423
+ *
424
+ * @param {Record<string, unknown>} scenario
425
+ * @param {string} milestone
426
+ * @returns {string}
427
+ */
428
+ function resolveMilestoneEventName(scenario, milestone) {
429
+ const milestoneEntry = Array.isArray(scenario.milestones)
430
+ ? scenario.milestones.find((entry) => entry?.id === milestone)
431
+ : undefined;
432
+ if (typeof milestoneEntry?.event === 'string' && milestoneEntry.event.length > 0) {
433
+ return milestoneEntry.event;
434
+ }
435
+ const metricEvent = scenario.metricEvents?.[milestone];
436
+ return typeof metricEvent === 'string' && metricEvent.length > 0 ? metricEvent : milestone;
437
+ }
438
+ /**
439
+ * Resolves the scenario truth event that represents initial app readiness.
440
+ *
441
+ * @param {Record<string, unknown>} scenario
442
+ * @returns {string | null}
443
+ */
444
+ function resolveScenarioReadinessEvent(scenario) {
445
+ const explicitReadyEvent = scenario.truthEvents?.ready?.event;
446
+ if (typeof explicitReadyEvent === 'string' && explicitReadyEvent.length > 0) {
447
+ return explicitReadyEvent;
448
+ }
449
+ const milestoneEntry = Array.isArray(scenario.milestones)
450
+ ? scenario.milestones.find((entry) => (String(entry?.event ?? '').includes('ready')))
451
+ : undefined;
452
+ return typeof milestoneEntry?.event === 'string' && milestoneEntry.event.length > 0
453
+ ? milestoneEntry.event
454
+ : null;
455
+ }
456
+ /**
457
+ * Applies wait gates from the normalized execution plan to platform-declared commands.
458
+ *
459
+ * @param {Record<string, unknown>} scenario
460
+ * @param {IosSimctlProfileCommand[]} commands
461
+ * @returns {IosSimctlProfileCommand[]}
462
+ */
463
+ function applyExecutionPlanCommandGates(scenario, commands) {
464
+ const planCommands = resolveExecutionPlanProfileCommands(scenario);
465
+ if (planCommands.length === 0) {
466
+ return commands;
467
+ }
468
+ return commands.map((command, index) => {
469
+ const planCommand = planCommands[index];
470
+ if (!planCommand || typeof planCommand.waitForMilestone !== 'string' || typeof command.waitForMilestone === 'string') {
471
+ return command;
472
+ }
473
+ return {
474
+ ...command,
475
+ waitForMilestone: planCommand.waitForMilestone,
476
+ ...(typeof command.waitTimeoutMs === 'number'
477
+ ? {}
478
+ : { waitTimeoutMs: readPositiveInteger(planCommand.waitTimeoutMs, 0) }),
479
+ };
480
+ });
284
481
  }
285
482
  /**
286
483
  * Expands scenario-declared iOS commands for a simctl capture profile session.
@@ -316,7 +513,7 @@ function resolveIosSimctlProfileCommands(scenario) {
316
513
  });
317
514
  }
318
515
  }
319
- return commands;
516
+ return applyExecutionPlanCommandGates(scenario, commands);
320
517
  }
321
518
  /**
322
519
  * Returns true when the scenario asks iOS simctl capture to preserve a screenshot.
@@ -476,6 +673,7 @@ async function runProfileIos(args, options = {}) {
476
673
  ...(typeof profileCommand.queueId === 'string' ? { queueId: profileCommand.queueId } : {}),
477
674
  ...(typeof profileCommand.sequence === 'number' ? { sequence: profileCommand.sequence } : {}),
478
675
  ...(typeof profileCommand.waitForMilestone === 'string' ? { waitForMilestone: profileCommand.waitForMilestone } : {}),
676
+ ...(typeof profileCommand.waitMs === 'number' ? { waitMs: profileCommand.waitMs } : {}),
479
677
  ...(typeof profileCommand.waitTimeoutMs === 'number' ? { waitTimeoutMs: profileCommand.waitTimeoutMs } : {}),
480
678
  }),
481
679
  waitMs: profileCommand.waitMs,
@@ -514,6 +712,7 @@ async function runProfileIos(args, options = {}) {
514
712
  ...(typeof profileCommand.queueId === 'string' ? { queueId: profileCommand.queueId } : {}),
515
713
  ...(typeof profileCommand.sequence === 'number' ? { sequence: profileCommand.sequence } : {}),
516
714
  ...(typeof profileCommand.waitForMilestone === 'string' ? { waitForMilestone: profileCommand.waitForMilestone } : {}),
715
+ ...(typeof profileCommand.waitMs === 'number' ? { waitMs: profileCommand.waitMs } : {}),
517
716
  ...(typeof profileCommand.waitTimeoutMs === 'number' ? { waitTimeoutMs: profileCommand.waitTimeoutMs } : {}),
518
717
  })),
519
718
  runId,
@@ -580,7 +779,11 @@ async function runProfileIos(args, options = {}) {
580
779
  : baseProfileArgs;
581
780
  const lifecyclePhase = resolveManifestLifecyclePhase(args);
582
781
  const environmentSource = agentDeviceCapture ? 'agent-device' : 'simctl';
583
- const lifecycleArtifact = simctlCapture ? 'raw/ios-simctl-log.txt' : 'raw/interaction.log';
782
+ const copiedSimctlLogArtifact = simctlCapture &&
783
+ !fs.existsSync(path.join(simctlCapture.runDir, 'raw', 'ios-profile-events.log')) &&
784
+ fs.existsSync(path.join(simctlCapture.runDir, 'raw', 'ios-simctl-log.txt'))
785
+ ? 'raw/ios-simctl-log.txt'
786
+ : undefined;
584
787
  return runProfileMobile(profileArgs, {
585
788
  commandTransport: agentDeviceCapture
586
789
  ? 'agent-device'
@@ -596,13 +799,13 @@ async function runProfileIos(args, options = {}) {
596
799
  value: 'foreground',
597
800
  evidence: 'asserted',
598
801
  source: environmentSource,
599
- artifact: lifecycleArtifact,
802
+ ...(copiedSimctlLogArtifact ? { artifact: copiedSimctlLogArtifact } : {}),
600
803
  },
601
804
  lifecyclePhase: {
602
805
  value: 'foreground',
603
806
  evidence: 'asserted',
604
807
  source: environmentSource,
605
- artifact: lifecycleArtifact,
808
+ ...(copiedSimctlLogArtifact ? { artifact: copiedSimctlLogArtifact } : {}),
606
809
  },
607
810
  },
608
811
  environmentPreconditions: {
@@ -615,7 +818,7 @@ async function runProfileIos(args, options = {}) {
615
818
  value: lifecyclePhase,
616
819
  evidence: 'asserted',
617
820
  source: environmentSource,
618
- artifact: lifecycleArtifact,
821
+ ...(copiedSimctlLogArtifact ? { artifact: copiedSimctlLogArtifact } : {}),
619
822
  },
620
823
  },
621
824
  interactionDriver: agentDeviceCapture ? 'agent-device' : 'ios-simctl',
@@ -35,6 +35,25 @@ type ProviderEvidenceKind = 'accessibility' | 'logs' | 'profiler';
35
35
  type SignalEvidenceKind = 'js' | 'memory' | 'network';
36
36
  type EvidenceChannel = 'capture' | 'provider' | 'signal';
37
37
  type EvidenceKind = CaptureEvidenceKind | ProviderEvidenceKind | SignalEvidenceKind;
38
+ type DiagnosticStatus = 'captured' | 'not_requested' | 'not_supported' | 'unavailable' | 'failed' | 'skipped' | 'missing';
39
+ type DiagnosticKind = EvidenceKind | 'logs';
40
+ type DiagnosticInventoryEntry = {
41
+ kind: DiagnosticKind;
42
+ status: DiagnosticStatus;
43
+ required: boolean;
44
+ name?: string;
45
+ provider?: string;
46
+ runnerId?: string;
47
+ path?: string;
48
+ reason?: string;
49
+ nextAction?: string;
50
+ sidecarRoot?: string;
51
+ evidenceDependency?: {
52
+ kind: string;
53
+ root?: 'run' | 'sidecar';
54
+ path: string;
55
+ };
56
+ };
38
57
  type EvidenceAttachment = {
39
58
  channel: EvidenceChannel;
40
59
  completenessStatus: 'complete';
@@ -43,6 +62,7 @@ type EvidenceAttachment = {
43
62
  kind: EvidenceKind;
44
63
  manifestPath: string;
45
64
  redactionStatus: 'not-redacted';
65
+ required: boolean;
46
66
  sha256: string;
47
67
  sourcePath: string;
48
68
  sourceFileName: string;
@@ -54,6 +74,8 @@ type EvidenceAttachmentInput = {
54
74
  destinationPath: string;
55
75
  kind: EvidenceKind;
56
76
  manifestPath: string;
77
+ providerId?: string;
78
+ required?: boolean;
57
79
  sourcePath: string;
58
80
  };
59
81
  type AttachedEvidence = {
@@ -70,6 +92,7 @@ type ProviderCommandOutput = {
70
92
  channel: EvidenceChannel;
71
93
  kind: EvidenceKind;
72
94
  path: string;
95
+ required?: boolean;
73
96
  };
74
97
  type ProviderCommand = {
75
98
  args?: string[];
@@ -78,7 +101,7 @@ type ProviderCommand = {
78
101
  env?: Record<string, string>;
79
102
  id: string;
80
103
  outputs: ProviderCommandOutput[];
81
- phase: 'prepare' | 'startWindow' | 'capture' | 'stopWindow' | 'finalize';
104
+ phase: 'prepare' | 'startWindow' | 'capture' | 'stopWindow' | 'afterCapture' | 'postRun' | 'finalize';
82
105
  };
83
106
  type ProviderCommandFailure = {
84
107
  commandId: string;
@@ -139,13 +162,18 @@ declare function resolveAttachedEvidence({ args, layout, providerInputs, }: {
139
162
  /**
140
163
  * Builds scenario health from profile metrics.
141
164
  *
142
- * @param {{scenario: Record<string, unknown>, runId: string, metrics: Record<string, unknown>}} options
165
+ * @param {{scenario: Record<string, unknown>, runId: string, metrics: Record<string, unknown>, diagnostics?: DiagnosticInventoryEntry[], profileEventCount?: number, profileSessionEntryCount?: number, commandTransport?: string, sessionEntries?: Record<string, unknown>[]}} options
143
166
  * @returns {Record<string, unknown>}
144
167
  */
145
- declare function buildProfileHealth({ scenario, runId, metrics, }: {
168
+ declare function buildProfileHealth({ scenario, runId, metrics, diagnostics, profileEventCount, profileSessionEntryCount, commandTransport, sessionEntries, }: {
146
169
  scenario: Record<string, any>;
147
170
  runId: string;
148
171
  metrics: Record<string, any>;
172
+ diagnostics?: DiagnosticInventoryEntry[];
173
+ profileEventCount?: number;
174
+ profileSessionEntryCount?: number;
175
+ commandTransport?: string;
176
+ sessionEntries?: Record<string, any>[];
149
177
  }): Record<string, unknown>;
150
178
  /**
151
179
  * Builds failed scenario health from evidence-provider command failures.