@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/README.md +7 -1
- package/agents/diagnosis-worker.md +6 -2
- package/config/routes.example.json +0 -1
- package/package.json +1 -1
- package/prompts/abel-design.md +15 -4
- package/skills/abel-workflow/SKILL.md +5 -3
- package/src/apply-transaction.ts +1 -5
- package/src/child-session.ts +37 -46
- package/src/contracts.ts +278 -32
- package/src/control-contracts.ts +62 -0
- package/src/delivery-compiler.ts +239 -124
- package/src/design-control.ts +112 -44
- package/src/design-journal.ts +22 -9
- package/src/file-snapshot.ts +1 -1
- package/src/index.ts +393 -52
- package/src/packet-runtime.ts +79 -21
- package/src/parent-provider.ts +63 -30
- package/src/route-policy.ts +58 -14
- package/src/run-store.ts +308 -37
- package/src/subagent-activity.ts +82 -6
- package/src/submit-tool.ts +182 -10
- package/src/task-ledger.ts +1 -2
- package/src/worker-broker.ts +72 -13
- package/src/workflow-engine.ts +219 -31
- package/src/workspace-store.ts +2 -10
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: [
|
|
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([
|
|
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
|
|
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
|
|
2162
|
+
const PACKET_PARAMETERS = {
|
|
1879
2163
|
type: "object",
|
|
1880
2164
|
properties: {
|
|
1881
|
-
|
|
1882
|
-
type: "string",
|
|
1883
|
-
enum: ["start", "status", "resume", "rebind", "cancel", "discard"],
|
|
1884
|
-
},
|
|
1885
|
-
stage: {
|
|
2165
|
+
action: {
|
|
1886
2166
|
type: "string",
|
|
1887
|
-
enum: ["
|
|
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
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
payload
|
|
1972
|
-
|
|
1973
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|