@kontextmind/kxm 0.7.24 → 0.7.26

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.
@@ -484,6 +484,10 @@ interface VnextStatusDrive {
484
484
  divergence?: string;
485
485
  }
486
486
 
487
+ function formatCancelledStatus(reason: string): string {
488
+ return reason.length > 0 ? `cancelled (${reason})` : "cancelled";
489
+ }
490
+
487
491
  function formatDriveStatusLine(runStatus: string, drive: VnextStatusDrive | undefined): string | undefined {
488
492
  if (!drive || typeof drive.driveId !== "string" || drive.driveId.length === 0) return undefined;
489
493
  const receipt = drive.receipt ?? null;
@@ -503,12 +507,27 @@ function formatDriveStatusLine(runStatus: string, drive: VnextStatusDrive | unde
503
507
  ? `drive ${drive.driveId}: handoff (receipt verified)`
504
508
  : `drive ${drive.driveId}: handoff`;
505
509
  }
510
+ if (receipt.settlement?.status === "cancelled") {
511
+ const label = formatCancelledStatus(reason);
512
+ return drive.verified === true
513
+ ? `drive ${drive.driveId}: ${label} (receipt verified)`
514
+ : `drive ${drive.driveId}: ${label}`;
515
+ }
506
516
  if (drive.verified === true) {
507
517
  return `drive ${drive.driveId}: completed (receipt verified)`;
508
518
  }
509
519
  return `drive ${drive.driveId}: completed`;
510
520
  }
511
521
 
522
+ function formatRunStatusLine(
523
+ run: { runId: string; status: string; workflowId: string; updatedAt: string },
524
+ drive: VnextStatusDrive | undefined,
525
+ ): string {
526
+ const reason = typeof drive?.receipt?.settlement?.reason === "string" ? drive.receipt.settlement.reason : "";
527
+ const statusLabel = run.status === "cancelled" ? formatCancelledStatus(reason) : run.status;
528
+ return `run ${run.runId}: ${statusLabel} (workflow ${run.workflowId}, updated ${run.updatedAt})`;
529
+ }
530
+
512
531
  export async function cmdVnextRunStatus(runtime: Runtime, runId: string): Promise<number> {
513
532
  try {
514
533
  const projectRoot = discoverVnextProjectRoot(runtime.cwd);
@@ -525,7 +544,7 @@ export async function cmdVnextRunStatus(runtime: Runtime, runId: string): Promis
525
544
  runtime.io,
526
545
  runtime.json,
527
546
  { ok: true, command: "runs status", run, ...(drive !== undefined ? { drive } : {}) },
528
- `run ${run.runId}: ${run.status} (workflow ${run.workflowId}, updated ${run.updatedAt})${driveLine ? `\n${driveLine}` : ""}`,
547
+ `${formatRunStatusLine(run, drive)}${driveLine ? `\n${driveLine}` : ""}`,
529
548
  );
530
549
  return 0;
531
550
  } catch (error) {
@@ -538,7 +557,32 @@ export async function cmdVnextRunStatus(runtime: Runtime, runId: string): Promis
538
557
  }
539
558
  }
540
559
 
541
- export async function cmdVnextRunDrive(runtime: Runtime, runId: string, simulated: boolean): Promise<number> {
560
+ const DEFAULT_DRIVE_WAIT_MS = 60_000;
561
+ const MAX_DRIVE_WAIT_MS = 600_000;
562
+
563
+ function parseDriveWaitTimeoutMs(raw: number | string | undefined): number | undefined {
564
+ if (raw === undefined) return DEFAULT_DRIVE_WAIT_MS;
565
+ const value = typeof raw === "number" ? raw : Number(typeof raw === "string" ? raw.trim() : raw);
566
+ if (!Number.isInteger(value) || value < 1 || value > MAX_DRIVE_WAIT_MS) return undefined;
567
+ return value;
568
+ }
569
+
570
+ function sleepMs(ms: number): Promise<void> {
571
+ return new Promise((resolve) => setTimeout(resolve, ms));
572
+ }
573
+
574
+ function driveWaitCompleted(drive: VnextStatusDrive | undefined): boolean {
575
+ return drive?.verified === true
576
+ && drive.receipt?.settlement?.kind === "terminal"
577
+ && drive.receipt.settlement.status === "completed";
578
+ }
579
+
580
+ export async function cmdVnextRunDrive(
581
+ runtime: Runtime,
582
+ runId: string,
583
+ simulated: boolean,
584
+ options: { wait?: boolean; timeoutMs?: number | string } = {},
585
+ ): Promise<number> {
542
586
  try {
543
587
  const projectRoot = discoverVnextProjectRoot(runtime.cwd);
544
588
  if (!projectRoot) {
@@ -550,8 +594,19 @@ export async function cmdVnextRunDrive(runtime: Runtime, runId: string, simulate
550
594
  print(runtime.io, runtime.json, { ok: true, command: "runs drive", dryRun: true, runId, mode }, `drive plan: run ${runId} in ${mode} mode (no events written)`);
551
595
  return 0;
552
596
  }
597
+ const timeoutMs = options.wait === true ? parseDriveWaitTimeoutMs(options.timeoutMs) : DEFAULT_DRIVE_WAIT_MS;
598
+ if (options.wait === true && timeoutMs === undefined) {
599
+ print(
600
+ runtime.io,
601
+ runtime.json,
602
+ { ok: false, command: "runs drive", error: "run_drive_timeout_invalid" },
603
+ `run drive --wait --timeout-ms must be an integer between 1 and ${MAX_DRIVE_WAIT_MS}`,
604
+ );
605
+ return 1;
606
+ }
553
607
  const supervisor = await (vnextDriveCliSeams.ensureSupervisor ?? ensureVnextSupervisor)({ env: runtime.env });
554
- const result = await (vnextDriveCliSeams.runtimeRequest ?? vnextRuntimeRequest)(supervisor, "POST", `/v1/runs/${encodeURIComponent(runId)}/drive?projectRoot=${encodeURIComponent(projectRoot)}`, { mode });
608
+ const request = vnextDriveCliSeams.runtimeRequest ?? vnextRuntimeRequest;
609
+ const result = await request(supervisor, "POST", `/v1/runs/${encodeURIComponent(runId)}/drive?projectRoot=${encodeURIComponent(projectRoot)}`, { mode });
555
610
  const driveId = result.driveId;
556
611
  const poll = result.poll;
557
612
  const status = result.status;
@@ -559,13 +614,39 @@ export async function cmdVnextRunDrive(runtime: Runtime, runId: string, simulate
559
614
  print(runtime.io, runtime.json, { ok: false, command: "runs drive", error: "run_drive_io_failed" }, "run drive failed because a local operation did not complete");
560
615
  return 1;
561
616
  }
562
- print(
563
- runtime.io,
564
- runtime.json,
565
- { ok: true, command: "runs drive", runId, driveId, poll, mode, status },
566
- `drive ${driveId}: accepted (poll ${poll})`,
567
- );
568
- return 0;
617
+
618
+ if (options.wait !== true) {
619
+ print(
620
+ runtime.io,
621
+ runtime.json,
622
+ { ok: true, command: "runs drive", runId, driveId, poll, mode, status },
623
+ `drive ${driveId}: accepted (poll ${poll})`,
624
+ );
625
+ return 0;
626
+ }
627
+
628
+ const deadline = Date.now() + timeoutMs!;
629
+ let interval = 25;
630
+ while (true) {
631
+ const stateResult = await request(supervisor, "GET", `/v1/runs/${encodeURIComponent(runId)}?projectRoot=${encodeURIComponent(projectRoot)}`);
632
+ const drive = stateResult.drive as VnextStatusDrive | undefined;
633
+ if (drive?.receipt) {
634
+ print(
635
+ runtime.io,
636
+ runtime.json,
637
+ { ok: driveWaitCompleted(drive), command: "runs drive", receipt: drive.receipt, verified: drive.verified === true },
638
+ JSON.stringify(drive.receipt, null, 2),
639
+ );
640
+ return driveWaitCompleted(drive) ? 0 : 1;
641
+ }
642
+ const remaining = deadline - Date.now();
643
+ if (remaining <= 0) break;
644
+ await sleepMs(Math.min(interval, remaining));
645
+ interval = Math.min(Math.floor(interval * 1.5), 250);
646
+ }
647
+
648
+ print(runtime.io, runtime.json, { ok: false, command: "runs drive", error: "timeout" }, "drive wait timed out");
649
+ return 1;
569
650
  } catch (error) {
570
651
  if (error instanceof VnextConfigError) {
571
652
  print(runtime.io, runtime.json, { ok: false, command: "runs drive", error: "run_drive_failed", issues: error.issues }, `run drive failed: ${error.message}`);
@@ -753,3 +834,45 @@ export async function cmdVnextRuntime(runtime: Runtime, action: string): Promise
753
834
  return 1;
754
835
  }
755
836
  }
837
+
838
+ export async function cmdVnextRunReceipt(runtime: Runtime, runId: string, options: { all?: boolean } = {}): Promise<number> {
839
+ try {
840
+ const projectRoot = discoverVnextProjectRoot(runtime.cwd);
841
+ if (!projectRoot) {
842
+ print(runtime.io, runtime.json, { ok: false, command: "runs receipt", error: "project_required" }, "kxm runs receipt requires a vNext project (run kxm init first)");
843
+ return 1;
844
+ }
845
+ const supervisor = await (vnextDriveCliSeams.ensureSupervisor ?? ensureVnextSupervisor)({ env: runtime.env });
846
+ const result = await (vnextDriveCliSeams.runtimeRequest ?? vnextRuntimeRequest)(
847
+ supervisor,
848
+ "GET",
849
+ `/v1/runs/${encodeURIComponent(runId)}/drive?projectRoot=${encodeURIComponent(projectRoot)}`,
850
+ );
851
+ if (!Array.isArray(result.receipts)) {
852
+ print(runtime.io, runtime.json, { ok: false, command: "runs receipt", error: "run_receipt_io_failed" }, "run receipt failed because a local operation did not complete");
853
+ return 1;
854
+ }
855
+ const receipts = result.receipts as unknown[];
856
+ if (receipts.length === 0) {
857
+ print(runtime.io, runtime.json, { ok: false, command: "runs receipt", error: "no_receipts" }, `run ${runId} has no drive receipts`);
858
+ return 1;
859
+ }
860
+ if (options.all === true) {
861
+ print(runtime.io, runtime.json, { ok: true, command: "runs receipt", receipts }, JSON.stringify(receipts, null, 2));
862
+ return 0;
863
+ }
864
+ const latest = receipts[0];
865
+ const settlement = latest && typeof latest === "object" && "settlement" in latest
866
+ ? (latest as { settlement: unknown }).settlement
867
+ : latest;
868
+ print(runtime.io, runtime.json, { ok: true, command: "runs receipt", receipt: latest }, JSON.stringify(settlement, null, 2));
869
+ return 0;
870
+ } catch (error) {
871
+ if (error instanceof VnextConfigError) {
872
+ print(runtime.io, runtime.json, { ok: false, command: "runs receipt", error: "run_receipt_failed", issues: error.issues }, `run receipt failed: ${error.message}`);
873
+ return 1;
874
+ }
875
+ print(runtime.io, runtime.json, { ok: false, command: "runs receipt", error: "run_receipt_io_failed" }, "run receipt failed because a local operation did not complete");
876
+ return 1;
877
+ }
878
+ }
@@ -107,6 +107,7 @@ import {
107
107
  cmdVnextRun,
108
108
  cmdVnextRunStatus,
109
109
  cmdVnextRunDrive,
110
+ cmdVnextRunReceipt,
110
111
  cmdVnextRunCancel,
111
112
  cmdVnextRunList,
112
113
  cmdHarnessList,
@@ -390,8 +391,19 @@ function createProgram(ctx: CliContext, result: { code: number }): Command {
390
391
  addGlobalOptions(runCmd.command("drive").description("Drive a run with an explicit model-free simulation"))
391
392
  .argument("<runId>", "Run id")
392
393
  .option("--simulated", "Use the model-free simulation producer")
393
- .action(async function runDriveAction(this: Command, runId: string, options: { simulated?: boolean }) {
394
- result.code = await cmdVnextRunDrive(runtimeFrom(ctx, this), runId, options.simulated === true);
394
+ .option("--wait", "Wait until a drive receipt is recorded; exits 0 only for a VERIFIED COMPLETED settlement")
395
+ .option("--timeout-ms <n>", "Wait timeout in milliseconds (default 60000, max 600000)")
396
+ .action(async function runDriveAction(this: Command, runId: string, options: { simulated?: boolean; wait?: boolean; timeoutMs?: string }) {
397
+ result.code = await cmdVnextRunDrive(runtimeFrom(ctx, this), runId, options.simulated === true, {
398
+ wait: options.wait === true,
399
+ ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
400
+ });
401
+ });
402
+ addGlobalOptions(runCmd.command("receipt").description("Print the newest drive receipt for a run"))
403
+ .argument("<runId>", "Run id")
404
+ .option("--all", "Print the capped receipt list for the run")
405
+ .action(async function runReceiptAction(this: Command, runId: string, options: { all?: boolean }) {
406
+ result.code = await cmdVnextRunReceipt(runtimeFrom(ctx, this), runId, { all: options.all === true });
395
407
  });
396
408
  addGlobalOptions(runCmd.command("cancel").description("Durably request cancellation of a run"))
397
409
  .argument("<runId>", "Run id")
@@ -8,7 +8,7 @@ import { AGENT_COMMANDS_MAP, enforceToolPolicy, getMcpTools, reconcileInbox } fr
8
8
  import { deliverInboxNotification } from "./inbox.ts";
9
9
  import type { HubEvent, MessageRecord } from "./protocol.ts";
10
10
 
11
- const VERSION = "0.7.24";
11
+ const VERSION = "0.7.26";
12
12
  const inbox = new Map<string, MessageRecord>();
13
13
  const notifiedInbox = new Set<string>();
14
14
  let meshClient: HubClient | undefined;
@@ -62,6 +62,30 @@ export interface VnextFoldOptions {
62
62
  observationId: string,
63
63
  ) => { completeness: "complete" | "incomplete" | "no-start" } | undefined;
64
64
  readonly gateKind?: (stepId: string) => string | undefined;
65
+ readonly projectLimits?: {
66
+ readonly maxRunDurationMs?: number;
67
+ };
68
+ }
69
+
70
+ export type VnextRunDurationBudgetSource = "workflow" | "project" | "both";
71
+
72
+ export interface VnextRunDurationBudget {
73
+ readonly budgetMs: number;
74
+ readonly source: VnextRunDurationBudgetSource;
75
+ }
76
+
77
+ export function effectiveRunDurationBudget(
78
+ planLimits: { readonly maxRunDurationMs?: number } | undefined,
79
+ projectLimits: { readonly maxRunDurationMs?: number } | undefined,
80
+ ): VnextRunDurationBudget | undefined {
81
+ const workflow = planLimits?.maxRunDurationMs;
82
+ const project = projectLimits?.maxRunDurationMs;
83
+ if (workflow === undefined && project === undefined) return undefined;
84
+ if (workflow !== undefined && project !== undefined) {
85
+ return { budgetMs: Math.min(workflow, project), source: "both" };
86
+ }
87
+ if (workflow !== undefined) return { budgetMs: workflow, source: "workflow" };
88
+ return { budgetMs: project!, source: "project" };
65
89
  }
66
90
 
67
91
  export interface VnextFoldAttemptState {
@@ -127,6 +151,7 @@ export interface VnextRunState {
127
151
  readonly terminalReason?: string | undefined;
128
152
  readonly failureReason?: string | undefined;
129
153
  readonly drive?: VnextRunDriveBinding | undefined;
154
+ readonly runningSince?: string | undefined;
130
155
  }
131
156
 
132
157
  interface MutableAttempt {
@@ -179,6 +204,7 @@ interface MutableState {
179
204
  cancelCommandId?: string | undefined;
180
205
  lastTerminalTransition?: VnextTerminalStatus | undefined;
181
206
  drive?: VnextRunDriveBinding | undefined;
207
+ runningSince?: string | undefined;
182
208
  }
183
209
 
184
210
  function emptyPanel(): MutablePanel {
@@ -468,7 +494,7 @@ export function foldVnextRunState(
468
494
  foldRunStatus(state, plan, event);
469
495
  break;
470
496
  case "run.cancel_requested":
471
- foldCancelRequested(state, event);
497
+ foldCancelRequested(state, event, plan, options);
472
498
  break;
473
499
  case "run.drive_opened":
474
500
  foldDriveOpened(state, event);
@@ -606,6 +632,9 @@ function foldRunStatus(state: MutableState, plan: VnextCompiledPlan | undefined,
606
632
  if ((status === "running" || status === "cancelling") && !plan) {
607
633
  throw runtimeError("run_plan_missing", state.runId, `status ${status} requires a pinned run plan`);
608
634
  }
635
+ if (status === "running" && state.runningSince === undefined) {
636
+ state.runningSince = event.occurredAt;
637
+ }
609
638
  if (TERMINAL_RUN.has(status)) {
610
639
  assertTerminalRunStatus(state, plan, status, event);
611
640
  if (typeof event.payload.reason === "string") state.terminalReason = event.payload.reason;
@@ -717,10 +746,41 @@ function foldDriveOpened(state: MutableState, event: VnextRunEvent): void {
717
746
  state.drive = { driveId, mode, openedSequence: event.sequence };
718
747
  }
719
748
 
720
- function foldCancelRequested(state: MutableState, event: VnextRunEvent): void {
749
+ function foldCancelRequested(
750
+ state: MutableState,
751
+ event: VnextRunEvent,
752
+ plan: VnextCompiledPlan | undefined,
753
+ options: VnextFoldOptions,
754
+ ): void {
721
755
  if (state.cancelRequested || TERMINAL_RUN.has(state.status)) {
722
756
  throw runtimeError("run_events_illegal", state.runId, "duplicate run.cancel_requested is not legal");
723
757
  }
758
+ if (event.payload.reason === "budget_run_duration") {
759
+ const budget = event.payload.budget;
760
+ if (!budget || typeof budget !== "object" || Array.isArray(budget)) {
761
+ throw runtimeError("run_events_illegal", state.runId, "budget_run_duration requires a budget payload");
762
+ }
763
+ const body = budget as Record<string, unknown>;
764
+ const budgetMs = body.budgetMs;
765
+ const elapsedMs = body.elapsedMs;
766
+ const source = body.source;
767
+ if (typeof budgetMs !== "number" || !Number.isInteger(budgetMs) || budgetMs < 0) {
768
+ throw runtimeError("run_events_illegal", state.runId, "budget_run_duration budgetMs is invalid");
769
+ }
770
+ if (typeof elapsedMs !== "number" || !Number.isInteger(elapsedMs) || elapsedMs < 0) {
771
+ throw runtimeError("run_events_illegal", state.runId, "budget_run_duration elapsedMs is invalid");
772
+ }
773
+ if (source !== "workflow" && source !== "project" && source !== "both") {
774
+ throw runtimeError("run_events_illegal", state.runId, "budget_run_duration source is invalid");
775
+ }
776
+ const expected = effectiveRunDurationBudget(plan?.limits, options.projectLimits);
777
+ if (!expected || expected.budgetMs !== budgetMs || expected.source !== source) {
778
+ throw runtimeError("run_events_illegal", state.runId, "budget_run_duration budgetMs does not match the plan");
779
+ }
780
+ if (elapsedMs < budgetMs) {
781
+ throw runtimeError("run_events_illegal", state.runId, "budget_run_duration elapsedMs is below budgetMs");
782
+ }
783
+ }
724
784
  state.cancelRequested = true;
725
785
  if (event.commandId) state.cancelCommandId = event.commandId;
726
786
  }
@@ -1403,6 +1463,7 @@ function freezeState(state: MutableState): VnextRunState {
1403
1463
  ...(state.terminalReason !== undefined ? { terminalReason: state.terminalReason } : {}),
1404
1464
  ...(state.terminalReason !== undefined && state.status === "failed" ? { failureReason: state.terminalReason } : {}),
1405
1465
  ...(state.drive !== undefined ? { drive: Object.freeze({ ...state.drive }) } : {}),
1466
+ ...(state.runningSince !== undefined ? { runningSince: state.runningSince } : {}),
1406
1467
  };
1407
1468
  return Object.freeze(frozen);
1408
1469
  }
@@ -257,7 +257,7 @@ export function recordGateNoStartInTransaction(
257
257
  const cancelled = observation.stopCause === "cancel";
258
258
  const now = new Date().toISOString();
259
259
  const seq = sequencer(context, run, now);
260
- if (cancelled) ensureOperatorCancel(seq, ids.state, run);
260
+ const cancelReason = cancelled ? ensureOperatorCancel(seq, ids.state, run, context) : undefined;
261
261
  const observationId = newVnextObservationId();
262
262
  const observed = seq.push("effect.observed", {
263
263
  effect: { id: ids.effectId, policy: { class: ids.gateKind === "artifacts-exist" ? "read-only" : "unknown", sharedMutable: false } },
@@ -289,7 +289,7 @@ export function recordGateNoStartInTransaction(
289
289
  seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
290
290
  seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
291
291
  seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
292
- seq.push("run.status_changed", { status: "cancelled", reason: "operator_cancel", stepId });
292
+ seq.push("run.status_changed", { status: "cancelled", reason: cancelReason ?? "operator_cancel", stepId });
293
293
  } else {
294
294
  seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "producer_rejected", status: "result_recorded" });
295
295
  seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
@@ -385,7 +385,7 @@ export function recordGateCancelObservedInTransaction(
385
385
  }
386
386
  const now = new Date().toISOString();
387
387
  const seq = sequencer(context, run, now);
388
- ensureOperatorCancel(seq, ids.state, run);
388
+ const cancelReason = ensureOperatorCancel(seq, ids.state, run, context);
389
389
  if (ids.gateKind === "artifacts-exist") {
390
390
  seq.push("assignment.executing", { assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, attemptId: ids.attemptId, status: "executing" });
391
391
  seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "executing" });
@@ -428,7 +428,7 @@ export function recordGateCancelObservedInTransaction(
428
428
  seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
429
429
  seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
430
430
  seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
431
- seq.push("run.status_changed", { status: "cancelled", reason: "operator_cancel", stepId });
431
+ seq.push("run.status_changed", { status: "cancelled", reason: cancelReason, stepId });
432
432
  for (const event of seq.events) context.eventStore.appendEvent(event);
433
433
  context.eventStore.insertGateObservation(observationRow);
434
434
  context.eventStore.settleCapability(ids.attemptId, "settled");
@@ -536,8 +536,9 @@ function ensureOperatorCancel(
536
536
  seq: ReturnType<typeof sequencer>,
537
537
  state: ReturnType<typeof foldStoredVnextRun>,
538
538
  run: VnextRunRecord,
539
- ): void {
540
- if (state.status === "cancelling") return;
539
+ context: VnextRuntimeContext,
540
+ ): string {
541
+ if (state.status === "cancelling") return cancelReasonFromLog(context, run.runId);
541
542
  if (state.status !== "running") {
542
543
  throw runtimeError("run_events_illegal", run.runId, "cancelled gate proof requires a running or cancelling run");
543
544
  }
@@ -546,6 +547,18 @@ function ensureOperatorCancel(
546
547
  reason: "operator_cancel",
547
548
  });
548
549
  seq.push("run.status_changed", { status: "cancelling", reason: "operator_cancel" });
550
+ return "operator_cancel";
551
+ }
552
+
553
+ function cancelReasonFromLog(context: VnextRuntimeContext, runId: string): string {
554
+ const events = context.eventStore.events(runId, 0, 1_000_000);
555
+ for (let index = events.length - 1; index >= 0; index -= 1) {
556
+ const event = events[index]!;
557
+ if (event.eventType === "run.cancel_requested" && typeof event.payload.reason === "string") {
558
+ return event.payload.reason;
559
+ }
560
+ }
561
+ return "operator_cancel";
549
562
  }
550
563
 
551
564
  function sequencer(context: VnextRuntimeContext, run: VnextRunRecord, now: string) {