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.
- package/app/profile-session.ts +263 -17
- package/dist/core/artifact-contract.d.ts +6 -4
- package/dist/core/artifact-contract.js +164 -15
- package/dist/core/schema-validator.d.ts +1 -0
- package/dist/core/schema-validator.js +1 -0
- package/dist/runner/android-adb-driver.d.ts +7 -2
- package/dist/runner/android-adb-driver.js +7 -1
- package/dist/runner/android-adb.d.ts +40 -5
- package/dist/runner/android-adb.js +1046 -664
- package/dist/runner/ios-simctl.d.ts +1 -0
- package/dist/runner/ios-simctl.js +1 -0
- package/dist/runner/profile-android.d.ts +11 -1
- package/dist/runner/profile-android.js +230 -16
- package/dist/runner/profile-ios.d.ts +3 -2
- package/dist/runner/profile-ios.js +223 -20
- package/dist/runner/profile-mobile.d.ts +31 -3
- package/dist/runner/profile-mobile.js +793 -20
- package/dist/runner/validate-project.js +3 -0
- package/dist/scripts/consumer-rehearsal.d.ts +119 -0
- package/dist/scripts/consumer-rehearsal.js +757 -0
- package/dist/scripts/downstream-local-package-gate.d.ts +2 -0
- package/dist/scripts/downstream-local-package-gate.js +264 -0
- package/dist/scripts/package-smoke.d.ts +96 -0
- package/dist/scripts/package-smoke.js +2282 -0
- package/dist/scripts/release-readiness.d.ts +2 -0
- package/dist/scripts/release-readiness.js +520 -0
- package/docs/adapters.md +3 -1
- package/docs/api.md +2 -2
- package/docs/authoring.md +34 -2
- package/docs/consumer-rehearsal.md +27 -1
- package/docs/contracts.md +16 -2
- package/docs/live-proofs.md +5 -3
- package/examples/mobile-app/runner-manifests/evidence-provider.json +3 -3
- package/examples/mobile-app/scripts/asl-capture-profiler-provider.mjs +25 -0
- package/examples/runners/README.md +3 -3
- package/examples/runners/axe-accessibility-provider.json +2 -2
- package/examples/runners/script-accessibility-provider.json +2 -2
- package/examples/runners/script-memory-provider.json +2 -2
- package/examples/runners/script-network-provider.json +2 -2
- package/examples/runners/script-profiler-provider.json +2 -2
- package/package.json +11 -3
- package/schemas/manifest.schema.json +73 -3
- package/schemas/profiler.schema.json +243 -0
- package/schemas/runner-capabilities.schema.json +8 -2
- package/schemas/scenario.schema.json +18 -2
- package/templates/evidence-provider.json +3 -3
- 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
|
|
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
|
-
*
|
|
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
|
|
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.
|
|
228
|
-
return total
|
|
243
|
+
const perIterationWaitMs = executionPlan.steps.reduce((total, step, index) => {
|
|
244
|
+
if (step.portMethod !== 'waitForTruthEvent') {
|
|
245
|
+
return total;
|
|
229
246
|
}
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
802
|
+
...(copiedSimctlLogArtifact ? { artifact: copiedSimctlLogArtifact } : {}),
|
|
600
803
|
},
|
|
601
804
|
lifecyclePhase: {
|
|
602
805
|
value: 'foreground',
|
|
603
806
|
evidence: 'asserted',
|
|
604
807
|
source: environmentSource,
|
|
605
|
-
artifact:
|
|
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:
|
|
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.
|