@williambeto/ai-workflow 2.8.1 → 2.9.0

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.
@@ -710,8 +710,13 @@ var EvidenceCollector = class {
710
710
  let overallStatus = "PASS";
711
711
  if (results.some((result) => ["FAIL", "BLOCKED", "FAIL_QUALITY_GATE"].includes(result.status))) overallStatus = "FAIL_QUALITY_GATE";
712
712
  else if (implementation && deliveryChangedFiles.length > 0 && !validationObserved) overallStatus = "BLOCKED";
713
- else if (behaviorTestRequired && !passingBehaviorTest) overallStatus = "BLOCKED";
714
- else if (["FAIL", "BLOCKED", "FAIL_QUALITY_GATE"].includes(policyValidation.overallStatus)) overallStatus = "FAIL_QUALITY_GATE";
713
+ else if (behaviorTestRequired && !passingBehaviorTest) {
714
+ if (this.riskLevel === "high") {
715
+ overallStatus = "BLOCKED";
716
+ } else {
717
+ overallStatus = "PASS_WITH_NOTES";
718
+ }
719
+ } else if (["FAIL", "BLOCKED", "FAIL_QUALITY_GATE"].includes(policyValidation.overallStatus)) overallStatus = "FAIL_QUALITY_GATE";
715
720
  else if (results.some((result) => result.status === "PASS_WITH_NOTES") || policyValidation.overallStatus === "PASS_WITH_NOTES") overallStatus = "PASS_WITH_NOTES";
716
721
  if (!artifactFidelityCheck || !artifactFidelityCheck.passed) {
717
722
  overallStatus = "BLOCKED";
@@ -757,12 +762,16 @@ var EvidenceCollector = class {
757
762
  "configure or run the smallest relevant validator"
758
763
  ));
759
764
  if (behaviorTestRequired && behaviorTests.length === 0) {
760
- limitations.push(blockingDiagnostic(
761
- "behavior-test",
762
- "UI or test-capable executable behavior changed without an automated behavior test",
763
- "a relevant automated behavior test must pass",
764
- "run or configure the focused behavior test; build, lint, and screenshots are not substitutes"
765
- ));
765
+ if (this.riskLevel === "high") {
766
+ limitations.push(blockingDiagnostic(
767
+ "behavior-test",
768
+ "UI or test-capable executable behavior changed without an automated behavior test",
769
+ "a relevant automated behavior test must pass",
770
+ "run or configure the focused behavior test; build, lint, and screenshots are not substitutes"
771
+ ));
772
+ } else {
773
+ limitations.push("behavior-test: UI or executable behavior changed without an automated behavior test (allowed for low/medium risk tasks)");
774
+ }
766
775
  }
767
776
  for (const result of results) {
768
777
  if (["FAIL", "BLOCKED", "FAIL_QUALITY_GATE"].includes(result.status)) {
@@ -1044,4 +1053,4 @@ export {
1044
1053
  EvidenceCollector,
1045
1054
  ValidationPlanner
1046
1055
  };
1047
- //# sourceMappingURL=chunk-LD7EHAU2.js.map
1056
+ //# sourceMappingURL=chunk-GLYX3HEY.js.map
@@ -235,6 +235,8 @@ function isDestructiveCommand(command) {
235
235
  return gitCommandIsDestructive(command);
236
236
  }
237
237
  var OpenCodeAdapter = class {
238
+ runtime = "opencode";
239
+ provenance = "opencode-adapter";
238
240
  cwd;
239
241
  constructor({ cwd = process.cwd() } = {}) {
240
242
  this.cwd = cwd;
@@ -572,8 +574,153 @@ AIWK_ORCHESTRATED_CHILD: The parent execute process exclusively owns collect-evi
572
574
  }
573
575
  };
574
576
 
575
- // src/core/sdd/validator.ts
577
+ // src/core/runtime/codex-runtime-adapter.ts
578
+ import { spawnSync as spawnSync2, execSync as execSync2 } from "child_process";
576
579
  import fs from "fs/promises";
580
+ import os from "os";
581
+ import path from "path";
582
+ var SPECIALIZED_AGENTS = /* @__PURE__ */ new Set(["Nexus", "Orion", "Astra", "Sage", "Phoenix"]);
583
+ function nativeAgentName(agent) {
584
+ return agent.trim().toLowerCase();
585
+ }
586
+ var CodexRuntimeAdapter = class {
587
+ runtime = "codex";
588
+ provenance = "codex-cli-jsonl";
589
+ cwd;
590
+ constructor({ cwd = process.cwd() } = {}) {
591
+ this.cwd = cwd;
592
+ }
593
+ async isProjectTrusted() {
594
+ const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
595
+ const configPath = path.join(codexHome, "config.toml");
596
+ const config = await fs.readFile(configPath, "utf8").catch(() => "");
597
+ if (!config) return false;
598
+ const normalizedCwd = path.resolve(this.cwd).replaceAll("\\", "\\\\");
599
+ const section = new RegExp(
600
+ `(?:^|\\n)\\[projects\\.${JSON.stringify(normalizedCwd).replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")}\\]\\s*\\n([\\s\\S]*?)(?=\\n\\[|$)`,
601
+ "m"
602
+ ).exec(config)?.[1] || "";
603
+ return /^trust_level\s*=\s*["']trusted["']\s*$/m.test(section);
604
+ }
605
+ async inspect() {
606
+ try {
607
+ const help = spawnSync2("codex", ["exec", "--help"], { encoding: "utf8" });
608
+ const available = help.status === 0;
609
+ const helpText = `${help.stdout || ""}
610
+ ${help.stderr || ""}`;
611
+ const projectTrusted = await this.isProjectTrusted();
612
+ const configuredAgents = await Promise.all(
613
+ [...SPECIALIZED_AGENTS].map(
614
+ (agent) => fs.access(path.join(this.cwd, ".codex", "agents", `${nativeAgentName(agent)}.toml`)).then(() => true).catch(() => false)
615
+ )
616
+ );
617
+ return {
618
+ available,
619
+ supports: {
620
+ run: available,
621
+ server: false,
622
+ formatJson: /--json\b/.test(helpText),
623
+ agent: projectTrusted && configuredAgents.every(Boolean),
624
+ model: /--model\b/.test(helpText)
625
+ },
626
+ projectTrusted
627
+ };
628
+ } catch {
629
+ return {
630
+ available: false,
631
+ supports: { run: false, server: false, formatJson: false, agent: false, model: false },
632
+ projectTrusted: false
633
+ };
634
+ }
635
+ }
636
+ async execute(_message, { agent = null, readOnly = false } = {}) {
637
+ const inspection = await this.inspect();
638
+ const requestedAgent = agent || null;
639
+ if (!readOnly) {
640
+ let branch = "";
641
+ try {
642
+ branch = execSync2("git branch --show-current", { cwd: this.cwd, encoding: "utf8", stdio: "pipe" }).trim();
643
+ } catch {
644
+ }
645
+ if (["main", "master"].includes(branch) && process.env.AI_WORKFLOW_BYPASS_PROTECTED_BRANCH !== "true") {
646
+ return {
647
+ success: false,
648
+ status: "BLOCKED",
649
+ error: "FAIL_PROTECTED_BRANCH: Direct writes to protected branch are prohibited.",
650
+ commandsRun: [],
651
+ eventCount: 0,
652
+ runtimeAgentRequested: requestedAgent,
653
+ runtimeAgentApplied: null,
654
+ runtimeAgentSupported: inspection.supports.agent,
655
+ runtimeAgentSelectionMode: "native-delegation",
656
+ runtimeActorConfirmation: "unavailable",
657
+ output: ""
658
+ };
659
+ }
660
+ }
661
+ if (!inspection.available || !inspection.supports.formatJson) {
662
+ return {
663
+ success: false,
664
+ status: "BLOCKED",
665
+ error: "Codex CLI with `exec --json` is required for observed workflow execution.",
666
+ commandsRun: [],
667
+ eventCount: 0,
668
+ runtimeAgentRequested: requestedAgent,
669
+ runtimeAgentApplied: null,
670
+ runtimeAgentSupported: inspection.supports.agent,
671
+ runtimeAgentSelectionMode: "native-delegation",
672
+ runtimeActorConfirmation: "unavailable",
673
+ output: ""
674
+ };
675
+ }
676
+ if (inspection.projectTrusted === false) {
677
+ return {
678
+ success: false,
679
+ status: "BLOCKED",
680
+ error: "Codex project is not trusted; project-local native agents are disabled until the repository is trusted in Codex.",
681
+ commandsRun: [],
682
+ eventCount: 0,
683
+ runtimeAgentRequested: requestedAgent,
684
+ runtimeAgentApplied: null,
685
+ runtimeAgentSupported: false,
686
+ runtimeAgentSelectionMode: "unavailable",
687
+ runtimeActorConfirmation: "unavailable",
688
+ output: ""
689
+ };
690
+ }
691
+ if (!inspection.supports.agent) {
692
+ return {
693
+ success: false,
694
+ status: "BLOCKED",
695
+ error: "Codex native agent configuration is incomplete; cannot confirm the requested workflow owner.",
696
+ commandsRun: [],
697
+ eventCount: 0,
698
+ runtimeAgentRequested: requestedAgent,
699
+ runtimeAgentApplied: null,
700
+ runtimeAgentSupported: false,
701
+ runtimeAgentSelectionMode: "unavailable",
702
+ runtimeActorConfirmation: "unavailable",
703
+ output: ""
704
+ };
705
+ }
706
+ return {
707
+ success: false,
708
+ status: "BLOCKED",
709
+ error: "Codex CLI `exec` does not provide stable custom-agent selection and observed ownership. AI Workflow Kit blocks CLI delivery rather than claiming Astra execution. Use interactive Codex in this trusted repository; a future app-server adapter is required for CLI orchestration.",
710
+ commandsRun: [],
711
+ eventCount: 0,
712
+ runtimeAgentRequested: requestedAgent,
713
+ runtimeAgentApplied: null,
714
+ runtimeAgentSupported: true,
715
+ runtimeAgentSelectionMode: "unavailable",
716
+ runtimeActorConfirmation: "unavailable",
717
+ output: ""
718
+ };
719
+ }
720
+ };
721
+
722
+ // src/core/sdd/validator.ts
723
+ import fs2 from "fs/promises";
577
724
  var SpecValidator = class {
578
725
  validateContent(content, requiredStatus = "APPROVED") {
579
726
  const title = content.split(/\r?\n/, 1)[0]?.trim() || "";
@@ -622,7 +769,7 @@ var SpecValidator = class {
622
769
  */
623
770
  async validate(filePath) {
624
771
  try {
625
- const content = await fs.readFile(filePath, "utf8");
772
+ const content = await fs2.readFile(filePath, "utf8");
626
773
  return this.validateContent(content, "APPROVED");
627
774
  } catch (error) {
628
775
  if (error.code === "ENOENT") {
@@ -634,9 +781,9 @@ var SpecValidator = class {
634
781
  };
635
782
 
636
783
  // src/core/handoff/handoff-engine.ts
637
- import { execSync as execSync2 } from "child_process";
638
- import fs2 from "fs/promises";
639
- import path from "path";
784
+ import { execSync as execSync3 } from "child_process";
785
+ import fs3 from "fs/promises";
786
+ import path2 from "path";
640
787
  var HandoffEngine = class {
641
788
  cwd;
642
789
  constructor({ cwd }) {
@@ -649,9 +796,9 @@ var HandoffEngine = class {
649
796
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
650
797
  let specsContent = "";
651
798
  for (const specPath of specPaths) {
652
- const absolutePath = path.isAbsolute(specPath) ? specPath : path.join(this.cwd, specPath);
799
+ const absolutePath = path2.isAbsolute(specPath) ? specPath : path2.join(this.cwd, specPath);
653
800
  try {
654
- const content = await fs2.readFile(absolutePath, "utf8");
801
+ const content = await fs3.readFile(absolutePath, "utf8");
655
802
  specsContent += `### File: ${specPath}
656
803
 
657
804
  ${content}
@@ -667,7 +814,7 @@ ${content}
667
814
  }
668
815
  let gitDiff = "No changes detected.";
669
816
  try {
670
- gitDiff = execSync2("git diff HEAD", { cwd: this.cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || "No changes detected.";
817
+ gitDiff = execSync3("git diff HEAD", { cwd: this.cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || "No changes detected.";
671
818
  } catch (err) {
672
819
  gitDiff = `[Error capturing diff: ${err.message}]`;
673
820
  }
@@ -678,9 +825,9 @@ ${content}
678
825
  evidenceJson = JSON.stringify(data, null, 2);
679
826
  evidenceSummary = `Status: ${data.status || data.internalStatus || "unknown"}, Commands: ${data.commands?.length || 0}`;
680
827
  } else {
681
- const evidencePath = path.join(this.cwd, "EVIDENCE.json");
828
+ const evidencePath = path2.join(this.cwd, "EVIDENCE.json");
682
829
  try {
683
- const content = await fs2.readFile(evidencePath, "utf8");
830
+ const content = await fs3.readFile(evidencePath, "utf8");
684
831
  const data = JSON.parse(content);
685
832
  evidenceJson = JSON.stringify(data, null, 2);
686
833
  evidenceSummary = `Status: ${data.status}, Commands: ${data.commands?.length || 0}`;
@@ -688,16 +835,16 @@ ${content}
688
835
  }
689
836
  }
690
837
  const possibleTemplatePaths = [
691
- path.join(this.cwd, ".ai-workflow/templates/HANDOFF.template.md"),
692
- path.join(this.cwd, ".ai-workflow/harness/handoffs/HANDOFF.template.md"),
693
- path.join(getPackageRoot(), "dist-assets/templates/HANDOFF.template.md"),
694
- path.join(getPackageRoot(), "dist-assets/harness/handoffs/HANDOFF.template.md")
838
+ path2.join(this.cwd, ".ai-workflow/templates/HANDOFF.template.md"),
839
+ path2.join(this.cwd, ".ai-workflow/harness/handoffs/HANDOFF.template.md"),
840
+ path2.join(getPackageRoot(), "dist-assets/templates/HANDOFF.template.md"),
841
+ path2.join(getPackageRoot(), "dist-assets/harness/handoffs/HANDOFF.template.md")
695
842
  ];
696
843
  let template = null;
697
844
  let lastError = null;
698
845
  for (const tPath of possibleTemplatePaths) {
699
846
  try {
700
- template = await fs2.readFile(tPath, "utf8");
847
+ template = await fs3.readFile(tPath, "utf8");
701
848
  break;
702
849
  } catch (err) {
703
850
  lastError = err;
@@ -709,18 +856,18 @@ ${possibleTemplatePaths.join("\n")}
709
856
  Last error: ${lastError?.message}`);
710
857
  }
711
858
  const packet = template.replace("${TASK_ID}", taskId || "unknown").replace("${TIMESTAMP}", timestamp).replace("${AUTHOR}", author || "AI Workflow Kit").replace("${STATUS}", status || "IN_PROGRESS").replace("${SPECS_CONTENT}", specsContent || "No specs provided.").replace("${EVIDENCE_SUMMARY}", evidenceSummary).replace("${EVIDENCE_JSON}", evidenceJson).replace("${GIT_DIFF}", gitDiff).replace("${NEXT_ACTIONS}", nextActions || "Awaiting manager assignment.");
712
- const handoffDir = path.join(this.cwd, ".ai-workflow/handoffs");
713
- await fs2.mkdir(handoffDir, { recursive: true });
859
+ const handoffDir = path2.join(this.cwd, ".ai-workflow/handoffs");
860
+ await fs3.mkdir(handoffDir, { recursive: true });
714
861
  const packetName = `HANDOFF-${taskId || "TMP"}-${Date.now()}.md`;
715
- const packetPath = path.join(handoffDir, packetName);
716
- await fs2.writeFile(packetPath, packet);
862
+ const packetPath = path2.join(handoffDir, packetName);
863
+ await fs3.writeFile(packetPath, packet);
717
864
  return packetPath;
718
865
  }
719
866
  };
720
867
 
721
868
  // src/core/healing/healer-engine.ts
722
- import fs3 from "fs/promises";
723
- import path2 from "path";
869
+ import fs4 from "fs/promises";
870
+ import path3 from "path";
724
871
  import crypto from "crypto";
725
872
 
726
873
  // src/core/statuses.ts
@@ -793,7 +940,7 @@ var HealerEngine = class {
793
940
  this.ledger = ledger;
794
941
  }
795
942
  get statePath() {
796
- return path2.join(this.cwd, ".ai-workflow", `healing-state-${this.taskSlug}.json`);
943
+ return path3.join(this.cwd, ".ai-workflow", `healing-state-${this.taskSlug}.json`);
797
944
  }
798
945
  fingerprint(result) {
799
946
  return crypto.createHash("sha256").update(stableJson(collectBlockingFindings(result))).digest("hex");
@@ -813,18 +960,18 @@ var HealerEngine = class {
813
960
  return false;
814
961
  }
815
962
  async writeState(state) {
816
- await fs3.mkdir(path2.dirname(this.statePath), { recursive: true });
817
- await fs3.writeFile(this.statePath, JSON.stringify(state, null, 2));
963
+ await fs4.mkdir(path3.dirname(this.statePath), { recursive: true });
964
+ await fs4.writeFile(this.statePath, JSON.stringify(state, null, 2));
818
965
  }
819
966
  async resetState() {
820
967
  try {
821
- await fs3.unlink(this.statePath);
968
+ await fs4.unlink(this.statePath);
822
969
  } catch {
823
970
  }
824
971
  }
825
972
  async writeRemediationRequest({ attempt, result }) {
826
- const requestPath = path2.join(this.cwd, ".ai-workflow", "remediation-request.json");
827
- await fs3.mkdir(path2.dirname(requestPath), { recursive: true });
973
+ const requestPath = path3.join(this.cwd, ".ai-workflow", "remediation-request.json");
974
+ await fs4.mkdir(path3.dirname(requestPath), { recursive: true });
828
975
  const request = {
829
976
  taskSlug: this.taskSlug,
830
977
  executionMode: this.mode,
@@ -835,7 +982,7 @@ var HealerEngine = class {
835
982
  findingsFingerprint: this.fingerprint(result),
836
983
  instruction: "Apply the smallest safe correction for the listed gates, then rerun validation. Do not broaden scope."
837
984
  };
838
- await fs3.writeFile(requestPath, JSON.stringify(request, null, 2));
985
+ await fs4.writeFile(requestPath, JSON.stringify(request, null, 2));
839
986
  return requestPath;
840
987
  }
841
988
  async run({
@@ -942,8 +1089,8 @@ var HealerEngine = class {
942
1089
  };
943
1090
 
944
1091
  // src/core/evidence/evidence-ledger.ts
945
- import fs4 from "fs/promises";
946
- import path3 from "path";
1092
+ import fs5 from "fs/promises";
1093
+ import path4 from "path";
947
1094
  import { createHash } from "crypto";
948
1095
  var EvidenceLedger = class {
949
1096
  cwd;
@@ -996,8 +1143,8 @@ var EvidenceLedger = class {
996
1143
  * Saves the ledger events to a JSON file.
997
1144
  */
998
1145
  async save(filePath) {
999
- const targetPath = path3.isAbsolute(filePath) ? filePath : path3.join(this.cwd, filePath);
1000
- await fs4.mkdir(path3.dirname(targetPath), { recursive: true });
1146
+ const targetPath = path4.isAbsolute(filePath) ? filePath : path4.join(this.cwd, filePath);
1147
+ await fs5.mkdir(path4.dirname(targetPath), { recursive: true });
1001
1148
  for (const event of this.events) {
1002
1149
  if (event.workflowId !== this.workflowId) {
1003
1150
  throw new Error(`Ledger workflowId conflict: '${event.workflowId}' does not match '${this.workflowId}'.`);
@@ -1005,9 +1152,9 @@ var EvidenceLedger = class {
1005
1152
  }
1006
1153
  const serialized = JSON.stringify(this.events, null, 2);
1007
1154
  const contentHash = createHash("sha256").update(serialized).digest("hex");
1008
- const targetExists = await fs4.access(targetPath).then(() => true).catch(() => false);
1155
+ const targetExists = await fs5.access(targetPath).then(() => true).catch(() => false);
1009
1156
  if (targetExists) {
1010
- const diskHash = createHash("sha256").update(await fs4.readFile(targetPath)).digest("hex");
1157
+ const diskHash = createHash("sha256").update(await fs5.readFile(targetPath)).digest("hex");
1011
1158
  if (this.loadedPath !== targetPath || this.loadedContentHash === null || diskHash !== this.loadedContentHash) {
1012
1159
  throw new Error(`Ledger changed on disk or was not loaded before update: '${targetPath}'.`);
1013
1160
  }
@@ -1016,20 +1163,20 @@ var EvidenceLedger = class {
1016
1163
  }
1017
1164
  const temporaryPath = `${targetPath}.${process.pid}.${Date.now()}.tmp`;
1018
1165
  try {
1019
- await fs4.writeFile(temporaryPath, serialized, { encoding: "utf8", flag: "wx" });
1020
- await fs4.rename(temporaryPath, targetPath);
1166
+ await fs5.writeFile(temporaryPath, serialized, { encoding: "utf8", flag: "wx" });
1167
+ await fs5.rename(temporaryPath, targetPath);
1021
1168
  this.loadedPath = targetPath;
1022
1169
  this.loadedContentHash = contentHash;
1023
1170
  } catch (error) {
1024
- await fs4.rm(temporaryPath, { force: true }).catch(() => {
1171
+ await fs5.rm(temporaryPath, { force: true }).catch(() => {
1025
1172
  });
1026
1173
  throw error;
1027
1174
  }
1028
1175
  }
1029
1176
  async load(filePath) {
1030
- const targetPath = path3.isAbsolute(filePath) ? filePath : path3.join(this.cwd, filePath);
1177
+ const targetPath = path4.isAbsolute(filePath) ? filePath : path4.join(this.cwd, filePath);
1031
1178
  try {
1032
- const content = await fs4.readFile(targetPath, "utf8");
1179
+ const content = await fs5.readFile(targetPath, "utf8");
1033
1180
  const parsed = JSON.parse(content);
1034
1181
  if (!Array.isArray(parsed) || parsed.some((event) => event.workflowId !== this.workflowId)) {
1035
1182
  throw new Error(`Ledger is incompatible with workflow '${this.workflowId}'.`);
@@ -1157,9 +1304,9 @@ var DelegationController = class {
1157
1304
  actor: supported ? actor : "Atlas",
1158
1305
  actorType: supported ? "runtime-agent" : "control-plane",
1159
1306
  observed: true,
1160
- runtime: "opencode",
1307
+ runtime: this.adapter.runtime,
1161
1308
  eventType: startType,
1162
- provenance: "opencode-adapter",
1309
+ provenance: this.adapter.provenance,
1163
1310
  data: {
1164
1311
  phase,
1165
1312
  runtimeAgentRequested: actor,
@@ -1190,7 +1337,7 @@ var DelegationController = class {
1190
1337
  runResult = await this.adapter.execute(prompt, { agent: actor, readOnly, requireActorConfirmation: true, orchestratedChild: true });
1191
1338
  }
1192
1339
  if (runResult.success && requireExplicitActor) {
1193
- if (!["explicit", "sdk-session"].includes(runResult.runtimeAgentSelectionMode || "") || runResult.runtimeAgentApplied !== actor || runResult.runtimeActorConfirmation !== "confirmed") {
1340
+ if (!["explicit", "sdk-session", "native-delegation"].includes(runResult.runtimeAgentSelectionMode || "") || runResult.runtimeAgentApplied !== actor || runResult.runtimeActorConfirmation !== "confirmed") {
1194
1341
  runResult = {
1195
1342
  ...runResult,
1196
1343
  success: false,
@@ -1216,9 +1363,9 @@ var DelegationController = class {
1216
1363
  actor: runResult.runtimeAgentApplied === actor ? actor : "Atlas",
1217
1364
  actorType: runResult.runtimeAgentApplied === actor ? "runtime-agent" : "control-plane",
1218
1365
  observed: true,
1219
- runtime: "opencode",
1366
+ runtime: this.adapter.runtime,
1220
1367
  eventType: completeType,
1221
- provenance: "opencode-adapter",
1368
+ provenance: this.adapter.provenance,
1222
1369
  data: {
1223
1370
  phase,
1224
1371
  success: runResult.success,
@@ -1261,9 +1408,9 @@ var DelegationController = class {
1261
1408
  actor: canApply ? owner : "Atlas",
1262
1409
  actorType: canApply ? "runtime-agent" : "control-plane",
1263
1410
  observed: true,
1264
- runtime: "opencode",
1411
+ runtime: this.adapter.runtime,
1265
1412
  eventType: "implementation_start",
1266
- provenance: "opencode-adapter",
1413
+ provenance: this.adapter.provenance,
1267
1414
  data: {
1268
1415
  agent: owner,
1269
1416
  prompt: promptMsg,
@@ -1287,9 +1434,9 @@ var DelegationController = class {
1287
1434
  actor: isApplied ? owner : "Atlas",
1288
1435
  actorType: isApplied ? "runtime-agent" : "control-plane",
1289
1436
  observed: true,
1290
- runtime: "opencode",
1437
+ runtime: this.adapter.runtime,
1291
1438
  eventType: "implementation_complete",
1292
- provenance: "opencode-adapter",
1439
+ provenance: this.adapter.provenance,
1293
1440
  data: {
1294
1441
  success: runResult.success,
1295
1442
  commandsRun: runResult.commandsRun || [],
@@ -1337,9 +1484,9 @@ var DelegationController = class {
1337
1484
  actor: canApply ? "Sage" : "Atlas",
1338
1485
  actorType: canApply ? "auditor" : "control-plane",
1339
1486
  observed: true,
1340
- runtime: "opencode",
1487
+ runtime: this.adapter.runtime,
1341
1488
  eventType: "validation_start",
1342
- provenance: "opencode-adapter",
1489
+ provenance: this.adapter.provenance,
1343
1490
  data: {
1344
1491
  agent: "Sage",
1345
1492
  event: "validation.started",
@@ -1357,8 +1504,8 @@ var DelegationController = class {
1357
1504
  }
1358
1505
  let gitDiff = "";
1359
1506
  try {
1360
- const { execSync: execSync5 } = await import("child_process");
1361
- gitDiff = execSync5("git diff HEAD", { cwd: this.cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
1507
+ const { execSync: execSync6 } = await import("child_process");
1508
+ gitDiff = execSync6("git diff HEAD", { cwd: this.cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
1362
1509
  } catch {
1363
1510
  }
1364
1511
  const prompt = `Please validate the implementation. Diff:
@@ -1372,9 +1519,9 @@ ${JSON.stringify(result, null, 2)}`;
1372
1519
  actor: isApplied ? "Sage" : "Atlas",
1373
1520
  actorType: isApplied ? "auditor" : "control-plane",
1374
1521
  observed: true,
1375
- runtime: "opencode",
1522
+ runtime: this.adapter.runtime,
1376
1523
  eventType: "validation_complete",
1377
- provenance: "opencode-adapter",
1524
+ provenance: this.adapter.provenance,
1378
1525
  data: {
1379
1526
  success: runResult.success,
1380
1527
  commandsRun: runResult.commandsRun || [],
@@ -1401,11 +1548,38 @@ ${JSON.stringify(result, null, 2)}`;
1401
1548
  }
1402
1549
  };
1403
1550
 
1551
+ // src/core/runtime/runtime-factory.ts
1552
+ import fs6 from "fs/promises";
1553
+ import path5 from "path";
1554
+ function isRuntimeName(value) {
1555
+ return value === "opencode" || value === "codex";
1556
+ }
1557
+ async function resolveRuntime({ cwd, runtime }) {
1558
+ if (runtime !== void 0) {
1559
+ if (!isRuntimeName(runtime)) {
1560
+ throw new Error(`Unsupported runtime '${runtime}'. Expected opencode or codex.`);
1561
+ }
1562
+ return runtime;
1563
+ }
1564
+ try {
1565
+ const raw = await fs6.readFile(path5.join(cwd, ".ai-workflow", "config.json"), "utf8");
1566
+ const config = JSON.parse(raw);
1567
+ const configured = typeof config.runtime === "string" ? config.runtime.split("+").map((item) => item.trim()) : [];
1568
+ if (configured.length === 1 && configured[0] === "codex") return "codex";
1569
+ } catch {
1570
+ }
1571
+ return "opencode";
1572
+ }
1573
+ async function createRuntimeAdapter(options) {
1574
+ const runtime = await resolveRuntime(options);
1575
+ return runtime === "codex" ? new CodexRuntimeAdapter({ cwd: options.cwd }) : new OpenCodeAdapter({ cwd: options.cwd });
1576
+ }
1577
+
1404
1578
  // src/core/finalization/workspace-snapshot.ts
1405
- import fs5 from "fs/promises";
1406
- import path4 from "path";
1579
+ import fs7 from "fs/promises";
1580
+ import path6 from "path";
1407
1581
  import crypto2 from "crypto";
1408
- import { execFileSync, execSync as execSync3 } from "child_process";
1582
+ import { execFileSync, execSync as execSync4 } from "child_process";
1409
1583
  var WorkspaceSnapshot = class {
1410
1584
  cwd;
1411
1585
  fastTrack;
@@ -1437,7 +1611,7 @@ var WorkspaceSnapshot = class {
1437
1611
  }
1438
1612
  const execGit = (args) => {
1439
1613
  try {
1440
- return execSync3(`git ${args}`, { cwd: this.cwd, encoding: "utf8", stdio: "pipe" }).trim();
1614
+ return execSync4(`git ${args}`, { cwd: this.cwd, encoding: "utf8", stdio: "pipe" }).trim();
1441
1615
  } catch {
1442
1616
  return "";
1443
1617
  }
@@ -1483,10 +1657,10 @@ var WorkspaceSnapshot = class {
1483
1657
  const normalizedPath = relativePath.replaceAll("\\", "/");
1484
1658
  if (this.isIgnored(normalizedPath)) continue;
1485
1659
  try {
1486
- const fullPath = path4.join(this.cwd, relativePath);
1487
- const stat = await fs5.stat(fullPath);
1660
+ const fullPath = path6.join(this.cwd, relativePath);
1661
+ const stat = await fs7.stat(fullPath);
1488
1662
  if (!stat.isFile()) continue;
1489
- const content = await fs5.readFile(fullPath);
1663
+ const content = await fs7.readFile(fullPath);
1490
1664
  const sha256 = crypto2.createHash("sha256").update(content).digest("hex");
1491
1665
  const isExecutable = (stat.mode & 73) !== 0;
1492
1666
  const mode = isExecutable ? "100755" : "100644";
@@ -1497,10 +1671,10 @@ var WorkspaceSnapshot = class {
1497
1671
  }
1498
1672
  async scanDir(dir, filesMap) {
1499
1673
  try {
1500
- const entries = await fs5.readdir(dir, { withFileTypes: true });
1674
+ const entries = await fs7.readdir(dir, { withFileTypes: true });
1501
1675
  for (const entry of entries) {
1502
- const fullPath = path4.join(dir, entry.name);
1503
- const relativePath = path4.relative(this.cwd, fullPath);
1676
+ const fullPath = path6.join(dir, entry.name);
1677
+ const relativePath = path6.relative(this.cwd, fullPath);
1504
1678
  if (this.isIgnored(relativePath)) {
1505
1679
  continue;
1506
1680
  }
@@ -1508,8 +1682,8 @@ var WorkspaceSnapshot = class {
1508
1682
  await this.scanDir(fullPath, filesMap);
1509
1683
  } else if (entry.isFile()) {
1510
1684
  try {
1511
- const stat = await fs5.stat(fullPath);
1512
- const content = await fs5.readFile(fullPath);
1685
+ const stat = await fs7.stat(fullPath);
1686
+ const content = await fs7.readFile(fullPath);
1513
1687
  const sha256 = crypto2.createHash("sha256").update(content).digest("hex");
1514
1688
  const isExecutable = (stat.mode & 73) !== 0;
1515
1689
  const mode = isExecutable ? "100755" : "100644";
@@ -1543,9 +1717,9 @@ var WorkspaceSnapshot = class {
1543
1717
  };
1544
1718
 
1545
1719
  // src/core/finalization/finalizer.ts
1546
- import { execSync as execSync4 } from "child_process";
1547
- import path5 from "path";
1548
- import fs6 from "fs/promises";
1720
+ import { execSync as execSync5 } from "child_process";
1721
+ import path7 from "path";
1722
+ import fs8 from "fs/promises";
1549
1723
  var Finalizer = class {
1550
1724
  cwd;
1551
1725
  snapshotManager;
@@ -1558,9 +1732,9 @@ var Finalizer = class {
1558
1732
  getChangedFilesFromGit() {
1559
1733
  try {
1560
1734
  const files = /* @__PURE__ */ new Set();
1561
- const diff = execSync4("git diff --name-only HEAD", { cwd: this.cwd, encoding: "utf8" });
1735
+ const diff = execSync5("git diff --name-only HEAD", { cwd: this.cwd, encoding: "utf8" });
1562
1736
  diff.split("\n").map((f) => f.trim()).filter(Boolean).forEach((f) => files.add(f));
1563
- const status = execSync4("git status --short --untracked-files=all", { cwd: this.cwd, encoding: "utf8" });
1737
+ const status = execSync5("git status --short --untracked-files=all", { cwd: this.cwd, encoding: "utf8" });
1564
1738
  status.split("\n").forEach((line) => {
1565
1739
  const match = line.match(/^(?:[ MADRCU?!]{2}\s+|[MADRCU?!]\s+)(.+)$/);
1566
1740
  if (match) {
@@ -1618,8 +1792,8 @@ var Finalizer = class {
1618
1792
  const verifyResult = await fidelityGate.verify(filesToCheck);
1619
1793
  let passed = requestFidelity.passed && verifyResult.passed;
1620
1794
  let reason = !requestFidelity.passed ? requestFidelity.reason : verifyResult.reason;
1621
- const evidencePath = path5.join(this.cwd, "EVIDENCE.json");
1622
- const hasEvidence = await fs6.access(evidencePath).then(() => true).catch(() => false);
1795
+ const evidencePath = path7.join(this.cwd, "EVIDENCE.json");
1796
+ const hasEvidence = await fs8.access(evidencePath).then(() => true).catch(() => false);
1623
1797
  if (hasEvidence) {
1624
1798
  const { EvidenceValidator } = await import("./evidence-validator-HS3NTWAB.js");
1625
1799
  const isTest = process.env.NODE_ENV === "test" || process.env.VITEST === "true";
@@ -1827,7 +2001,7 @@ var RequestClassifier = class {
1827
2001
  };
1828
2002
 
1829
2003
  // src/core/execution-planner.ts
1830
- import path6 from "path";
2004
+ import path8 from "path";
1831
2005
  var ExecutionPlanner = class {
1832
2006
  cwd;
1833
2007
  constructor({ cwd = process.cwd() } = {}) {
@@ -1839,7 +2013,7 @@ var ExecutionPlanner = class {
1839
2013
  plan(classification, taskSlug = "task") {
1840
2014
  const remediationLimit = classification.remediationLimit;
1841
2015
  const branchNeeded = classification.intent === "write";
1842
- const specPath = classification.specNeeded ? path6.join("docs/workflows", taskSlug, "spec.md") : null;
2016
+ const specPath = classification.specNeeded ? path8.join("docs/workflows", taskSlug, "spec.md") : null;
1843
2017
  const owner = classification.routingDecision?.mutationOwner === "Astra" ? "Astra" : classification.routingDecision?.selectedActor || (classification.intent === "write" ? "Astra" : classification.owner);
1844
2018
  const validationsExpected = [];
1845
2019
  if (classification.validationNeeded) {
@@ -1932,6 +2106,7 @@ export {
1932
2106
  isTerminalFailure,
1933
2107
  COMPLETION_STATUS_TEXT,
1934
2108
  OpenCodeAdapter,
2109
+ CodexRuntimeAdapter,
1935
2110
  SpecValidator,
1936
2111
  HandoffEngine,
1937
2112
  HealerEngine,
@@ -1939,9 +2114,11 @@ export {
1939
2114
  resolveWorkflowProfile,
1940
2115
  EvidenceLedger,
1941
2116
  DelegationController,
2117
+ resolveRuntime,
2118
+ createRuntimeAdapter,
1942
2119
  Finalizer,
1943
2120
  RequestClassifier,
1944
2121
  ExecutionPlanner,
1945
2122
  WorkflowStateMachine
1946
2123
  };
1947
- //# sourceMappingURL=chunk-4FI5ODAM.js.map
2124
+ //# sourceMappingURL=chunk-JDSEOEYW.js.map