@abelxiaoxing/cadence 1.2.2 → 1.2.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/src/index.ts CHANGED
@@ -38,6 +38,7 @@ import {
38
38
  verificationSteps,
39
39
  } from "./contracts.ts";
40
40
  import {
41
+ CONTROL_COMMAND_PARAMETERS,
41
42
  canonicalizeControlCommandToolInput,
42
43
  validateControlCommand,
43
44
  } from "./control-contracts.ts";
@@ -45,14 +46,15 @@ import {
45
46
  assessDeliveryTraceability,
46
47
  compileImplementPlan,
47
48
  DeliveryValidationError,
49
+ DesignPlanValidationError,
48
50
  type GateApprovalProof,
49
- IMPLEMENT_PLAN_SCHEMA_VERSION,
50
51
  parseGateAReceipt,
51
52
  parseImplementPlan,
52
53
  parseReadyReceipt,
53
54
  } from "./delivery-compiler.ts";
54
55
  import {
55
56
  DesignController,
57
+ DesignFinalizationError,
56
58
  validateDesignControlRequest,
57
59
  } from "./design-control.ts";
58
60
  import { canonicalJson, hashCanonicalValue } from "./implement-graph.ts";
@@ -67,8 +69,9 @@ import {
67
69
  unavailableRoutePolicy,
68
70
  type WorkerRoutePolicy,
69
71
  } from "./route-policy.ts";
72
+ import { RunStoreFormatError, RunStoreMigrationError } from "./run-store.ts";
70
73
  import { observeSafePath } from "./safe-path.ts";
71
- import { resolveStateRoot } from "./state-root.ts";
74
+ import { resolveStateRoot, StateRootError } from "./state-root.ts";
72
75
  import {
73
76
  ACTIVITY_DETAILS_KEY,
74
77
  ActivityController,
@@ -229,15 +232,15 @@ const DESIGN_CONTROL_REQUEST_SCHEMA = {
229
232
  required: ["operation", "operationId", field],
230
233
  additionalProperties: false,
231
234
  })),
232
- {
233
- type: "object",
235
+ ...(["status", "validate-plan-draft"] as const).map((operation) => ({
236
+ type: "object" as const,
234
237
  properties: {
235
- operation: { type: "string", enum: ["status"] },
236
- runId: { type: "string" },
238
+ operation: { type: "string" as const, enum: [operation] },
239
+ runId: { type: "string" as const },
237
240
  },
238
241
  required: ["operation", "runId"],
239
242
  additionalProperties: false,
240
- },
243
+ })),
241
244
  {
242
245
  type: "object",
243
246
  properties: {
@@ -421,6 +424,241 @@ function splitUsage(result: unknown): { payload: unknown; usage?: unknown } {
421
424
  return { payload, usage };
422
425
  }
423
426
 
427
+ const SAFE_DESIGN_ERROR_CODE = /^[a-z0-9](?:[a-z0-9._:-]{0,126}[a-z0-9])?$/iu;
428
+ const MAX_DESIGN_ERROR_DIAGNOSTICS = 64;
429
+
430
+ type SafeDesignFailure = {
431
+ kind: "design-control-failure";
432
+ operation: string;
433
+ code: string;
434
+ diagnostics: ReadonlyArray<Record<string, string | boolean>>;
435
+ };
436
+
437
+ type DesignFailureBoundary =
438
+ | "dispatch"
439
+ | "initialization"
440
+ | "execution"
441
+ | "serialization";
442
+
443
+ function sqliteLocked(error: unknown): boolean {
444
+ return (
445
+ typeof error === "object" &&
446
+ error !== null &&
447
+ "errcode" in error &&
448
+ ((error as { errcode?: unknown }).errcode === 5 ||
449
+ (error as { errcode?: unknown }).errcode === 6)
450
+ );
451
+ }
452
+
453
+ function sqliteUnavailable(error: unknown): boolean {
454
+ if (typeof error !== "object" || error === null) return false;
455
+ const errcode = Number((error as { errcode?: unknown }).errcode);
456
+ const code = (error as { code?: unknown }).code;
457
+ return (
458
+ [8, 10, 13, 14, 23, 24].includes(errcode) ||
459
+ (typeof code === "string" &&
460
+ [
461
+ "EACCES",
462
+ "EBUSY",
463
+ "EMFILE",
464
+ "ENFILE",
465
+ "ENOSPC",
466
+ "EPERM",
467
+ "EROFS",
468
+ ].includes(code))
469
+ );
470
+ }
471
+
472
+ function classifyDesignFailure(
473
+ error: unknown,
474
+ operation: string,
475
+ boundary: DesignFailureBoundary,
476
+ ): {
477
+ code: string;
478
+ category: string;
479
+ retryable: boolean;
480
+ field?: string;
481
+ } {
482
+ const message = error instanceof Error ? error.message : "";
483
+ if (error instanceof DesignPlanValidationError) {
484
+ return {
485
+ code: "design-plan-validation-invalid",
486
+ category: "validation",
487
+ retryable: false,
488
+ };
489
+ }
490
+ if (
491
+ error instanceof RunStoreMigrationError ||
492
+ error instanceof RunStoreFormatError
493
+ ) {
494
+ return {
495
+ code: "design-store-migration-failed",
496
+ category: "storage",
497
+ retryable: false,
498
+ field: "schemaVersion",
499
+ };
500
+ }
501
+ if (sqliteLocked(error)) {
502
+ return {
503
+ code: "design-store-locked",
504
+ category: "storage",
505
+ retryable: true,
506
+ };
507
+ }
508
+ if (sqliteUnavailable(error)) {
509
+ return {
510
+ code: "design-store-unavailable",
511
+ category: "storage",
512
+ retryable: false,
513
+ };
514
+ }
515
+ if (error instanceof StateRootError) {
516
+ return {
517
+ code: "design-store-unavailable",
518
+ category: "storage",
519
+ retryable: false,
520
+ };
521
+ }
522
+ if (
523
+ message === "design-operation-conflict" ||
524
+ message === "operation-id-conflict"
525
+ ) {
526
+ return {
527
+ code: "design-operation-conflict",
528
+ category: "control",
529
+ retryable: false,
530
+ field: "operationId",
531
+ };
532
+ }
533
+ if (message === "stage-control-mismatch") {
534
+ return {
535
+ code: "design-stage-inactive",
536
+ category: "stage",
537
+ retryable: false,
538
+ };
539
+ }
540
+ if (message === "invalid-design-control-request") {
541
+ return {
542
+ code: "invalid-design-control-request",
543
+ category: "validation",
544
+ retryable: false,
545
+ };
546
+ }
547
+ if (boundary === "serialization") {
548
+ return {
549
+ code: "design-receipt-serialization-failed",
550
+ category: "control",
551
+ retryable: false,
552
+ };
553
+ }
554
+ if (boundary === "initialization") {
555
+ return {
556
+ code: "design-store-unavailable",
557
+ category: "storage",
558
+ retryable: false,
559
+ };
560
+ }
561
+ if (
562
+ error instanceof DesignFinalizationError ||
563
+ error instanceof DeliveryValidationError ||
564
+ (message.startsWith("design-") && SAFE_DESIGN_ERROR_CODE.test(message))
565
+ ) {
566
+ return { code: message, category: "control", retryable: false };
567
+ }
568
+ if (operation === "start") {
569
+ return {
570
+ code: "design-run-create-failed",
571
+ category: "storage",
572
+ retryable: false,
573
+ };
574
+ }
575
+ return {
576
+ code: "design-control-internal",
577
+ category: "control",
578
+ retryable: false,
579
+ };
580
+ }
581
+
582
+ function safeDesignFailure(
583
+ error: unknown,
584
+ operation: string,
585
+ boundary: DesignFailureBoundary = "dispatch",
586
+ ): SafeDesignFailure {
587
+ const classified = classifyDesignFailure(error, operation, boundary);
588
+ const rawDiagnostics: readonly unknown[] =
589
+ error instanceof DesignFinalizationError ||
590
+ error instanceof DesignPlanValidationError ||
591
+ error instanceof DeliveryValidationError
592
+ ? error.diagnostics
593
+ : [];
594
+ const diagnostics = rawDiagnostics
595
+ .flatMap((candidate) => {
596
+ if (typeof candidate === "string") {
597
+ const diagnosticCode = candidate.split(":", 1)[0] ?? "";
598
+ return SAFE_DESIGN_ERROR_CODE.test(diagnosticCode)
599
+ ? [{ code: diagnosticCode }]
600
+ : [];
601
+ }
602
+ if (
603
+ candidate === null ||
604
+ typeof candidate !== "object" ||
605
+ Array.isArray(candidate)
606
+ ) {
607
+ return [];
608
+ }
609
+ const admitted = Object.fromEntries(
610
+ Object.entries(candidate as Record<string, unknown>)
611
+ .filter((entry): entry is [string, string | boolean] => {
612
+ if (entry[0] === "retryable") {
613
+ return typeof entry[1] === "boolean";
614
+ }
615
+ return (
616
+ [
617
+ "code",
618
+ "taskId",
619
+ "phase",
620
+ "field",
621
+ "category",
622
+ "owner",
623
+ "verificationId",
624
+ "outputId",
625
+ "dependencyTaskId",
626
+ "producerTaskId",
627
+ "producerPhase",
628
+ ].includes(entry[0]) &&
629
+ typeof entry[1] === "string" &&
630
+ SAFE_DESIGN_ERROR_CODE.test(entry[1])
631
+ );
632
+ })
633
+ .sort(([left], [right]) => left.localeCompare(right)),
634
+ );
635
+ return typeof admitted.code === "string" ? [admitted] : [];
636
+ })
637
+ .sort((left, right) =>
638
+ canonicalJson(left).localeCompare(canonicalJson(right)),
639
+ )
640
+ .filter(
641
+ (candidate, index, values) =>
642
+ index === 0 ||
643
+ canonicalJson(candidate) !== canonicalJson(values[index - 1]),
644
+ )
645
+ .slice(0, MAX_DESIGN_ERROR_DIAGNOSTICS);
646
+ if (diagnostics.length === 0) {
647
+ diagnostics.push({
648
+ code: classified.code,
649
+ category: classified.category,
650
+ retryable: classified.retryable,
651
+ ...(classified.field ? { field: classified.field } : {}),
652
+ });
653
+ }
654
+ return {
655
+ kind: "design-control-failure",
656
+ operation: SAFE_DESIGN_ERROR_CODE.test(operation) ? operation : "unknown",
657
+ code: classified.code,
658
+ diagnostics,
659
+ };
660
+ }
661
+
424
662
  export interface WorkflowControlEngine {
425
663
  execute(
426
664
  command: unknown,
@@ -733,7 +971,10 @@ export function packageDeliverySource(
733
971
  try {
734
972
  receipt = parseReadyReceipt(receiptBytes);
735
973
  } catch {
736
- throw new DeliveryValidationError(["delivery-receipt-invalid"]);
974
+ throw new DeliveryValidationError([
975
+ "delivery-receipt-invalid",
976
+ "delivery-recompile-required",
977
+ ]);
737
978
  }
738
979
  const diagnostics = new Set<string>();
739
980
  if (receipt.change !== input.change) {
@@ -899,7 +1140,6 @@ export function packageDeliverySource(
899
1140
  }
900
1141
  if (
901
1142
  receipt.plan.path !== "implement-plan.json" ||
902
- receipt.plan.schemaVersion !== IMPLEMENT_PLAN_SCHEMA_VERSION ||
903
1143
  receipt.plan.rawSha256 !== sha256(planBytes) ||
904
1144
  receipt.plan.canonicalHash !== hashCanonicalValue(plan)
905
1145
  ) {
@@ -982,7 +1222,6 @@ export function packageDeliverySource(
982
1222
  throw new DeliveryValidationError([...diagnostics]);
983
1223
  }
984
1224
  return {
985
- version: 2,
986
1225
  gate: "gate-b",
987
1226
  revision: receipt.deliveryRevision,
988
1227
  receiptHash,
@@ -1251,7 +1490,7 @@ function normalizedFailureIdentities(input: {
1251
1490
  if (vitestFailures.length > 0) {
1252
1491
  return [...new Set(vitestFailures)]
1253
1492
  .sort()
1254
- .map((failure) => sha256(`vitest-failure-v1\0${failure}`));
1493
+ .map((failure) => sha256(`vitest-failure\0${failure}`));
1255
1494
  }
1256
1495
  const normalizedOutput = input.output
1257
1496
  .replace(new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "gu"), "")
@@ -1261,7 +1500,6 @@ function normalizedFailureIdentities(input: {
1261
1500
  return [
1262
1501
  sha256(
1263
1502
  canonicalJson({
1264
- version: 1,
1265
1503
  verificationId: input.step.id,
1266
1504
  exitCode: input.exitCode,
1267
1505
  output: normalizedOutput,
@@ -1514,6 +1752,8 @@ export function openPackageWorkflowControlEngine(
1514
1752
  context,
1515
1753
  parentPayloadBridge,
1516
1754
  input.signal,
1755
+ process.env,
1756
+ { onResponse: input.onHeaders },
1517
1757
  );
1518
1758
  if (!phaseRuntime.ok) {
1519
1759
  if (phaseRuntime.failure.kind === "cancelled") {
@@ -1565,6 +1805,9 @@ export function openPackageWorkflowControlEngine(
1565
1805
  ...(input.artifactCorrection
1566
1806
  ? { artifactCorrection: structuredClone(input.artifactCorrection) }
1567
1807
  : {}),
1808
+ ...(input.contextRequest
1809
+ ? { requestedContext: structuredClone(input.contextRequest) }
1810
+ : {}),
1568
1811
  ...(input.repair
1569
1812
  ? {
1570
1813
  repair: {
@@ -1608,7 +1851,6 @@ export function openPackageWorkflowControlEngine(
1608
1851
  failureOverride: phaseRuntime.failureOverride,
1609
1852
  ledgerProjection: input.ledgerProjection,
1610
1853
  candidateArtifact: input.candidateArtifact,
1611
- onStreamStart: input.onHeaders,
1612
1854
  onStreamProgress: input.onProgress,
1613
1855
  });
1614
1856
  if (!child.ok) {
@@ -1624,10 +1866,18 @@ export function openPackageWorkflowControlEngine(
1624
1866
  if (child.failure.kind === "result-limit") {
1625
1867
  return { kind: "paused", code: "needs-task-split" };
1626
1868
  }
1869
+ const attemptDiagnostic = {
1870
+ finalCategory: child.classification.finalCategory,
1871
+ submitAttempts: child.classification.attempts,
1872
+ schema: child.classification.schema,
1873
+ identityMismatch: Object.entries(child.classification.identity)
1874
+ .filter(([, matches]) => !matches)
1875
+ .map(([dimension]) => dimension),
1876
+ };
1627
1877
  return child.failure.kind === "environment" ||
1628
1878
  child.failure.kind === "verification-adapter"
1629
- ? { kind: "paused", code: child.failure.code }
1630
- : { kind: "retryable", code: child.failure.code };
1879
+ ? { kind: "paused", code: child.failure.code, attemptDiagnostic }
1880
+ : { kind: "retryable", code: child.failure.code, attemptDiagnostic };
1631
1881
  }
1632
1882
  const result = child.result;
1633
1883
  if (result.kind === "context-request") {
@@ -1764,6 +2014,39 @@ export function openPackageWorkflowControlEngine(
1764
2014
  };
1765
2015
  }
1766
2016
 
2017
+ function runStoreUnavailableStatus(
2018
+ error: RunStoreFormatError,
2019
+ command: {
2020
+ stage: "abel-implement";
2021
+ change: string;
2022
+ },
2023
+ ): Record<string, unknown> {
2024
+ return {
2025
+ stage: command.stage,
2026
+ change: command.change,
2027
+ state: "paused",
2028
+ durable: false,
2029
+ completed: false,
2030
+ pause: { code: error.code },
2031
+ legalCommands: ["status", "start"],
2032
+ tasks: [],
2033
+ queue: [],
2034
+ controlStore: {
2035
+ code: error.code,
2036
+ databasePath: error.databasePath,
2037
+ recovery: structuredClone(error.recovery),
2038
+ },
2039
+ };
2040
+ }
2041
+
2042
+ function runStoreResetError(error: RunStoreFormatError): Error {
2043
+ const reset = new Error(
2044
+ `${error.code}: back up and remove the private run store at ${error.databasePath}, then retry the operation`,
2045
+ );
2046
+ reset.name = "RunStoreResetError";
2047
+ return reset;
2048
+ }
2049
+
1767
2050
  function visibleRoutePolicyStatus(
1768
2051
  resolution: RoutePolicyResolution,
1769
2052
  brokerStatus: Record<string, unknown> | undefined,
@@ -1783,6 +2066,7 @@ export function registerWorkflowControl(
1783
2066
  const parentPayloadBridge = new ParentPayloadBridge();
1784
2067
  const activity = new ActivityController();
1785
2068
  const engines = new Map<string, Promise<WorkflowControlEngine>>();
2069
+ const designFailures = new Map<string, SafeDesignFailure>();
1786
2070
  let pendingPrompt: EligiblePrompt | undefined;
1787
2071
  let activePrompt: EligiblePrompt | undefined;
1788
2072
  let designToolSnapshot: string[] | undefined;
@@ -1875,30 +2159,15 @@ export function registerWorkflowControl(
1875
2159
 
1876
2160
  // Pi validates tool arguments before execute(), so expose only the schema
1877
2161
  // legal for the active stage instead of one ambiguous command/packet union.
1878
- const CONTROL_COMMAND_PARAMETERS = {
2162
+ const PACKET_PARAMETERS = {
1879
2163
  type: "object",
1880
2164
  properties: {
1881
- command: {
1882
- type: "string",
1883
- enum: ["start", "status", "resume", "rebind", "cancel", "discard"],
1884
- },
1885
- stage: {
2165
+ action: {
1886
2166
  type: "string",
1887
- enum: ["abel-implement"],
2167
+ enum: [...PACKET_ACTIONS, "design"],
2168
+ description:
2169
+ "Top-level dispatch action. design and run require request; finish is explicit stage exit and MUST omit request.",
1888
2170
  },
1889
- change: { type: "string" },
1890
- operationId: { type: "string" },
1891
- deliveryRevision: { type: "integer", minimum: 1 },
1892
- receiptHash: { type: "string" },
1893
- routeId: { type: "string" },
1894
- },
1895
- required: ["command", "stage", "change"],
1896
- additionalProperties: false,
1897
- } as const;
1898
- const PACKET_PARAMETERS = {
1899
- type: "object",
1900
- properties: {
1901
- action: { type: "string", enum: [...PACKET_ACTIONS, "design"] },
1902
2171
  request: {
1903
2172
  anyOf: [
1904
2173
  DESIGN_REQUEST_SCHEMA,
@@ -1950,6 +2219,9 @@ export function registerWorkflowControl(
1950
2219
  ) {
1951
2220
  throw new Error("control-envelope-ambiguous");
1952
2221
  }
2222
+ if (record.action === "finish" && Object.hasOwn(record, "request")) {
2223
+ throw new Error("control-envelope-ambiguous");
2224
+ }
1953
2225
  if (
1954
2226
  activePrompt !== "abel-design" &&
1955
2227
  activePrompt !== "abel-diagnose"
@@ -1962,22 +2234,40 @@ export function registerWorkflowControl(
1962
2234
  }
1963
2235
  const designRequest = validateDesignControlRequest(record.request);
1964
2236
  if (!designRequest.ok) throw new Error(designRequest.code);
1965
- const engine = await engineFor(ctx);
1966
- if (!engine.executeDesign)
1967
- throw new Error("design-control-unavailable");
1968
- const payload = await engine.executeDesign(designRequest.value);
1969
- if (
1970
- designRequest.value.operation === "finalize-delivery" &&
1971
- payload.state === "completed"
1972
- ) {
1973
- await deactivateStage();
2237
+ let boundary: DesignFailureBoundary = "initialization";
2238
+ try {
2239
+ const engine = await engineFor(ctx);
2240
+ if (!engine.executeDesign)
2241
+ throw new Error("design-control-unavailable");
2242
+ boundary = "execution";
2243
+ const payload = await engine.executeDesign(designRequest.value);
2244
+ boundary = "serialization";
2245
+ const serialized = JSON.stringify(payload);
2246
+ if (serialized === undefined) {
2247
+ throw new Error("design-receipt-serialization-failed");
2248
+ }
2249
+ boundary = "execution";
2250
+ if (
2251
+ designRequest.value.operation === "finalize-delivery" &&
2252
+ payload.state === "completed"
2253
+ ) {
2254
+ await deactivateStage();
2255
+ }
2256
+ return {
2257
+ content: [{ type: "text" as const, text: serialized }],
2258
+ details: payload,
2259
+ };
2260
+ } catch (error) {
2261
+ const failure = safeDesignFailure(
2262
+ error,
2263
+ designRequest.value.operation,
2264
+ boundary,
2265
+ );
2266
+ designFailures.set(toolCallId, failure);
2267
+ const sanitized = new Error(failure.code);
2268
+ sanitized.name = "DesignControlError";
2269
+ throw sanitized;
1974
2270
  }
1975
- return {
1976
- content: [
1977
- { type: "text" as const, text: JSON.stringify(payload) },
1978
- ],
1979
- details: payload,
1980
- };
1981
2271
  }
1982
2272
  let designEngine: WorkflowControlEngine | undefined;
1983
2273
  if (record.action === "run") {
@@ -1988,7 +2278,13 @@ export function registerWorkflowControl(
1988
2278
  );
1989
2279
  }
1990
2280
  if (packet.value.stage === "abel-design") {
1991
- designEngine = await engineFor(ctx);
2281
+ try {
2282
+ designEngine = await engineFor(ctx);
2283
+ } catch (error) {
2284
+ if (error instanceof RunStoreFormatError)
2285
+ throw runStoreResetError(error);
2286
+ throw error;
2287
+ }
1992
2288
  if (!designEngine.assertDesignRun || !packet.value.runId) {
1993
2289
  throw new Error("design-control-unavailable");
1994
2290
  }
@@ -2076,7 +2372,17 @@ export function registerWorkflowControl(
2076
2372
  ) {
2077
2373
  throw new Error("stage-control-mismatch");
2078
2374
  }
2079
- const engine = await engineFor(ctx);
2375
+ let engine: WorkflowControlEngine;
2376
+ try {
2377
+ engine = await engineFor(ctx);
2378
+ } catch (error) {
2379
+ if (!(error instanceof RunStoreFormatError)) throw error;
2380
+ const payload = runStoreUnavailableStatus(error, validation.value);
2381
+ return {
2382
+ content: [{ type: "text" as const, text: JSON.stringify(payload) }],
2383
+ details: payload,
2384
+ };
2385
+ }
2080
2386
  const tuiControl = ctx.mode === "tui";
2081
2387
  if (tuiControl) {
2082
2388
  try {
@@ -2161,6 +2467,39 @@ export function registerWorkflowControl(
2161
2467
  };
2162
2468
  registerDispatchTool("packet");
2163
2469
 
2470
+ pi.on("tool_result", (event) => {
2471
+ if (
2472
+ event.toolName !== DISPATCH_TOOL ||
2473
+ !event.isError ||
2474
+ event.input.action !== "design"
2475
+ ) {
2476
+ return;
2477
+ }
2478
+ const errorText = event.content
2479
+ .flatMap((item) =>
2480
+ item.type === "text" && typeof item.text === "string"
2481
+ ? [item.text]
2482
+ : [],
2483
+ )
2484
+ .join("\n");
2485
+ const failure = safeDesignFailure(
2486
+ new Error(errorText.trim()),
2487
+ typeof (event.input.request as Record<string, unknown> | undefined)
2488
+ ?.operation === "string"
2489
+ ? String((event.input.request as Record<string, unknown>).operation)
2490
+ : "unknown",
2491
+ );
2492
+ const details =
2493
+ event.details && typeof event.details === "object"
2494
+ ? (event.details as Record<string, unknown>)
2495
+ : {};
2496
+ const visible = designFailures.get(event.toolCallId) ?? failure;
2497
+ designFailures.delete(event.toolCallId);
2498
+ return {
2499
+ content: [{ type: "text" as const, text: JSON.stringify(visible) }],
2500
+ details: { ...details, designFailure: visible },
2501
+ };
2502
+ });
2164
2503
  pi.on("input", (event) => {
2165
2504
  pendingPrompt = invokedPrompt(event.text);
2166
2505
  return { action: "continue" };
@@ -2214,6 +2553,7 @@ export function registerWorkflowControl(
2214
2553
  pi.on("session_start", async (_event, ctx) => {
2215
2554
  pendingPrompt = undefined;
2216
2555
  activePrompt = undefined;
2556
+ designFailures.clear();
2217
2557
  activity.detach();
2218
2558
  await packetRuntime.drain();
2219
2559
  await closeEngines();
@@ -2229,6 +2569,7 @@ export function registerWorkflowControl(
2229
2569
  });
2230
2570
  pi.on("session_shutdown", async () => {
2231
2571
  activePrompt = undefined;
2572
+ designFailures.clear();
2232
2573
  activity.detach();
2233
2574
  await packetRuntime.drain();
2234
2575
  await closeEngines();