@abelxiaoxing/cadence 1.4.0 → 1.4.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.
Files changed (49) hide show
  1. package/README.md +58 -4
  2. package/agents/design-explorer.md +17 -11
  3. package/agents/diagnosis-worker.md +9 -1
  4. package/agents/implementation-worker.md +3 -1
  5. package/config/plan-draft.example.json +122 -0
  6. package/config/plan-draft.multiple-tasks.example.json +345 -0
  7. package/package.json +5 -3
  8. package/prompts/abel-design.md +93 -44
  9. package/src/activity-contracts.ts +82 -0
  10. package/src/candidate-artifact.ts +143 -0
  11. package/src/candidate-patch.ts +31 -11
  12. package/src/candidate-workspace.ts +74 -0
  13. package/src/change-contract-schema.ts +191 -0
  14. package/src/change-contract.ts +5 -0
  15. package/src/change-verification.ts +896 -0
  16. package/src/child-model.ts +129 -0
  17. package/src/child-session.ts +293 -326
  18. package/src/contracts.ts +184 -76
  19. package/src/delivery-compiler.ts +103 -120
  20. package/src/delivery-revision.ts +42 -0
  21. package/src/design-control.ts +113 -4
  22. package/src/design-diagnostics.ts +150 -0
  23. package/src/durable-contracts.ts +210 -0
  24. package/src/durable-workflow.ts +148 -3570
  25. package/src/evidence-draft.ts +122 -0
  26. package/src/implement-graph.ts +3 -0
  27. package/src/implement-plan.ts +70 -0
  28. package/src/index.ts +59 -522
  29. package/src/model-source.ts +22 -0
  30. package/src/package-candidate.ts +224 -0
  31. package/src/package-workflow.ts +253 -0
  32. package/src/packet-runtime.ts +27 -48
  33. package/src/parent-provider.ts +86 -198
  34. package/src/phase-execution.ts +2248 -0
  35. package/src/pi-adapter.ts +19 -0
  36. package/src/plan-draft-summary.ts +146 -0
  37. package/src/plan-draft.ts +493 -0
  38. package/src/subagent-activity.ts +32 -66
  39. package/src/submit-tool.ts +20 -59
  40. package/src/transport-budget.ts +32 -0
  41. package/src/worker-broker.ts +37 -57
  42. package/src/workflow-policy.ts +14 -53
  43. package/src/workflow-recovery-policy.ts +95 -0
  44. package/src/workflow-scheduling.ts +105 -0
  45. package/src/workflow-state-machine.ts +105 -497
  46. package/src/workflow-status.ts +384 -0
  47. package/src/workspace-io-worker.mjs +7 -0
  48. package/src/empty-resource-loader.ts +0 -38
  49. package/src/parent-payload-bridge.ts +0 -523
package/src/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { compareCanonicalStrings } from "./canonical.ts";
2
- import { packageDeliverySource } from "./package-delivery.ts";
2
+ import { CHANGE_CONTRACT_SCHEMA } from "./change-contract-schema.ts";
3
3
  import {
4
- changeVerificationResult,
5
- executePackageVerification,
6
- phaseVerificationResult,
7
- } from "./package-verification.ts";
8
- import { captureVerificationEnvironmentIdentity } from "./verification-environment.ts";
4
+ projectDesignDiagnostic,
5
+ type SafeDesignDiagnostic,
6
+ } from "./design-diagnostics.ts";
7
+ import { openPackageWorkflowService } from "./package-workflow.ts";
8
+ import { packageContext } from "./pi-adapter.ts";
9
9
 
10
10
  export {
11
11
  type PackageDeliverySourceOptions,
@@ -19,10 +19,8 @@ export { executePackageVerification } from "./package-verification.ts";
19
19
  // the active set at session start. Eligible-stage activation is wired by the
20
20
  // workflow routing (abel-design/implement/diagnose provenance) in the prompts
21
21
  // integration; abel-init and ordinary prompts never activate dispatch.
22
- import { AsyncLocalStorage } from "node:async_hooks";
23
- import { createHash } from "node:crypto";
24
- import { homedir } from "node:os";
25
- import path, { dirname, join } from "node:path";
22
+
23
+ import { dirname, join } from "node:path";
26
24
  import { fileURLToPath } from "node:url";
27
25
  import type {
28
26
  AgentToolResult,
@@ -33,13 +31,8 @@ import type {
33
31
  ToolRenderResultOptions,
34
32
  } from "@earendil-works/pi-coding-agent";
35
33
  import { type Activation, activateTool, deactivateTool } from "./activation.ts";
36
- import { loadAgentDefinitions } from "./agent-registry.ts";
37
- import { runChildSession } from "./child-session.ts";
38
- import {
39
- type DesignEvidenceResult,
40
- LIMITS,
41
- type StructuredVerificationContract,
42
- } from "./contracts.ts";
34
+
35
+ import type { DesignEvidenceResult } from "./contracts.ts";
43
36
  import {
44
37
  CONTROL_COMMAND_PARAMETERS,
45
38
  canonicalizeControlCommandToolInput,
@@ -50,25 +43,16 @@ import {
50
43
  DesignPlanValidationError,
51
44
  } from "./delivery-compiler.ts";
52
45
  import {
53
- DesignController,
46
+ DesignControlValidationError,
54
47
  DesignFinalizationError,
55
48
  validateDesignControlRequest,
56
49
  } from "./design-control.ts";
57
50
  import { canonicalJson } from "./implement-graph.ts";
58
- import { inspectOpenSpecDelivery } from "./openspec-cli.ts";
59
51
  import { PACKET_ACTIONS, PacketRuntime } from "./packet-runtime.ts";
60
- import { ParentPayloadBridge } from "./parent-payload-bridge.ts";
61
- import { runtimeForWorkerRoute } from "./parent-provider.ts";
62
- import {
63
- inspectRoutePolicy,
64
- loadRoutePolicy,
65
- type RoutePolicyResolution,
66
- unavailableRoutePolicy,
67
- type WorkerRoutePolicy,
68
- } from "./route-policy.ts";
52
+
69
53
  import { RunStoreFormatError, RunStoreMigrationError } from "./run-store.ts";
70
- import { isSafeRegularFile } from "./safe-path.ts";
71
- import { resolveStateRoot, StateRootError } from "./state-root.ts";
54
+
55
+ import { StateRootError } from "./state-root.ts";
72
56
  import {
73
57
  ACTIVITY_DETAILS_KEY,
74
58
  ActivityController,
@@ -76,11 +60,6 @@ import {
76
60
  renderActivityResult,
77
61
  type WorkflowActivityUpdate,
78
62
  } from "./subagent-activity.ts";
79
- import {
80
- classifyCandidateContextRequest,
81
- permitsContextRead,
82
- } from "./submit-tool.ts";
83
- import { openDurableWorkflowEngine } from "./workflow-engine.ts";
84
63
 
85
64
  export {
86
65
  inspectOpenSpecDelivery,
@@ -282,9 +261,9 @@ const DESIGN_CONTROL_REQUEST_SCHEMA = {
282
261
  operationId: { type: "string" },
283
262
  gate: { type: "string", enum: ["gate-a"] },
284
263
  contract: {
285
- anyOf: [{ type: "string" }, { type: "object" }],
264
+ anyOf: [{ type: "string" }, CHANGE_CONTRACT_SCHEMA],
286
265
  description:
287
- "Structured ChangeContract with goal, acceptance, constraints and policy. Legacy prose remains readable; code hashes authority.",
266
+ "ChangeContract: {goal, acceptance:[{id,statement,verification}], constraints:[{id,statement}], policy:{writeRoots,dependencies,verificationModes}}. Pass the example's changeContract object, not its whole plan. Legacy prose remains readable; code hashes authority.",
288
267
  },
289
268
  },
290
269
  required: ["operation", "runId", "operationId", "gate", "contract"],
@@ -314,7 +293,7 @@ const DESIGN_CONTROL_REQUEST_SCHEMA = {
314
293
  content: {
315
294
  type: "string",
316
295
  maxLength: 16 * 1024 * 1024,
317
- description: "Exact bounded UTF-8 artifact content.",
296
+ description: `Exact bounded UTF-8 artifact content. For plan-draft.json, omit tracking, phase verificationInputs and relatedTests disposition to derive them. Read the complete example at ${join(PACKAGE_ROOT, "config/plan-draft.example.json")}.`,
318
297
  },
319
298
  },
320
299
  required: ["operation", "runId", "operationId", "path", "content"],
@@ -429,7 +408,7 @@ type SafeDesignFailure = {
429
408
  kind: "design-control-failure";
430
409
  operation: string;
431
410
  code: string;
432
- diagnostics: ReadonlyArray<Record<string, string | boolean>>;
411
+ diagnostics: readonly SafeDesignDiagnostic[];
433
412
  };
434
413
 
435
414
  type DesignFailureBoundary =
@@ -478,6 +457,13 @@ function classifyDesignFailure(
478
457
  field?: string;
479
458
  } {
480
459
  const message = error instanceof Error ? error.message : "";
460
+ if (error instanceof DesignControlValidationError) {
461
+ return {
462
+ code: "invalid-design-control-request",
463
+ category: "validation",
464
+ retryable: false,
465
+ };
466
+ }
481
467
  if (error instanceof DesignPlanValidationError) {
482
468
  return {
483
469
  code: "design-plan-validation-invalid",
@@ -600,50 +586,8 @@ function safeDesignFailure(
600
586
  : [];
601
587
  const diagnostics = rawDiagnostics
602
588
  .flatMap((candidate) => {
603
- if (typeof candidate === "string") {
604
- const diagnosticCode = candidate.split(":", 1)[0] ?? "";
605
- return SAFE_DESIGN_ERROR_CODE.test(diagnosticCode)
606
- ? [{ code: diagnosticCode }]
607
- : [];
608
- }
609
- if (
610
- candidate === null ||
611
- typeof candidate !== "object" ||
612
- Array.isArray(candidate)
613
- ) {
614
- return [];
615
- }
616
- const admitted = Object.fromEntries(
617
- Object.entries(candidate as Record<string, unknown>)
618
- .filter((entry): entry is [string, string | boolean] => {
619
- if (entry[0] === "retryable") {
620
- return typeof entry[1] === "boolean";
621
- }
622
- return (
623
- [
624
- "code",
625
- "taskId",
626
- "phase",
627
- "field",
628
- "category",
629
- "owner",
630
- "verificationId",
631
- "outputId",
632
- "dependencyTaskId",
633
- "producerTaskId",
634
- "producerPhase",
635
- "command",
636
- "reason",
637
- "systemCode",
638
- "exitCode",
639
- ].includes(entry[0]) &&
640
- typeof entry[1] === "string" &&
641
- SAFE_DESIGN_ERROR_CODE.test(entry[1])
642
- );
643
- })
644
- .sort(([left], [right]) => compareCanonicalStrings(left, right)),
645
- );
646
- return typeof admitted.code === "string" ? [admitted] : [];
589
+ const diagnostic = projectDesignDiagnostic(candidate);
590
+ return diagnostic ? [diagnostic] : [];
647
591
  })
648
592
  .sort((left, right) =>
649
593
  compareCanonicalStrings(canonicalJson(left), canonicalJson(right)),
@@ -693,405 +637,23 @@ export interface WorkflowControlEngine {
693
637
 
694
638
  export type WorkflowControlEngineFactory = (
695
639
  ctx: ExtensionContext,
696
- parentPayloadBridge: ParentPayloadBridge,
697
640
  ) => WorkflowControlEngine | Promise<WorkflowControlEngine>;
698
641
 
699
- function sha256(value: Uint8Array | string): string {
700
- return createHash("sha256").update(value).digest("hex");
701
- }
702
-
703
- function affectedVerification(
704
- task: Parameters<
705
- Parameters<typeof openDurableWorkflowEngine>[0]["proposeCandidate"]
706
- >[0]["task"],
707
- ): StructuredVerificationContract {
708
- if (task.affectedVerification) {
709
- return structuredClone(task.affectedVerification);
710
- }
711
- const finalPhase = task.phases.refactor ?? task.phases.green;
712
- const verification = structuredClone(finalPhase.verification);
713
- const affected = [...new Set(task.impactClosure.affectedSuite)].sort();
714
- if (affected.length === 0) return verification;
715
- if (verification.kind === "vitest") {
716
- return { ...verification, testFiles: affected };
717
- }
718
- if (verification.kind === "steps") {
719
- return {
720
- ...verification,
721
- steps: verification.steps.map((step) =>
722
- step.kind === "vitest" ? { ...step, testFiles: affected } : step,
723
- ),
724
- };
725
- }
726
- return verification;
727
- }
728
-
729
- function childRequestId(
730
- operationId: string,
731
- taskId: string,
732
- phase: string,
733
- ): string {
734
- return `child-${sha256(`${operationId}\0${taskId}\0${phase}`).slice(0, 40)}`;
735
- }
736
-
642
+ /** Pi adapter: project host capabilities before entering the package services. */
737
643
  export function openPackageWorkflowControlEngine(
738
644
  initialContext: ExtensionContext,
739
- parentPayloadBridge: ParentPayloadBridge,
740
645
  ): WorkflowControlEngine {
741
- const consumerRoot = path.resolve(initialContext.cwd);
742
- const routeResolution = loadRoutePolicy({
743
- cwd: consumerRoot,
744
- home: homedir(),
745
- ...(initialContext.model ? { parentModel: initialContext.model } : {}),
746
- });
747
- const stateRoot = resolveStateRoot({
748
- consumerRoot,
749
- xdgStateHome: process.env.XDG_STATE_HOME,
750
- });
751
- const contexts = new AsyncLocalStorage<ExtensionContext>();
752
- const design = DesignController.open({
753
- consumerRoot,
754
- stateRoot,
755
- inspectOpenSpec: inspectOpenSpecDelivery,
756
- });
757
- const implementationAgent = loadAgentDefinitions().find(
758
- (agent) => agent.role === "implementation-worker",
759
- );
760
- if (!implementationAgent) {
761
- throw new Error("implementation-worker-agent-unavailable");
762
- }
763
- const hostLimit = (name: string, fallback: number): number => {
764
- const value =
765
- process.env[name] === undefined ? fallback : Number(process.env[name]);
766
- if (!Number.isSafeInteger(value) || value < 1)
767
- throw new Error("verification-host-limit-invalid");
768
- return value;
769
- };
770
- const maxReportBytes = hostLimit(
771
- "ABEL_VERIFICATION_MAX_REPORT_BYTES",
772
- 64 * 1024 * 1024,
773
- );
774
- const workHardLimit = hostLimit("ABEL_WORK_MAX_UNITS", 512);
775
- const engine = openDurableWorkflowEngine({
776
- workHardLimit,
777
- verificationPolicy: "report-file-v4",
778
- verificationEnvironment: (plan, signal) =>
779
- captureVerificationEnvironmentIdentity(
780
- consumerRoot,
781
- [
782
- ...plan.tasks.flatMap((task) => [
783
- ...Object.values(task.phases).map((phase) => phase.verification),
784
- task.affectedVerification,
785
- task.repairVerification,
786
- ]),
787
- plan.verification.baseline.fullSuite,
788
- plan.verification.change.fullSuite,
789
- plan.verification.change.postApply,
790
- ...(plan.verification.agentsCheckpoint.verification
791
- ? [plan.verification.agentsCheckpoint.verification]
792
- : []),
793
- ],
794
- signal,
795
- ),
796
- consumerRoot,
797
- stateRoot,
798
- deliverySource: packageDeliverySource(consumerRoot, {
799
- verifyGateProof: (input) => design.verifyGateProof(input),
800
- verifyFinalizedDelivery: (input) => design.verifyFinalizedDelivery(input),
801
- }),
802
- routePolicy: routeResolution.ok
803
- ? routeResolution.policy
804
- : unavailableRoutePolicy(),
805
- proposeCandidate: async (input) => {
806
- const context = contexts.getStore();
807
- if (!context) {
808
- return { kind: "paused", code: "parent-context-unavailable" };
809
- }
810
- const phaseRuntime = await runtimeForWorkerRoute(
811
- input.route as WorkerRoutePolicy,
812
- context,
813
- parentPayloadBridge,
814
- input.signal,
815
- process.env,
816
- { onResponse: input.onHeaders },
817
- );
818
- if (!phaseRuntime.ok) {
819
- if (phaseRuntime.failure.kind === "cancelled") {
820
- return { kind: "operation-cancelled", code: "cancelled" };
821
- }
822
- throw new Error(phaseRuntime.failure.code);
823
- }
824
- const phase = input.task.phases[input.phase];
825
- if (!phase) {
826
- return { kind: "paused", code: "task-phase-unavailable" };
827
- }
828
- const taskPhases = Object.values(input.task.phases);
829
- const executionBoundary = input.artifactCorrection
830
- ? {
831
- read: [
832
- ...new Set(
833
- taskPhases.flatMap((boundary) => [
834
- ...boundary.read,
835
- ...boundary.write,
836
- ...boundary.delete,
837
- ]),
838
- ),
839
- ].sort(),
840
- write: [
841
- ...new Set(taskPhases.flatMap((boundary) => boundary.write)),
842
- ].sort(),
843
- delete: [
844
- ...new Set(taskPhases.flatMap((boundary) => boundary.delete)),
845
- ].sort(),
846
- }
847
- : {
848
- read: [
849
- ...new Set(
850
- taskPhases.flatMap((boundary) => [
851
- ...boundary.read,
852
- ...boundary.write,
853
- ...boundary.delete,
854
- ]),
855
- ),
856
- ].sort(),
857
- write: [...phase.write],
858
- delete: [...phase.delete],
859
- };
860
- executionBoundary.read = [
861
- ...new Set([
862
- ...executionBoundary.read,
863
- ...(input.contextReadPaths ?? []).filter(
864
- (relative) =>
865
- permitsContextRead(relative, input.task.roots) &&
866
- isSafeRegularFile(input.workspaceRoot, relative),
867
- ),
868
- ]),
869
- ].sort();
870
- const phaseContract = {
871
- candidateId: input.candidateArtifact.identity.candidateId,
872
- taskId: input.taskId,
873
- phase: input.phase,
874
- readSet: executionBoundary.read,
875
- writeSet: executionBoundary.write,
876
- deleteSet: executionBoundary.delete,
877
- verification: structuredClone(phase.verification),
878
- agentsImpact: input.task.agents.impact,
879
- agentsTarget: input.task.agents.target ?? null,
880
- agentsManagedOnly: true,
881
- agentsWriteAllowed: false,
882
- impactClosure: structuredClone(input.task.impactClosure),
883
- ...(input.artifactCorrection
884
- ? { artifactCorrection: structuredClone(input.artifactCorrection) }
885
- : {}),
886
- ...(input.contextRequest
887
- ? { requestedContext: structuredClone(input.contextRequest) }
888
- : {}),
889
- ...(input.recoveryFeedback
890
- ? { recoveryFeedback: structuredClone(input.recoveryFeedback) }
891
- : {}),
892
- ...(input.repair
893
- ? {
894
- repair: {
895
- attempt: input.repair.attempt,
896
- attribution: input.repair.attribution,
897
- failureIdentities: [...input.repair.failureIdentities],
898
- verification: structuredClone(input.task.repairVerification),
899
- inBoundaryOnly: true,
900
- },
901
- }
902
- : {}),
903
- };
904
- const child = await runChildSession({
905
- cwd: input.workspaceRoot,
906
- modelRuntime: phaseRuntime.modelRuntime,
907
- model: phaseRuntime.model,
908
- systemPrompt: [
909
- implementationAgent.content,
910
- input.task.objective,
911
- input.task.context.agents,
912
- input.task.context.contract,
913
- "Use recoveryFeedback to change the failing approach. For compact-patch, prefer exact replace operations and omit unchanged bodies; submit one complete atomic patch. Never repeat an unchanged failing submission or weaken verification to obtain a pass.",
914
- `<phase-contract>${JSON.stringify(phaseContract)}</phase-contract>`,
915
- ].join("\n\n"),
916
- requestId: childRequestId(input.operationId, input.taskId, input.phase),
917
- taskId: input.taskId,
918
- role: "implementation-worker",
919
- phase: input.phase,
920
- output: "diff",
921
- roots: input.task.roots.map((root) =>
922
- path.resolve(input.workspaceRoot, root),
923
- ),
924
- allowedPaths: [
925
- ...new Set([
926
- ...executionBoundary.read,
927
- ...executionBoundary.write,
928
- ...executionBoundary.delete,
929
- ]),
930
- ],
931
- timeoutMs: LIMITS.phaseTimeoutMs,
932
- signal: input.signal,
933
- failureOverride: phaseRuntime.failureOverride,
934
- ledgerProjection: input.ledgerProjection,
935
- candidateArtifact: input.candidateArtifact,
936
- onStreamProgress: input.onProgress,
937
- });
938
- if (!child.ok) {
939
- if (child.failure.kind === "transport") {
940
- throw new Error(child.failure.code);
941
- }
942
- if (child.failure.kind === "cancelled") {
943
- return { kind: "operation-cancelled", code: "cancelled" };
944
- }
945
- if (child.failure.kind === "approval-boundary") {
946
- return { kind: "approval-needed", code: child.failure.code };
947
- }
948
- if (child.failure.kind === "result-limit") {
949
- return { kind: "retryable", code: "needs-task-split" };
950
- }
951
- const attemptDiagnostic = {
952
- finalCategory: child.classification.finalCategory,
953
- submitAttempts: child.classification.attempts,
954
- schema: child.classification.schema,
955
- identityMismatch: Object.entries(child.classification.identity)
956
- .filter(([, matches]) => !matches)
957
- .map(([dimension]) => dimension),
958
- };
959
- return child.failure.kind === "environment" ||
960
- child.failure.kind === "verification-adapter"
961
- ? { kind: "paused", code: child.failure.code, attemptDiagnostic }
962
- : { kind: "retryable", code: child.failure.code, attemptDiagnostic };
963
- }
964
- const result = child.result;
965
- if (result.kind === "context-request") {
966
- return classifyCandidateContextRequest(result, {
967
- phase: input.phase,
968
- contextReadRoots: input.task.roots,
969
- readPaths: phase.read,
970
- writePaths: phase.write,
971
- deletePaths: phase.delete,
972
- taskPaths: [
973
- ...new Set(
974
- Object.values(input.task.phases).flatMap((boundary) => [
975
- ...boundary.read,
976
- ...boundary.write,
977
- ...boundary.delete,
978
- ]),
979
- ),
980
- ],
981
- redWritePaths: input.task.phases.red.write,
982
- agents: {
983
- impact: input.task.agents.impact,
984
- ...(input.task.agents.target
985
- ? { target: input.task.agents.target }
986
- : {}),
987
- },
988
- });
989
- }
990
- if (result.kind !== "sealed-candidate") {
991
- return { kind: "retryable", code: "candidate-diff-invalid" };
992
- }
993
- return {
994
- kind: "sealed-candidate",
995
- candidateId: result.candidateId,
996
- artifactHash: result.artifactHash,
997
- bytes: result.bytes,
998
- paths: [...result.paths],
999
- };
1000
- },
1001
- verifyPhase: async (input) =>
1002
- phaseVerificationResult(
1003
- await executePackageVerification({
1004
- maxReportBytes,
1005
- executionWritePaths: input.executionWritePaths,
1006
- root: input.root,
1007
- dependencyOwner: consumerRoot,
1008
- verification: input.verification,
1009
- signal: input.signal,
1010
- }),
1011
- ),
1012
- verifyChange: async (input) => {
1013
- const verifications = input.verification
1014
- ? [input.verification]
1015
- : input.plan.tasks.map((task) => affectedVerification(task));
1016
- for (const verification of verifications) {
1017
- const result = await executePackageVerification({
1018
- maxReportBytes,
1019
- executionWritePaths: input.plan.tasks.flatMap((task) =>
1020
- Object.values(task.phases).flatMap((phase) => [
1021
- ...phase.write,
1022
- ...phase.delete,
1023
- ]),
1024
- ),
1025
- root: input.root,
1026
- dependencyOwner: consumerRoot,
1027
- verification,
1028
- signal: input.signal,
1029
- });
1030
- const observed = changeVerificationResult(result);
1031
- if (!observed.ok) return observed;
1032
- }
1033
- return {
1034
- ok: true,
1035
- exitCode: 0,
1036
- classification: "expected-green",
1037
- };
1038
- },
1039
- });
646
+ const service = openPackageWorkflowService(packageContext(initialContext));
1040
647
  return {
1041
- async execute(
1042
- command: unknown,
1043
- context = initialContext,
1044
- signal,
1045
- onActivity,
1046
- ) {
1047
- const operationRouteResolution = loadRoutePolicy({
1048
- cwd: consumerRoot,
1049
- home: homedir(),
1050
- ...(context.model ? { parentModel: context.model } : {}),
1051
- });
1052
- engine.updateRoutePolicy(
1053
- operationRouteResolution.ok
1054
- ? operationRouteResolution.policy
1055
- : unavailableRoutePolicy(),
1056
- );
1057
- const validation = validateControlCommand(command);
1058
- if (!validation.ok) {
1059
- const error = new Error(validation.code);
1060
- error.name = "ControlCommandError";
1061
- throw error;
1062
- }
1063
- return contexts.run(context, async () => {
1064
- const outcome = await engine.execute(
1065
- validation.value,
1066
- signal,
1067
- onActivity,
1068
- );
1069
- const routePolicy = visibleRoutePolicyStatus(
1070
- operationRouteResolution,
1071
- engine.routePolicyStatus(),
1072
- );
1073
- return { ...outcome, routePolicy };
1074
- });
1075
- },
1076
- executeDesign(request: unknown) {
1077
- return design.execute(request);
1078
- },
1079
- executeAmendment(change: string, batchId: string, request: unknown) {
1080
- return engine.amend(change, batchId, request, (assertAuthority) =>
1081
- design.executeWithAuthority(request, assertAuthority),
648
+ ...service,
649
+ execute(command, context = initialContext, signal, onActivity) {
650
+ return service.execute(
651
+ command,
652
+ packageContext(context),
653
+ signal,
654
+ onActivity,
1082
655
  );
1083
656
  },
1084
- assertDesignRun(runId: string) {
1085
- design.assertDesignRun(runId);
1086
- },
1087
- recordDesignEvidence(input) {
1088
- return design.recordEvidence(input);
1089
- },
1090
- async close() {
1091
- contexts.disable();
1092
- await engine.close();
1093
- design.close();
1094
- },
1095
657
  };
1096
658
  }
1097
659
 
@@ -1128,23 +690,10 @@ function runStoreResetError(error: RunStoreFormatError): Error {
1128
690
  return reset;
1129
691
  }
1130
692
 
1131
- function visibleRoutePolicyStatus(
1132
- resolution: RoutePolicyResolution,
1133
- brokerStatus: Record<string, unknown> | undefined,
1134
- ): Record<string, unknown> {
1135
- if (!resolution.ok) return inspectRoutePolicy(resolution);
1136
- const inspected = brokerStatus ?? inspectRoutePolicy(resolution);
1137
- return {
1138
- ...inspected,
1139
- source: { kind: resolution.source.kind },
1140
- };
1141
- }
1142
-
1143
693
  export function registerWorkflowControl(
1144
694
  pi: ExtensionAPI,
1145
695
  engineFactory: WorkflowControlEngineFactory = openPackageWorkflowControlEngine,
1146
696
  ): void {
1147
- const parentPayloadBridge = new ParentPayloadBridge();
1148
697
  const activity = new ActivityController();
1149
698
  const engines = new Map<string, Promise<WorkflowControlEngine>>();
1150
699
  const designOperations = new Set<Promise<void>>();
@@ -1179,13 +728,13 @@ export function registerWorkflowControl(
1179
728
  return true;
1180
729
  }
1181
730
  })();
1182
- const packetRuntime = new PacketRuntime({ activation, parentPayloadBridge });
731
+ const packetRuntime = new PacketRuntime({ activation });
1183
732
 
1184
733
  const engineFor = (ctx: ExtensionContext) => {
1185
734
  const key = ctx.cwd;
1186
735
  const existing = engines.get(key);
1187
736
  if (existing) return existing;
1188
- const opened = Promise.resolve(engineFactory(ctx, parentPayloadBridge));
737
+ const opened = Promise.resolve(engineFactory(ctx));
1189
738
  engines.set(key, opened);
1190
739
  void opened.catch(() => {
1191
740
  if (engines.get(key) === opened) engines.delete(key);
@@ -1241,7 +790,6 @@ export function registerWorkflowControl(
1241
790
  activePrompt = undefined;
1242
791
  await packetRuntime.drain();
1243
792
  deactivate();
1244
- parentPayloadBridge.clear();
1245
793
  };
1246
794
  const exitStage = async () => {
1247
795
  if (exitingStage) throw new Error("stage-control-mismatch");
@@ -1383,7 +931,14 @@ export function registerWorkflowControl(
1383
931
  throw new Error("stage-control-mismatch");
1384
932
  }
1385
933
  const parsed = validateDesignControlRequest(record.request);
1386
- if (!parsed.ok) throw new Error(parsed.code);
934
+ if (!parsed.ok) {
935
+ const error = new DesignControlValidationError(parsed.diagnostics);
936
+ designFailures.set(
937
+ toolCallId,
938
+ safeDesignFailure(error, "validation"),
939
+ );
940
+ throw error;
941
+ }
1387
942
  const request = parsed.value;
1388
943
  if (
1389
944
  request.operation === "bind-change" ||
@@ -1506,7 +1061,16 @@ export function registerWorkflowControl(
1506
1061
  throw new Error("stage-control-mismatch");
1507
1062
  }
1508
1063
  const designRequest = validateDesignControlRequest(record.request);
1509
- if (!designRequest.ok) throw new Error(designRequest.code);
1064
+ if (!designRequest.ok) {
1065
+ const error = new DesignControlValidationError(
1066
+ designRequest.diagnostics,
1067
+ );
1068
+ designFailures.set(
1069
+ toolCallId,
1070
+ safeDesignFailure(error, "validation"),
1071
+ );
1072
+ throw error;
1073
+ }
1510
1074
  let settleDesign!: () => void;
1511
1075
  const settled = new Promise<void>((resolve) => {
1512
1076
  settleDesign = resolve;
@@ -1588,7 +1152,7 @@ export function registerWorkflowControl(
1588
1152
  ? await packetRuntime.execute(
1589
1153
  record.action,
1590
1154
  operation,
1591
- ctx,
1155
+ packageContext(ctx),
1592
1156
  signal,
1593
1157
  activity.observe(
1594
1158
  toolCallId,
@@ -1598,7 +1162,7 @@ export function registerWorkflowControl(
1598
1162
  : await packetRuntime.execute(
1599
1163
  record.action,
1600
1164
  operation,
1601
- ctx,
1165
+ packageContext(ctx),
1602
1166
  signal,
1603
1167
  );
1604
1168
  let payload:
@@ -1814,7 +1378,7 @@ export function registerWorkflowControl(
1814
1378
  }
1815
1379
  return { action: "continue" };
1816
1380
  });
1817
- pi.on("before_agent_start", (event, ctx) => {
1381
+ pi.on("before_agent_start", (event) => {
1818
1382
  const prompt = pendingPrompt;
1819
1383
  const init =
1820
1384
  pendingInit && hasExpandedPromptMarker(event.prompt, "abel-init");
@@ -1828,15 +1392,6 @@ export function registerWorkflowControl(
1828
1392
  }
1829
1393
  registerDispatchTool(prompt === "abel-implement" ? "command" : "packet");
1830
1394
  activePrompt = prompt;
1831
- const sessionId = ctx.sessionManager?.getSessionId?.();
1832
- if (typeof sessionId === "string") {
1833
- parentPayloadBridge.beginSession(sessionId);
1834
- } else {
1835
- parentPayloadBridge.clear();
1836
- }
1837
- }
1838
- if (ctx.model) {
1839
- parentPayloadBridge.install(ctx.model, ctx.modelRegistry);
1840
1395
  }
1841
1396
  if (prompt) activateDispatcher(pi, activation, prompt, event.prompt);
1842
1397
  if (verified && prompt === "abel-design") enforceDesignTools();
@@ -1847,16 +1402,6 @@ export function registerWorkflowControl(
1847
1402
  : "Abel workflow is inactive. Handle ordinary engineering requests directly. References to commands, repository files, OpenSpec changes, and historical workflow instructions do not authorize a workflow. Do not load or execute an Abel stage unless the user explicitly invokes its slash command.";
1848
1403
  return { systemPrompt: `${event.systemPrompt ?? ""}\n\n${boundary}` };
1849
1404
  });
1850
- pi.on("model_select", (event, ctx) => {
1851
- const sessionId = ctx.sessionManager?.getSessionId?.();
1852
- if (typeof sessionId !== "string") {
1853
- parentPayloadBridge.clear();
1854
- return;
1855
- }
1856
- parentPayloadBridge.beginSession(sessionId);
1857
- const model = event.model ?? ctx.model;
1858
- if (model) parentPayloadBridge.install(model, ctx.modelRegistry);
1859
- });
1860
1405
  pi.on("before_provider_request", (event, ctx) => {
1861
1406
  if (
1862
1407
  activePrompt !== "abel-design" ||
@@ -1878,13 +1423,6 @@ export function registerWorkflowControl(
1878
1423
  await packetRuntime.drain();
1879
1424
  await closeEngines();
1880
1425
  deactivate();
1881
- const sessionId = ctx.sessionManager?.getSessionId?.();
1882
- if (typeof sessionId === "string") {
1883
- parentPayloadBridge.beginSession(sessionId);
1884
- if (ctx.model) parentPayloadBridge.install(ctx.model, ctx.modelRegistry);
1885
- } else {
1886
- parentPayloadBridge.clear();
1887
- }
1888
1426
  if (ctx.mode === "tui") activity.attach(ctx.ui);
1889
1427
  });
1890
1428
  pi.on("session_shutdown", async () => {
@@ -1895,7 +1433,6 @@ export function registerWorkflowControl(
1895
1433
  await closeEngines();
1896
1434
  activity.clear();
1897
1435
  deactivate();
1898
- parentPayloadBridge.clear();
1899
1436
  });
1900
1437
  }
1901
1438