@agentworkforce/workload-router 0.5.0 → 0.5.1

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/dist/index.js CHANGED
@@ -1,7 +1,4 @@
1
- import { spawn } from 'node:child_process';
2
- import { createHash } from 'node:crypto';
3
- import { resolve as resolvePath } from 'node:path';
4
- import { frontendImplementer, codeReviewer, architecturePlanner, requirementsAnalyst, debuggerPersona, securityReviewer, technicalWriter, verifierPersona, testStrategist, tddGuard, flakeHunter, opencodeWorkflowSpecialist, npmProvenancePublisher, cloudSandboxInfra, sageSlackEgressMigrator, sageProactiveRewirer, cloudSlackProxyGuard, agentRelayE2eConductor, capabilityDiscoverer, posthogAgent, personaMaker, antiSlopAuditor } from './generated/personas.js';
1
+ import { frontendImplementer, codeReviewer, architecturePlanner, requirementsAnalyst, debuggerPersona, securityReviewer, technicalWriter, verifierPersona, testStrategist, tddGuard, flakeHunter, opencodeWorkflowSpecialist, npmProvenancePublisher, cloudSandboxInfra, sageSlackEgressMigrator, sageProactiveRewirer, cloudSlackProxyGuard, agentRelayE2eConductor, capabilityDiscoverer, npmPackageBundlerGuard, posthogAgent, personaMaker, antiSlopAuditor, apiContractReviewer, dockerStackWrangler, e2eValidator, integrationTestAuthor, agentRelayWorkflow, relayOrchestrator } from './generated/personas.js';
5
2
  import defaultRoutingProfileJson from '../routing-profiles/default.json' with { type: 'json' };
6
3
  export const HARNESS_VALUES = ['opencode', 'codex', 'claude'];
7
4
  export const PERSONA_TIERS = ['best', 'best-value', 'minimum'];
@@ -36,9 +33,16 @@ export const PERSONA_INTENTS = [
36
33
  'cloud-slack-proxy-guard',
37
34
  'sage-cloud-e2e-conduction',
38
35
  'capability-discovery',
36
+ 'npm-package-compat',
39
37
  'posthog',
40
38
  'persona-authoring',
41
- 'slop-audit'
39
+ 'agent-relay-workflow',
40
+ 'slop-audit',
41
+ 'api-contract-review',
42
+ 'local-stack-orchestration',
43
+ 'e2e-validation',
44
+ 'write-integration-tests',
45
+ 'relay-orchestrator'
42
46
  ];
43
47
  export const PERMISSION_MODES = [
44
48
  'default',
@@ -68,24 +72,6 @@ export const HARNESS_SKILL_TARGETS = {
68
72
  codex: { asFlag: 'codex', dir: '.agents/skills' },
69
73
  opencode: { asFlag: 'opencode', dir: '.skills' }
70
74
  };
71
- export class PersonaExecutionError extends Error {
72
- result;
73
- cause;
74
- constructor(message, result, cause) {
75
- super(message, cause === undefined ? undefined : { cause });
76
- this.name = 'PersonaExecutionError';
77
- this.result = result;
78
- this.cause = cause;
79
- }
80
- }
81
- class CapturedCommandError extends Error {
82
- capture;
83
- constructor(message, capture) {
84
- super(message);
85
- this.name = 'CapturedCommandError';
86
- this.capture = capture;
87
- }
88
- }
89
75
  const PRPM_URL_RE = /^https?:\/\/prpm\.dev\/packages\/([^/\s?#]+)\/([^/\s?#]+)\/?(?:[?#].*)?$/i;
90
76
  const PRPM_BARE_REF_RE = /^([^/\s]+)\/([^/\s]+)$/;
91
77
  function lastSegment(ref) {
@@ -241,7 +227,7 @@ export function materializeSkills(skills, harness, options = {}) {
241
227
  const installedDir = installRoot !== undefined ? `${installRoot}/${repoRelativeDir}` : repoRelativeDir;
242
228
  // When the plan stages into `installRoot`, cleanup targets the whole
243
229
  // session dir (handled at plan level in buildCleanupArtifacts). Leave
244
- // per-skill cleanupPaths empty so Mode B callers running individual
230
+ // per-skill cleanupPaths empty so callers running individual
245
231
  // install.cleanupPaths don't accidentally remove unrelated things.
246
232
  const cleanupPaths = installRoot !== undefined
247
233
  ? Object.freeze([])
@@ -381,55 +367,6 @@ function buildCleanupArtifacts(plan) {
381
367
  cleanupCommandString
382
368
  };
383
369
  }
384
- function buildExecutionTask(systemPrompt, task, inputs) {
385
- const sections = [`System Instructions:\n${systemPrompt.trim()}`, `Task:\n${task.trim()}`];
386
- if (inputs && Object.keys(inputs).length > 0) {
387
- sections.push(`Additional Inputs (JSON):\n${JSON.stringify(inputs, null, 2)}`);
388
- }
389
- return sections.join('\n\n');
390
- }
391
- function hash8(value) {
392
- return createHash('sha256').update(value).digest('hex').slice(0, 8);
393
- }
394
- function sanitizeExecutionName(value) {
395
- const sanitized = value
396
- .trim()
397
- .toLowerCase()
398
- .replace(/[^a-z0-9_.-]+/g, '-')
399
- .replace(/^-+|-+$/g, '');
400
- return sanitized || `persona-${hash8(value)}`;
401
- }
402
- function createDeferred() {
403
- let settled = false;
404
- let resolveFn;
405
- let rejectFn;
406
- const promise = new Promise((resolve, reject) => {
407
- resolveFn = (value) => {
408
- settled = true;
409
- resolve(value);
410
- };
411
- rejectFn = (reason) => {
412
- settled = true;
413
- reject(reason);
414
- };
415
- });
416
- return {
417
- promise,
418
- resolve: resolveFn,
419
- reject: rejectFn,
420
- get settled() {
421
- return settled;
422
- }
423
- };
424
- }
425
- function createAbortError(message) {
426
- const error = new Error(message);
427
- error.name = 'AbortError';
428
- return error;
429
- }
430
- function isTimeoutError(message) {
431
- return typeof message === 'string' && /timed out/i.test(message);
432
- }
433
370
  function deepFreeze(value) {
434
371
  if (value === null || value === undefined || typeof value !== 'object') {
435
372
  return value;
@@ -445,177 +382,6 @@ function deepFreeze(value) {
445
382
  }
446
383
  return Object.freeze(value);
447
384
  }
448
- function linkAbortSignal(signal, controller) {
449
- if (!signal) {
450
- return () => { };
451
- }
452
- if (signal.aborted) {
453
- controller.abort(signal.reason);
454
- return () => { };
455
- }
456
- const onAbort = () => controller.abort(signal.reason);
457
- signal.addEventListener('abort', onAbort, { once: true });
458
- return () => signal.removeEventListener('abort', onAbort);
459
- }
460
- async function runCapturedCommand(options) {
461
- const { command, args, cwd, env, timeoutMs, signal, onSpawn, onProgress } = options;
462
- return new Promise((resolve, reject) => {
463
- if (signal?.aborted) {
464
- reject(createAbortError('Execution aborted before the process started'));
465
- return;
466
- }
467
- const child = spawn(command, args, {
468
- cwd,
469
- env,
470
- stdio: ['ignore', 'pipe', 'pipe']
471
- });
472
- let stdout = '';
473
- let stderr = '';
474
- let timedOut = false;
475
- let timeoutId;
476
- let killId;
477
- let abortDelayId;
478
- const cleanup = () => {
479
- if (timeoutId) {
480
- clearTimeout(timeoutId);
481
- }
482
- if (killId) {
483
- clearTimeout(killId);
484
- }
485
- if (abortDelayId) {
486
- clearTimeout(abortDelayId);
487
- }
488
- if (signal && abortHandler) {
489
- signal.removeEventListener('abort', abortHandler);
490
- }
491
- };
492
- const terminate = () => {
493
- child.kill('SIGTERM');
494
- killId = setTimeout(() => child.kill('SIGKILL'), 5_000);
495
- killId.unref?.();
496
- };
497
- const abortHandler = () => {
498
- if (stdout.length === 0 && stderr.length === 0) {
499
- abortDelayId = setTimeout(() => {
500
- abortDelayId = undefined;
501
- terminate();
502
- }, 15);
503
- abortDelayId.unref?.();
504
- return;
505
- }
506
- terminate();
507
- };
508
- if (signal) {
509
- signal.addEventListener('abort', abortHandler, { once: true });
510
- }
511
- if (timeoutMs !== undefined) {
512
- timeoutId = setTimeout(() => {
513
- timedOut = true;
514
- child.kill('SIGTERM');
515
- killId = setTimeout(() => child.kill('SIGKILL'), 5_000);
516
- killId.unref?.();
517
- }, timeoutMs);
518
- timeoutId.unref?.();
519
- }
520
- child.stdout?.on('data', (chunk) => {
521
- const text = chunk.toString();
522
- stdout += text;
523
- onProgress?.({ stream: 'stdout', text });
524
- });
525
- child.stderr?.on('data', (chunk) => {
526
- const text = chunk.toString();
527
- stderr += text;
528
- onProgress?.({ stream: 'stderr', text });
529
- });
530
- onSpawn?.();
531
- child.once('error', (error) => {
532
- cleanup();
533
- reject(error);
534
- });
535
- child.once('close', (code, exitSignal) => {
536
- cleanup();
537
- const capture = {
538
- stdout,
539
- stderr,
540
- exitCode: code,
541
- exitSignal: exitSignal ?? null
542
- };
543
- if (signal?.aborted) {
544
- const error = createAbortError('Execution cancelled');
545
- Object.assign(error, { capture });
546
- reject(error);
547
- return;
548
- }
549
- if (timedOut) {
550
- reject(new CapturedCommandError(`Command timed out after ${timeoutMs ?? 'unknown'}ms`, capture));
551
- return;
552
- }
553
- resolve(capture);
554
- });
555
- });
556
- }
557
- function createLocalExecutor(stepCaptures, options, buildCommand) {
558
- const execute = async (stepName, command, args, cwd, timeoutMs, ignoreExitCode = false) => {
559
- const partialCapture = {
560
- stdout: '',
561
- stderr: '',
562
- exitCode: null,
563
- exitSignal: null
564
- };
565
- try {
566
- const capture = await runCapturedCommand({
567
- command,
568
- args,
569
- cwd,
570
- env: options.env,
571
- timeoutMs,
572
- signal: options.signal,
573
- onSpawn: () => {
574
- stepCaptures.set(stepName, { ...partialCapture });
575
- options.onStepSpawn?.(stepName);
576
- },
577
- onProgress: (chunk) => {
578
- if (chunk.stream === 'stdout') {
579
- partialCapture.stdout += chunk.text;
580
- }
581
- else {
582
- partialCapture.stderr += chunk.text;
583
- }
584
- stepCaptures.set(stepName, { ...partialCapture });
585
- options.onStepProgress?.(stepName, chunk);
586
- options.onProgress?.(chunk);
587
- }
588
- });
589
- stepCaptures.set(stepName, capture);
590
- if (!ignoreExitCode && capture.exitCode !== null && capture.exitCode !== 0) {
591
- throw new CapturedCommandError(`Step "${stepName}" exited with code ${capture.exitCode}`, capture);
592
- }
593
- return capture;
594
- }
595
- catch (error) {
596
- const capture = error instanceof CapturedCommandError ? error.capture : error.capture;
597
- if (capture) {
598
- stepCaptures.set(stepName, capture);
599
- }
600
- throw error;
601
- }
602
- };
603
- return {
604
- async executeAgentStep(step, agentDef, resolvedTask, timeoutMs) {
605
- const extraArgs = agentDef.constraints?.model ? ['--model', agentDef.constraints.model] : undefined;
606
- const [command, ...args] = buildCommand(agentDef.cli, extraArgs, resolvedTask);
607
- const capture = await execute(step.name, command, args, resolvePath(step.cwd ?? options.cwd), timeoutMs, agentDef.cli === 'opencode');
608
- return capture.stdout;
609
- },
610
- async executeDeterministicStep(step, resolvedCommand, cwd) {
611
- const capture = await execute(step.name, 'sh', ['-c', resolvedCommand], resolvePath(cwd), step.timeoutMs);
612
- return {
613
- output: capture.stdout,
614
- exitCode: capture.exitCode ?? 0
615
- };
616
- }
617
- };
618
- }
619
385
  function isObject(value) {
620
386
  return typeof value === 'object' && value !== null;
621
387
  }
@@ -884,9 +650,16 @@ export const personaCatalog = {
884
650
  'cloud-slack-proxy-guard': parsePersonaSpec(cloudSlackProxyGuard, 'cloud-slack-proxy-guard'),
885
651
  'sage-cloud-e2e-conduction': parsePersonaSpec(agentRelayE2eConductor, 'sage-cloud-e2e-conduction'),
886
652
  'capability-discovery': parsePersonaSpec(capabilityDiscoverer, 'capability-discovery'),
653
+ 'npm-package-compat': parsePersonaSpec(npmPackageBundlerGuard, 'npm-package-compat'),
887
654
  posthog: parsePersonaSpec(posthogAgent, 'posthog'),
888
655
  'persona-authoring': parsePersonaSpec(personaMaker, 'persona-authoring'),
889
- 'slop-audit': parsePersonaSpec(antiSlopAuditor, 'slop-audit')
656
+ 'agent-relay-workflow': parsePersonaSpec(agentRelayWorkflow, 'agent-relay-workflow'),
657
+ 'slop-audit': parsePersonaSpec(antiSlopAuditor, 'slop-audit'),
658
+ 'api-contract-review': parsePersonaSpec(apiContractReviewer, 'api-contract-review'),
659
+ 'local-stack-orchestration': parsePersonaSpec(dockerStackWrangler, 'local-stack-orchestration'),
660
+ 'e2e-validation': parsePersonaSpec(e2eValidator, 'e2e-validation'),
661
+ 'write-integration-tests': parsePersonaSpec(integrationTestAuthor, 'write-integration-tests'),
662
+ 'relay-orchestrator': parsePersonaSpec(relayOrchestrator, 'relay-orchestrator')
890
663
  };
891
664
  export const routingProfiles = {
892
665
  default: parseRoutingProfile(defaultRoutingProfileJson, 'routingProfiles.default')
@@ -925,68 +698,19 @@ export function resolvePersonaByTier(intent, tier = 'best-value') {
925
698
  }
926
699
  /**
927
700
  * Resolve a persona for `intent` and return a {@link PersonaContext}
928
- * bundling the resolved persona, grouped install metadata, and a
929
- * `sendMessage()` closure for running the persona against a task.
701
+ * bundling the resolved persona and grouped install metadata.
930
702
  *
931
703
  * **This is not a React hook.** The `use*` prefix is unfortunate — it is
932
704
  * a plain synchronous factory with no implicit state, no side effects,
933
705
  * and no rules-of-hooks constraints. Calling `usePersona(intent)` does
934
706
  * nothing but resolve routing config and pre-compute the install plan.
935
- * Nothing is installed, spawned, or written to disk until you call
936
- * `sendMessage()` (or run the install command yourself).
937
- *
938
- * See {@link PersonaContext} for the two usage modes (let `sendMessage()`
939
- * handle install vs. pre-stage install and pass `installSkills: false`)
940
- * and the double-install caveat.
707
+ * Nothing is installed, spawned, or written to disk until you run
708
+ * `install.commandString` yourself.
941
709
  *
942
710
  * @example
943
- * // Mode A let sendMessage() install skills and run the agent in one call.
944
- * // Only `status: 'completed'` resolves; non-zero exits / timeouts throw
945
- * // PersonaExecutionError and cancellation throws AbortError, both with
946
- * // the typed ExecuteResult attached as `err.result`.
947
- * const { sendMessage } = usePersona('npm-provenance');
948
- * try {
949
- * const result = await sendMessage('Set up npm trusted publishing for this repo', {
950
- * workingDirectory: '.',
951
- * timeoutSeconds: 600,
952
- * });
953
- * // result.status === 'completed' here
954
- * } catch (err) {
955
- * const execErr = err as Error & { result?: ExecuteResult };
956
- * console.error('persona run failed', execErr.result?.status, execErr.result?.stderr);
957
- * }
958
- *
959
- * @example
960
- * // Mode B — pre-stage install out-of-band (e.g. in a Dockerfile), then
961
- * // run at runtime without re-installing:
962
- * const { install, sendMessage } = usePersona('npm-provenance');
963
- * // build/CI step:
711
+ * const { selection, install } = usePersona('npm-provenance');
964
712
  * spawnSync(install.commandString, { shell: true, stdio: 'inherit' });
965
- * // runtime step:
966
- * const result = await sendMessage('Your task', {
967
- * workingDirectory: '.',
968
- * installSkills: false,
969
- * });
970
- *
971
- * @example
972
- * // Cancellation + streaming progress. Aborting causes `await run` to
973
- * // throw an AbortError with `err.result.status === 'cancelled'`, so
974
- * // wrap in try/catch if you plan to abort.
975
- * const abort = new AbortController();
976
- * const run = usePersona('npm-provenance').sendMessage('Your task', {
977
- * signal: abort.signal,
978
- * onProgress: ({ stream, text }) => process[stream].write(text),
979
- * });
980
- * run.runId.then((id) => console.log('workflow run id:', id));
981
- * // ...later:
982
- * abort.abort(); // or: run.cancel('user requested');
983
- * try {
984
- * const result = await run;
985
- * // result.status === 'completed'
986
- * } catch (err) {
987
- * const execErr = err as Error & { result?: ExecuteResult };
988
- * // execErr.name === 'AbortError' and execErr.result?.status === 'cancelled'
989
- * }
713
+ * // hand `selection` to your harness launcher of choice.
990
714
  *
991
715
  * @param intent The persona intent to resolve (e.g. `'npm-provenance'`).
992
716
  * @param options Optional overrides. `harness` forces a specific harness
@@ -1008,7 +732,7 @@ export function usePersona(intent, options = {}) {
1008
732
  * Same as {@link usePersona}, but takes a pre-resolved {@link PersonaSelection}
1009
733
  * instead of an intent. Use this when you have a selection produced outside
1010
734
  * the standard repo catalog — for example, a user-local persona override
1011
- * loaded from disk — and want the same install/sendMessage surface.
735
+ * loaded from disk.
1012
736
  */
1013
737
  export function useSelection(baseSelection, options = {}) {
1014
738
  const effectiveHarness = options.harness ?? baseSelection.runtime.harness;
@@ -1036,189 +760,9 @@ export function useSelection(baseSelection, options = {}) {
1036
760
  cleanupCommand,
1037
761
  cleanupCommandString
1038
762
  });
1039
- const sendMessage = (task, sendMessageOptions = {}) => {
1040
- const runId = createDeferred();
1041
- // The primary rejection path for any failure in sendMessage() is `resultPromise`
1042
- // (which the caller awaits via `await execution`). `runId.promise` is an
1043
- // auxiliary promise that mirrors the same rejection when early setup fails
1044
- // before the workflow has actually started. Callers are not required to
1045
- // consume `execution.runId`, so attach a no-op catch here to suppress
1046
- // the unhandled-rejection warning (and, under Node's default
1047
- // --unhandled-rejections=throw, an uncaught-exception crash) that would
1048
- // otherwise fire when both of those conditions hold simultaneously.
1049
- runId.promise.catch(() => { });
1050
- const abortController = new AbortController();
1051
- const unlinkAbort = linkAbortSignal(sendMessageOptions.signal, abortController);
1052
- const stepName = sanitizeExecutionName(sendMessageOptions.name ?? `${frozenSelection.personaId}-${hash8(task)}`);
1053
- const workflowName = `use-persona-${stepName}`;
1054
- const installStepName = `${stepName}-install-skills`;
1055
- const cleanupStepName = `${stepName}-cleanup-skills`;
1056
- const workingDirectory = resolvePath(sendMessageOptions.workingDirectory ?? process.cwd());
1057
- const timeoutMs = Math.max(1, Math.round((sendMessageOptions.timeoutSeconds ??
1058
- frozenSelection.runtime.harnessSettings.timeoutSeconds) * 1000));
1059
- const shouldInstallSkills = sendMessageOptions.installSkills !== false && frozenInstallPlan.installs.length > 0;
1060
- const stepCaptures = new Map();
1061
- let cancelReason;
1062
- let workflowRunId;
1063
- let runIdReadyTimer;
1064
- const resolveRunId = (value = workflowRunId) => {
1065
- if (runIdReadyTimer) {
1066
- clearTimeout(runIdReadyTimer);
1067
- runIdReadyTimer = undefined;
1068
- }
1069
- if (value && !runId.settled) {
1070
- runId.resolve(value);
1071
- }
1072
- };
1073
- const resultPromise = (async () => {
1074
- try {
1075
- const { InMemoryWorkflowDb, WorkflowRunner, buildCommand, workflow } = await import('@agent-relay/sdk/workflows');
1076
- const executor = createLocalExecutor(stepCaptures, {
1077
- cwd: workingDirectory,
1078
- env: { ...process.env, ...sendMessageOptions.env },
1079
- signal: abortController.signal,
1080
- onStepSpawn: (startedStepName) => {
1081
- if (startedStepName !== stepName || runId.settled || runIdReadyTimer) {
1082
- return;
1083
- }
1084
- runIdReadyTimer = setTimeout(() => resolveRunId(), 250);
1085
- runIdReadyTimer.unref?.();
1086
- },
1087
- onStepProgress: (progressStepName) => {
1088
- if (progressStepName === stepName) {
1089
- resolveRunId();
1090
- }
1091
- },
1092
- onProgress: sendMessageOptions.onProgress
1093
- }, buildCommand);
1094
- const runner = new WorkflowRunner({
1095
- cwd: workingDirectory,
1096
- db: new InMemoryWorkflowDb(),
1097
- executor
1098
- });
1099
- runner.on((event) => {
1100
- if (event.type === 'run:started') {
1101
- workflowRunId = event.runId;
1102
- }
1103
- if ((event.type === 'step:completed' || event.type === 'step:failed') &&
1104
- event.stepName === stepName) {
1105
- resolveRunId(event.runId);
1106
- }
1107
- });
1108
- const agentName = `${stepName}-agent`;
1109
- const builder = workflow(workflowName)
1110
- .description(`Ad-hoc persona execution for ${frozenSelection.personaId}`)
1111
- .pattern('dag')
1112
- .timeout(timeoutMs)
1113
- .trajectories(false)
1114
- .agent(agentName, {
1115
- cli: frozenSelection.runtime.harness,
1116
- model: frozenSelection.runtime.model,
1117
- role: frozenSelection.personaId,
1118
- preset: 'worker',
1119
- interactive: false,
1120
- timeoutMs
1121
- });
1122
- if (shouldInstallSkills) {
1123
- builder.step(installStepName, {
1124
- type: 'deterministic',
1125
- command: installCommandString,
1126
- cwd: workingDirectory,
1127
- timeoutMs,
1128
- captureOutput: true,
1129
- failOnError: true
1130
- });
1131
- }
1132
- builder.step(stepName, {
1133
- agent: agentName,
1134
- task: buildExecutionTask(frozenSelection.runtime.systemPrompt, task, sendMessageOptions.inputs),
1135
- cwd: workingDirectory,
1136
- timeoutMs,
1137
- verification: { type: 'exit_code', value: '0' },
1138
- ...(shouldInstallSkills ? { dependsOn: [installStepName] } : {})
1139
- });
1140
- // Post-agent cleanup: removes the ephemeral skill artifact paths the
1141
- // provider scattered during the install step. Only runs when this
1142
- // sendMessage owns the install (Mode A) AND the agent step completed
1143
- // — if the agent step fails or is skipped, the dag runner will skip
1144
- // this step too, which is fine because (a) failure diagnostics stay
1145
- // on disk for the user to inspect, and (b) `rm -rf` is idempotent so
1146
- // a follow-up run can re-clean. The lockfile is deliberately not in
1147
- // cleanupPaths, so repeat runs still benefit from cached resolution.
1148
- if (shouldInstallSkills && frozenInstall.cleanupCommandString !== ':') {
1149
- builder.step(cleanupStepName, {
1150
- type: 'deterministic',
1151
- command: frozenInstall.cleanupCommandString,
1152
- cwd: workingDirectory,
1153
- timeoutMs,
1154
- captureOutput: true,
1155
- failOnError: false,
1156
- dependsOn: [stepName]
1157
- });
1158
- }
1159
- if (abortController.signal.aborted) {
1160
- runner.abort();
1161
- }
1162
- else {
1163
- abortController.signal.addEventListener('abort', () => runner.abort(), { once: true });
1164
- }
1165
- const run = (await runner.execute(builder.toConfig()));
1166
- if (!runId.settled) {
1167
- runId.resolve(run.id);
1168
- }
1169
- const primaryCapture = stepCaptures.get(stepName);
1170
- const fallbackCapture = shouldInstallSkills ? stepCaptures.get(installStepName) : undefined;
1171
- const capture = primaryCapture ?? fallbackCapture;
1172
- const result = {
1173
- status: run.status === 'cancelled'
1174
- ? 'cancelled'
1175
- : run.status === 'failed' && isTimeoutError(run.error)
1176
- ? 'timeout'
1177
- : run.status === 'completed'
1178
- ? 'completed'
1179
- : 'failed',
1180
- output: capture?.stdout ?? '',
1181
- stderr: capture?.stderr ?? '',
1182
- exitCode: capture?.exitCode ?? null,
1183
- durationMs: Date.now() - (Date.parse(run.startedAt) || Date.now()),
1184
- workflowRunId: run.id,
1185
- stepName
1186
- };
1187
- if (run.status === 'completed') {
1188
- return result;
1189
- }
1190
- if (run.status === 'cancelled') {
1191
- const error = createAbortError(cancelReason ?? 'Execution cancelled');
1192
- Object.assign(error, { result });
1193
- throw error;
1194
- }
1195
- throw new PersonaExecutionError(run.error ?? `Persona execution failed for step "${stepName}"`, result);
1196
- }
1197
- catch (error) {
1198
- if (!runId.settled) {
1199
- runId.reject(error);
1200
- }
1201
- throw error;
1202
- }
1203
- finally {
1204
- if (runIdReadyTimer) {
1205
- clearTimeout(runIdReadyTimer);
1206
- }
1207
- unlinkAbort();
1208
- }
1209
- })();
1210
- return Object.assign(resultPromise, {
1211
- cancel(reason) {
1212
- cancelReason = reason;
1213
- abortController.abort(reason);
1214
- },
1215
- runId: runId.promise
1216
- });
1217
- };
1218
763
  return Object.freeze({
1219
764
  selection: frozenSelection,
1220
- install: frozenInstall,
1221
- sendMessage
765
+ install: frozenInstall
1222
766
  });
1223
767
  }
1224
768
  export * from './eval.js';