@williambeto/ai-workflow 2.8.2 โ 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.
- package/CHANGELOG.md +41 -0
- package/bin/ai-workflow.js +226 -172
- package/bin/ai-workflow.js.map +1 -1
- package/{chunk-4FI5ODAM.js โ chunk-JDSEOEYW.js} +253 -76
- package/core/index.d.ts +65 -21
- package/core/index.js +9 -3
- package/dist-assets/docs/cli-reference.md +4 -2
- package/dist-assets/docs/compatibility/provider-usage.md +13 -2
- package/dist-assets/docs/compatibility/runtime-matrix.md +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
## [unreleased]
|
|
2
2
|
|
|
3
|
+
## [2.9.0] - 2026-07-15
|
|
4
|
+
|
|
5
|
+
### ๐ Features
|
|
6
|
+
|
|
7
|
+
- Add a Codex-native project installation with custom agents under
|
|
8
|
+
`.codex/agents/` and reusable skills, including commands, under
|
|
9
|
+
`.codex/skills/`
|
|
10
|
+
- Introduce runtime adapters and explicit `--runtime=opencode|codex` selection
|
|
11
|
+
while preserving OpenCode as the compatibility default
|
|
12
|
+
- Add Codex-aware doctor checks for trusted projects, native agents, skills,
|
|
13
|
+
policies, and CLI capabilities
|
|
14
|
+
|
|
15
|
+
### ๐ Bug Fixes
|
|
16
|
+
|
|
17
|
+
- Replace legacy Codex prompt and GitHub Copilot paths with documented native
|
|
18
|
+
Codex assets
|
|
19
|
+
- Preserve consumer-owned `AGENTS.md` files and install a Codex workflow
|
|
20
|
+
contract only when the root instructions are AIWK-managed or absent
|
|
21
|
+
- Block non-interactive Codex delivery when the runtime cannot prove custom
|
|
22
|
+
agent ownership instead of reporting unobserved Astra execution
|
|
23
|
+
- Keep runtime selection, delegation evidence, and bounded remediation aligned
|
|
24
|
+
across the OpenCode and Codex adapters
|
|
25
|
+
|
|
26
|
+
### ๐งช Testing
|
|
27
|
+
|
|
28
|
+
- Add unit coverage for Codex agent conversion, command skills, runtime
|
|
29
|
+
selection, JSON event parsing, trust checks, and fail-closed execution
|
|
30
|
+
- Add a clean-consumer Codex init and doctor smoke test without generating
|
|
31
|
+
legacy prompt paths
|
|
32
|
+
- Validate the local release tarball in clean repositories with GPT-5.6 Sol
|
|
33
|
+
Medium and GPT-5.3 Codex Spark; qualify Sol for direct delivery and mark
|
|
34
|
+
Spark unsupported for workspace-write delivery after repeated code-only
|
|
35
|
+
responses without observed edits or validation
|
|
36
|
+
|
|
37
|
+
### ๐ Documentation
|
|
38
|
+
|
|
39
|
+
- Reclassify Codex as experimental until an app-server E2E can prove native
|
|
40
|
+
delegation, workspace delivery, and observed validation
|
|
41
|
+
- Document project trust, native Codex asset locations, runtime selection, and
|
|
42
|
+
model-specific delivery limits
|
|
43
|
+
|
|
3
44
|
## [2.8.1] - 2026-07-14
|
|
4
45
|
|
|
5
46
|
### ๐ Bug Fixes
|
package/bin/ai-workflow.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
COMPLETION_STATUS_TEXT,
|
|
4
|
+
CodexRuntimeAdapter,
|
|
4
5
|
DelegationController,
|
|
5
6
|
EvidenceLedger,
|
|
6
7
|
ExecutionPlanner,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
RequestClassifier,
|
|
12
13
|
SpecValidator,
|
|
13
14
|
WorkflowStateMachine,
|
|
15
|
+
createRuntimeAdapter,
|
|
14
16
|
discoverPackageFiles,
|
|
15
17
|
getFullAgentContent,
|
|
16
18
|
getFullSkillFiles,
|
|
@@ -21,7 +23,7 @@ import {
|
|
|
21
23
|
isTerminalFailure,
|
|
22
24
|
readPackageFile,
|
|
23
25
|
resolveWorkflowProfile
|
|
24
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-JDSEOEYW.js";
|
|
25
27
|
import {
|
|
26
28
|
EvidenceValidator,
|
|
27
29
|
require_ajv,
|
|
@@ -532,6 +534,7 @@ function buildAiWorkflowConfig({ profile, runtimes = ["opencode"] }) {
|
|
|
532
534
|
const version = getPackageVersion();
|
|
533
535
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
534
536
|
const primaryRuntime = runtimes.join("+");
|
|
537
|
+
const codexOnly = runtimes.length === 1 && runtimes[0] === "codex";
|
|
535
538
|
return {
|
|
536
539
|
package: "@williambeto/ai-workflow",
|
|
537
540
|
version,
|
|
@@ -540,10 +543,10 @@ function buildAiWorkflowConfig({ profile, runtimes = ["opencode"] }) {
|
|
|
540
543
|
installMode: "project-local",
|
|
541
544
|
generatedAt: now,
|
|
542
545
|
paths: {
|
|
543
|
-
agents: ".ai-workflow/opencode/agents",
|
|
544
|
-
commands: ".ai-workflow/opencode/commands",
|
|
545
|
-
skills: ".ai-workflow/opencode/skills",
|
|
546
|
-
policies: ".ai-workflow/opencode/docs/policies",
|
|
546
|
+
agents: codexOnly ? ".codex/agents" : ".ai-workflow/opencode/agents",
|
|
547
|
+
commands: codexOnly ? ".codex/skills" : ".ai-workflow/opencode/commands",
|
|
548
|
+
skills: codexOnly ? ".codex/skills" : ".ai-workflow/opencode/skills",
|
|
549
|
+
policies: codexOnly ? ".codex/docs/policies" : ".ai-workflow/opencode/docs/policies",
|
|
547
550
|
harness: ".ai-workflow/harness",
|
|
548
551
|
schemas: ".ai-workflow/schemas"
|
|
549
552
|
}
|
|
@@ -1057,167 +1060,156 @@ Every task completion MUST follow the payload format defined in the individual r
|
|
|
1057
1060
|
// src/adapters/platforms/codex.ts
|
|
1058
1061
|
import fs4 from "fs/promises";
|
|
1059
1062
|
import path6 from "path";
|
|
1063
|
+
function parseMarkdownFrontmatter(content) {
|
|
1064
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
1065
|
+
if (!match) return { body: content };
|
|
1066
|
+
const metadata = match[1];
|
|
1067
|
+
const name = metadata.match(/^name:\s*["']?([^\n"']+)["']?\s*$/m)?.[1]?.trim();
|
|
1068
|
+
const description = metadata.match(/^description:\s*["']?([^\n"']+)["']?\s*$/m)?.[1]?.trim();
|
|
1069
|
+
return { body: content.slice(match[0].length), name, description };
|
|
1070
|
+
}
|
|
1071
|
+
function tomlBasicString(value) {
|
|
1072
|
+
return JSON.stringify(value.replace(/\r?\n/g, " "));
|
|
1073
|
+
}
|
|
1074
|
+
function tomlMultilineString(value) {
|
|
1075
|
+
return value.replace(/\r\n/g, "\n").replace(/"""/g, '\\"\\"\\"');
|
|
1076
|
+
}
|
|
1077
|
+
function yamlQuoted(value) {
|
|
1078
|
+
return JSON.stringify(value);
|
|
1079
|
+
}
|
|
1080
|
+
function commandSkillContent(content, commandName) {
|
|
1081
|
+
const parsed = parseMarkdownFrontmatter(content);
|
|
1082
|
+
const description = parsed.description || `Run the AI Workflow Kit ${commandName} command contract.`;
|
|
1083
|
+
return [
|
|
1084
|
+
"---",
|
|
1085
|
+
`name: ${yamlQuoted(`aiwk-command-${commandName}`)}`,
|
|
1086
|
+
`description: ${yamlQuoted(description)}`,
|
|
1087
|
+
"---",
|
|
1088
|
+
"",
|
|
1089
|
+
parsed.body.trim(),
|
|
1090
|
+
""
|
|
1091
|
+
].join("\n");
|
|
1092
|
+
}
|
|
1093
|
+
function codexRootInstructions() {
|
|
1094
|
+
return `# AI Workflow Kit for Codex
|
|
1095
|
+
|
|
1096
|
+
## Purpose
|
|
1097
|
+
|
|
1098
|
+
Transform a natural request into a proportional, safe, and verifiable software delivery.
|
|
1099
|
+
|
|
1100
|
+
## Required flow
|
|
1101
|
+
|
|
1102
|
+
Request \u2192 Planning \u2192 Branch Gate \u2192 Delegation when useful \u2192 Implementation \u2192 Validation \u2192 Evidence
|
|
1103
|
+
|
|
1104
|
+
## Role ownership
|
|
1105
|
+
|
|
1106
|
+
- Atlas routes the request and coordinates the workflow.
|
|
1107
|
+
- Astra owns all requested workspace mutations, including work initiated from direct chat. Atlas must delegate write work to the native \`astra\` agent; it must not write on Astra's behalf.
|
|
1108
|
+
- Nexus owns discovery and specifications. Orion owns technical planning and release sequencing.
|
|
1109
|
+
- Sage performs independent validation only when risk or evidence policy requires it. Phoenix performs bounded remediation from observed findings.
|
|
1110
|
+
- Do not delegate simple read-only answers merely for ceremony.
|
|
1111
|
+
|
|
1112
|
+
## Safety and completion
|
|
1113
|
+
|
|
1114
|
+
- Never implement directly on \`main\` or \`master\`.
|
|
1115
|
+
- Before changing files, inspect the branch and preserve unrelated dirty work.
|
|
1116
|
+
- Do not create a specification, evidence artifact, test, screenshot, or agent handoff unless the request or proportional policy requires it.
|
|
1117
|
+
- Run relevant validation after a workspace change. Do not report \`CHANGED\` or \`COMPLETED\` without observed validation evidence.
|
|
1118
|
+
- When \`ai-workflow execute\` is coordinating the task, its parent process owns finalization. A delegated agent must not run a second finalizer in that mode.
|
|
1119
|
+
- Report changed files, commands actually run, results, and limitations concisely. Do not print a full implementation in chat when it has been written to the workspace.
|
|
1120
|
+
|
|
1121
|
+
## Native Codex resources
|
|
1122
|
+
|
|
1123
|
+
- Roles are defined in \`.codex/agents/\`.
|
|
1124
|
+
- Reusable skills, including AIWK commands, are defined in \`.codex/skills/\`.
|
|
1125
|
+
- Governance policies are available in \`.codex/docs/policies/\`.
|
|
1126
|
+
`;
|
|
1127
|
+
}
|
|
1128
|
+
function toCodexAgentToml(name, source) {
|
|
1129
|
+
const parsed = parseMarkdownFrontmatter(source);
|
|
1130
|
+
const agentName = (parsed.name || name).trim().toLowerCase();
|
|
1131
|
+
const description = parsed.description || `AI Workflow Kit ${agentName} agent.`;
|
|
1132
|
+
const developerInstructions = [
|
|
1133
|
+
parsed.body.trim(),
|
|
1134
|
+
"",
|
|
1135
|
+
"## Codex execution boundary",
|
|
1136
|
+
"- Follow the repository AGENTS.md and this role contract.",
|
|
1137
|
+
"- Report only observed commands, validations, and workspace effects.",
|
|
1138
|
+
"- Do not claim another role's work or finalize the parent AI Workflow execution."
|
|
1139
|
+
].join("\n");
|
|
1140
|
+
return {
|
|
1141
|
+
name: agentName,
|
|
1142
|
+
description,
|
|
1143
|
+
content: [
|
|
1144
|
+
`name = ${tomlBasicString(agentName)}`,
|
|
1145
|
+
`description = ${tomlBasicString(description)}`,
|
|
1146
|
+
'developer_instructions = """',
|
|
1147
|
+
tomlMultilineString(developerInstructions),
|
|
1148
|
+
'"""',
|
|
1149
|
+
""
|
|
1150
|
+
].join("\n")
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1060
1153
|
var CodexAdapter = class {
|
|
1061
1154
|
cwd;
|
|
1062
1155
|
constructor({ cwd }) {
|
|
1063
1156
|
this.cwd = cwd;
|
|
1064
1157
|
}
|
|
1065
|
-
/**
|
|
1066
|
-
* Transforms an OpenCode agent into a Codex prompt with completion contract.
|
|
1067
|
-
*/
|
|
1068
1158
|
async transformAgent(filePath) {
|
|
1069
1159
|
const content = await fs4.readFile(filePath, "utf8");
|
|
1070
|
-
|
|
1071
|
-
const hardenedContent = `${content}
|
|
1072
|
-
|
|
1073
|
-
## System Directives for Codex
|
|
1074
|
-
- **No Conversational Filler**: Output only the requested artifact or analysis. Do not apologize or add filler text.
|
|
1075
|
-
- **Explicit Paths**: Always specify the exact, absolute or relative file path before making edits.
|
|
1076
|
-
- **Read-Only First**: Do not invent or assume file contents. Always read the workspace first.
|
|
1077
|
-
|
|
1078
|
-
## Completion Contract (Mandatory)
|
|
1079
|
-
Every task completion MUST provide the following payload:
|
|
1080
|
-
1. **Status**: ${COMPLETION_STATUS_TEXT}
|
|
1081
|
-
2. **Scope reviewed**: Brief summary of what was analyzed.
|
|
1082
|
-
3. **Findings**: Evidence-based discoveries.
|
|
1083
|
-
4. **Files affected**: List of all concrete paths modified or created.
|
|
1084
|
-
5. **Validation/evidence**: Commands run and their results.
|
|
1085
|
-
6. **Risks/notes**: Any caveats or technical debt introduced.
|
|
1086
|
-
7. **Required manager action**: Clear next step for the orchestrator.
|
|
1087
|
-
8. **Can manager continue**: [yes/no]
|
|
1088
|
-
`;
|
|
1089
|
-
return {
|
|
1090
|
-
content: hardenedContent,
|
|
1091
|
-
name: fileName
|
|
1092
|
-
};
|
|
1160
|
+
return toCodexAgentToml(path6.basename(filePath, ".md"), content);
|
|
1093
1161
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
* - Commands -> .codex/prompts/
|
|
1100
|
-
*/
|
|
1101
|
-
async deploy(installRoot = ".ai-workflow", force) {
|
|
1102
|
-
const sourceAgentsDir = path6.join(this.cwd, installRoot, "opencode/agents");
|
|
1103
|
-
const sourceSkillsDir = path6.join(this.cwd, installRoot, "opencode/skills");
|
|
1104
|
-
const sourceCommandsDir = path6.join(this.cwd, installRoot, "opencode/commands");
|
|
1105
|
-
const codexAgentsDir = path6.join(this.cwd, ".github/agents");
|
|
1162
|
+
async deploy(installRoot = ".ai-workflow", force = false) {
|
|
1163
|
+
const sourceAgentsDir = path6.join(this.cwd, installRoot, "opencode", "agents");
|
|
1164
|
+
const sourceSkillsDir = path6.join(this.cwd, installRoot, "opencode", "skills");
|
|
1165
|
+
const sourceCommandsDir = path6.join(this.cwd, installRoot, "opencode", "commands");
|
|
1166
|
+
const codexAgentsDir = path6.join(this.cwd, ".codex", "agents");
|
|
1106
1167
|
await fs4.mkdir(codexAgentsDir, { recursive: true });
|
|
1107
|
-
const
|
|
1108
|
-
for (const file of agentFiles) {
|
|
1168
|
+
for (const file of await fs4.readdir(sourceAgentsDir).catch(() => [])) {
|
|
1109
1169
|
if (!file.endsWith(".md")) continue;
|
|
1110
1170
|
const transformed = await this.transformAgent(path6.join(sourceAgentsDir, file));
|
|
1111
|
-
await fs4.writeFile(path6.join(codexAgentsDir, `${transformed.name}.
|
|
1171
|
+
await fs4.writeFile(path6.join(codexAgentsDir, `${transformed.name}.toml`), transformed.content);
|
|
1112
1172
|
}
|
|
1113
|
-
const codexSkillsDir = path6.join(this.cwd, ".codex
|
|
1173
|
+
const codexSkillsDir = path6.join(this.cwd, ".codex", "skills");
|
|
1114
1174
|
await fs4.mkdir(codexSkillsDir, { recursive: true });
|
|
1115
|
-
const
|
|
1116
|
-
for (const folder of skillFolders) {
|
|
1175
|
+
for (const folder of await fs4.readdir(sourceSkillsDir).catch(() => [])) {
|
|
1117
1176
|
const skillSource = path6.join(sourceSkillsDir, folder, "SKILL.md");
|
|
1118
|
-
if (await this.exists(skillSource))
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
const targetFileName = file.endsWith(".toml") ? `${path6.basename(file, ".toml")}.md` : file;
|
|
1131
|
-
const content = await fs4.readFile(path6.join(sourceCommandsDir, file), "utf8");
|
|
1132
|
-
await fs4.writeFile(path6.join(codexPromptsDir, targetFileName), content);
|
|
1177
|
+
if (!await this.exists(skillSource)) continue;
|
|
1178
|
+
const targetDir = path6.join(codexSkillsDir, folder);
|
|
1179
|
+
await fs4.mkdir(targetDir, { recursive: true });
|
|
1180
|
+
await fs4.writeFile(path6.join(targetDir, "SKILL.md"), await fs4.readFile(skillSource, "utf8"));
|
|
1181
|
+
}
|
|
1182
|
+
for (const file of await fs4.readdir(sourceCommandsDir).catch(() => [])) {
|
|
1183
|
+
if (!file.endsWith(".md") || file.toLowerCase() === "readme.md") continue;
|
|
1184
|
+
const commandName = path6.basename(file, ".md");
|
|
1185
|
+
const targetDir = path6.join(codexSkillsDir, `aiwk-command-${commandName}`);
|
|
1186
|
+
await fs4.mkdir(targetDir, { recursive: true });
|
|
1187
|
+
const source = await fs4.readFile(path6.join(sourceCommandsDir, file), "utf8");
|
|
1188
|
+
await fs4.writeFile(path6.join(targetDir, "SKILL.md"), commandSkillContent(source, commandName));
|
|
1133
1189
|
}
|
|
1134
1190
|
const codexPoliciesDir = path6.join(this.cwd, ".codex", "docs", "policies");
|
|
1135
1191
|
await fs4.mkdir(codexPoliciesDir, { recursive: true });
|
|
1136
1192
|
const sourcePoliciesDir = path6.join(this.cwd, installRoot, "opencode", "docs", "policies");
|
|
1137
|
-
const
|
|
1138
|
-
for (const file of policyFiles) {
|
|
1193
|
+
for (const file of await fs4.readdir(sourcePoliciesDir).catch(() => [])) {
|
|
1139
1194
|
if (!file.endsWith(".md")) continue;
|
|
1140
|
-
|
|
1141
|
-
|
|
1195
|
+
await fs4.writeFile(
|
|
1196
|
+
path6.join(codexPoliciesDir, file),
|
|
1197
|
+
await fs4.readFile(path6.join(sourcePoliciesDir, file), "utf8")
|
|
1198
|
+
);
|
|
1142
1199
|
}
|
|
1143
|
-
await this.deployRootCopilotInstructions();
|
|
1144
|
-
await this.deployRootCodexMd(force);
|
|
1145
1200
|
await this.deployRootAgentsMd(force);
|
|
1146
1201
|
}
|
|
1147
|
-
async
|
|
1148
|
-
const githubDir = path6.join(this.cwd, ".github");
|
|
1149
|
-
await fs4.mkdir(githubDir, { recursive: true });
|
|
1150
|
-
const targetPath = path6.join(githubDir, "copilot-instructions.md");
|
|
1151
|
-
const instructions = `# AI Workflow Kit Governance
|
|
1152
|
-
|
|
1153
|
-
This repository uses the **AI Workflow Kit** (OpenCode-first) workflow.
|
|
1154
|
-
|
|
1155
|
-
## Mandate: Atlas Authority Protocol
|
|
1156
|
-
All architectural changes must align with the specifications issued by the **Specification Authority**.
|
|
1157
|
-
|
|
1158
|
-
## Rules & Personas
|
|
1159
|
-
- Always follow the **Branch Gate** policy (never work on \`main\`).
|
|
1160
|
-
- Always follow the **SDD Workflow** (Spec -> Plan -> PR -> Evidence).
|
|
1161
|
-
- When acting as an agent, consult the instructions in \`.github/agents/\`.
|
|
1162
|
-
- When utilizing skills, consult the files in \`.codex/skills/\`.
|
|
1163
|
-
|
|
1164
|
-
## System Boundaries
|
|
1165
|
-
- **No Conversational Filler**: Do not apologize or add filler text.
|
|
1166
|
-
- **Strict Evidence**: You must provide the Completion Contract payload when finishing a task.
|
|
1167
|
-
- **Immutable Core**: Do not modify files inside \`.ai-workflow/\`, \`.agents/\`, \`.claude/\`, or \`.codex/\` unless explicitly requested.
|
|
1168
|
-
|
|
1169
|
-
## Completion Contract
|
|
1170
|
-
Every task completion MUST follow the payload format defined in the individual agent files.
|
|
1171
|
-
`;
|
|
1172
|
-
await fs4.writeFile(targetPath, instructions);
|
|
1173
|
-
}
|
|
1174
|
-
async deployRootCodexMd(force) {
|
|
1175
|
-
const targetPath = path6.join(this.cwd, "CODEX.md");
|
|
1176
|
-
if (!force && await this.exists(targetPath)) return;
|
|
1177
|
-
const templatePath = path6.join(this.cwd, ".ai-workflow", "templates", "CODEX.md.template");
|
|
1178
|
-
if (await this.exists(templatePath)) {
|
|
1179
|
-
const content = await fs4.readFile(templatePath, "utf8");
|
|
1180
|
-
await fs4.writeFile(targetPath, content);
|
|
1181
|
-
return;
|
|
1182
|
-
}
|
|
1183
|
-
const instructions = `# AI Workflow Kit Governance
|
|
1184
|
-
|
|
1185
|
-
This repository uses the **AI Workflow Kit** (OpenCode-first) workflow.
|
|
1186
|
-
|
|
1187
|
-
## Mandate: Atlas Authority Protocol
|
|
1188
|
-
All architectural changes must align with the specifications issued by the **Specification Authority**.
|
|
1189
|
-
|
|
1190
|
-
## Rules & Personas
|
|
1191
|
-
- Always follow the **Branch Gate** policy (never work on \`main\`).
|
|
1192
|
-
- Always follow the **SDD Workflow** (Spec -> Plan -> PR -> Evidence).
|
|
1193
|
-
- When acting as an agent, consult the instructions in \`.github/agents/\`.
|
|
1194
|
-
- When utilizing skills, consult the files in \`.codex/skills/\`.
|
|
1195
|
-
|
|
1196
|
-
## System Boundaries
|
|
1197
|
-
- **No Conversational Filler**: Do not apologize or add filler text.
|
|
1198
|
-
- **Strict Evidence**: You must provide the Completion Contract payload when finishing a task.
|
|
1199
|
-
- **Immutable Core**: Do not modify files inside \`.ai-workflow/\`, \`.agents/\`, \`.claude/\`, or \`.codex/\` unless explicitly requested.
|
|
1200
|
-
|
|
1201
|
-
## Completion Contract
|
|
1202
|
-
Every task completion MUST follow the payload format defined in the individual agent files.
|
|
1203
|
-
`;
|
|
1204
|
-
await fs4.writeFile(targetPath, instructions);
|
|
1205
|
-
}
|
|
1206
|
-
async deployRootAgentsMd(force) {
|
|
1202
|
+
async deployRootAgentsMd(force = false) {
|
|
1207
1203
|
const targetPath = path6.join(this.cwd, "AGENTS.md");
|
|
1208
|
-
if (!force && await this.exists(targetPath))
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
let content = await fs4.readFile(templatePath, "utf8");
|
|
1212
|
-
content = content.replace(/\.agents\/skills/g, ".codex/skills");
|
|
1213
|
-
content = content.replace(/opencode\/commands/g, ".codex/prompts");
|
|
1214
|
-
content = content.replace(/opencode\/docs\/policies/g, ".codex/docs/policies");
|
|
1215
|
-
await fs4.writeFile(targetPath, content);
|
|
1204
|
+
if (!force && await this.exists(targetPath)) {
|
|
1205
|
+
const existing = await fs4.readFile(targetPath, "utf8");
|
|
1206
|
+
if (!existing.startsWith("# AI Workflow Kit contributor contract") && !existing.startsWith("# AI Workflow Kit for Codex")) return;
|
|
1216
1207
|
}
|
|
1208
|
+
await fs4.writeFile(targetPath, codexRootInstructions());
|
|
1217
1209
|
}
|
|
1218
|
-
async exists(
|
|
1210
|
+
async exists(candidate) {
|
|
1219
1211
|
try {
|
|
1220
|
-
await fs4.access(
|
|
1212
|
+
await fs4.access(candidate);
|
|
1221
1213
|
return true;
|
|
1222
1214
|
} catch {
|
|
1223
1215
|
return false;
|
|
@@ -1726,7 +1718,7 @@ async function executePlatformMigrations(cwd, installRoot, platforms, claude, co
|
|
|
1726
1718
|
if (codex) {
|
|
1727
1719
|
const adapter = new CodexAdapter({ cwd });
|
|
1728
1720
|
await adapter.deploy(installRoot, force);
|
|
1729
|
-
console.log("- codex: deployed
|
|
1721
|
+
console.log("- codex: deployed native agents to .codex/agents and skills (including commands) to .codex/skills");
|
|
1730
1722
|
}
|
|
1731
1723
|
if (antigravity) {
|
|
1732
1724
|
const adapter = new AntigravityAdapter({ cwd });
|
|
@@ -2036,24 +2028,79 @@ async function checkPackageJson(cwd, state) {
|
|
|
2036
2028
|
}
|
|
2037
2029
|
}
|
|
2038
2030
|
async function checkCodexRuntime(cwd, state) {
|
|
2039
|
-
const
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2031
|
+
const rootAgentsPath = path9.join(cwd, "AGENTS.md");
|
|
2032
|
+
const rootAgents = await fs7.readFile(rootAgentsPath, "utf8").catch(() => "");
|
|
2033
|
+
if (!rootAgents) {
|
|
2034
|
+
state.hasFailure = true;
|
|
2035
|
+
console.log("FAIL root AGENTS.md missing");
|
|
2036
|
+
} else if (!rootAgents.includes("Astra owns all requested workspace mutations")) {
|
|
2037
|
+
state.hasFailure = true;
|
|
2038
|
+
console.log("FAIL root AGENTS.md lacks the Codex Astra ownership contract");
|
|
2039
|
+
} else {
|
|
2040
|
+
console.log("PASS root AGENTS.md contains the Codex workflow contract");
|
|
2041
|
+
}
|
|
2042
|
+
const agentsDir = path9.join(cwd, ".codex", "agents");
|
|
2043
|
+
const requiredAgents = ["atlas", "astra", "nexus", "orion", "sage", "phoenix"];
|
|
2044
|
+
for (const agent of requiredAgents) {
|
|
2045
|
+
const agentPath = path9.join(agentsDir, `${agent}.toml`);
|
|
2046
|
+
const content = await fs7.readFile(agentPath, "utf8").catch(() => "");
|
|
2047
|
+
const valid = new RegExp(`^name\\s*=\\s*["']${agent}["']\\s*$`, "m").test(content) && /^description\s*=\s*["']\S/m.test(content) && /^developer_instructions\s*=\s*"""/m.test(content) && /"""\s*$/m.test(content);
|
|
2048
|
+
if (valid) {
|
|
2049
|
+
console.log(`PASS native Codex agent: ${agent}`);
|
|
2050
|
+
} else {
|
|
2051
|
+
state.hasFailure = true;
|
|
2052
|
+
console.log(`FAIL native Codex agent invalid or missing: ${agent}`);
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
const skillsDir = path9.join(cwd, ".codex", "skills");
|
|
2056
|
+
const skillFolders = await fs7.readdir(skillsDir).catch(() => []);
|
|
2057
|
+
if (skillFolders.length === 0) {
|
|
2058
|
+
state.hasFailure = true;
|
|
2059
|
+
console.log("FAIL .codex/skills missing or empty");
|
|
2060
|
+
} else {
|
|
2061
|
+
console.log(`PASS .codex/skills detected (${skillFolders.length})`);
|
|
2062
|
+
}
|
|
2063
|
+
const sourceCommandsDir = path9.join(cwd, ".ai-workflow", "opencode", "commands");
|
|
2064
|
+
for (const command of await fs7.readdir(sourceCommandsDir).catch(() => [])) {
|
|
2065
|
+
if (!command.endsWith(".md") || command.toLowerCase() === "readme.md") continue;
|
|
2066
|
+
const name = path9.basename(command, ".md");
|
|
2067
|
+
const commandSkillPath = path9.join(skillsDir, `aiwk-command-${name}`, "SKILL.md");
|
|
2068
|
+
const content = await fs7.readFile(commandSkillPath, "utf8").catch(() => "");
|
|
2069
|
+
if (content.includes(`name: "aiwk-command-${name}"`)) {
|
|
2070
|
+
console.log(`PASS Codex command skill: ${name}`);
|
|
2049
2071
|
} else {
|
|
2050
2072
|
state.hasFailure = true;
|
|
2051
|
-
console.log(`FAIL ${
|
|
2073
|
+
console.log(`FAIL Codex command skill invalid or missing: ${name}`);
|
|
2052
2074
|
}
|
|
2053
2075
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2076
|
+
if (await exists(path9.join(cwd, ".codex", "docs", "policies"))) {
|
|
2077
|
+
console.log("PASS .codex/docs/policies detected");
|
|
2078
|
+
} else {
|
|
2079
|
+
state.hasFailure = true;
|
|
2080
|
+
console.log("FAIL .codex/docs/policies missing");
|
|
2081
|
+
}
|
|
2082
|
+
if (await exists(path9.join(cwd, ".codex", "prompts"))) {
|
|
2083
|
+
state.hasWarning = true;
|
|
2084
|
+
console.log("WARN legacy .codex/prompts detected; AIWK commands now use .codex/skills");
|
|
2085
|
+
}
|
|
2086
|
+
const adapter = new CodexRuntimeAdapter({ cwd });
|
|
2087
|
+
const inspection = await adapter.inspect();
|
|
2088
|
+
if (!inspection.available) {
|
|
2089
|
+
state.hasFailure = true;
|
|
2090
|
+
console.log("FAIL Codex CLI is not installed or not available in PATH");
|
|
2091
|
+
} else if (!inspection.supports.formatJson) {
|
|
2092
|
+
state.hasFailure = true;
|
|
2093
|
+
console.log("FAIL Codex CLI lacks exec --json required for observed evidence");
|
|
2094
|
+
} else if (inspection.projectTrusted === false) {
|
|
2095
|
+
state.hasFailure = true;
|
|
2096
|
+
console.log("FAIL Codex project is not trusted; native agents are disabled until this repository is trusted in Codex");
|
|
2097
|
+
} else if (!inspection.supports.agent) {
|
|
2098
|
+
state.hasFailure = true;
|
|
2099
|
+
console.log("FAIL Codex native agents are incomplete");
|
|
2100
|
+
} else {
|
|
2101
|
+
state.hasWarning = true;
|
|
2102
|
+
console.log("PASS Codex CLI, JSON events, and native agents are available");
|
|
2103
|
+
console.log("WARN Codex CLI delivery is blocked because `codex exec` cannot prove custom-agent ownership; use interactive Codex");
|
|
2057
2104
|
}
|
|
2058
2105
|
if (state.runtimes.includes("codex")) {
|
|
2059
2106
|
console.log("PASS config runtime includes codex");
|
|
@@ -2497,7 +2544,7 @@ async function findCompatibleLedger(cwd, specRelativePath) {
|
|
|
2497
2544
|
}
|
|
2498
2545
|
return matches;
|
|
2499
2546
|
}
|
|
2500
|
-
async function runDeepImplementation(cwd, absoluteSpecPath, specPath, approvalPath, specContent) {
|
|
2547
|
+
async function runDeepImplementation(cwd, absoluteSpecPath, specPath, approvalPath, specContent, runtime) {
|
|
2501
2548
|
if (!approvalPath) throw new Error("[SDD BLOCKED] DEEP resume requires --approval-path.");
|
|
2502
2549
|
const approvalValidator = new ApprovalReceiptValidator({ cwd });
|
|
2503
2550
|
const approval = await approvalValidator.validate(approvalPath, absoluteSpecPath);
|
|
@@ -2537,7 +2584,9 @@ async function runDeepImplementation(cwd, absoluteSpecPath, specPath, approvalPa
|
|
|
2537
2584
|
throw new Error("[SDD BLOCKED] Ledger specification path does not match --spec-path.");
|
|
2538
2585
|
}
|
|
2539
2586
|
const draftHash = nexusTerminals[0]?.data?.artifactHash || null;
|
|
2540
|
-
const
|
|
2587
|
+
const runtimeAdapter = await createRuntimeAdapter({ cwd, runtime });
|
|
2588
|
+
console.log(`[RUNTIME] ${runtimeAdapter.runtime}`);
|
|
2589
|
+
const controller = new DelegationController({ cwd, ledger, adapter: runtimeAdapter });
|
|
2541
2590
|
const absolutePlanPath = path14.join(cwd, "docs/workflows", workflowId, "technical-plan.json");
|
|
2542
2591
|
const relativePlanPath = repositoryRelative(cwd, absolutePlanPath);
|
|
2543
2592
|
if (await fs12.access(absolutePlanPath).then(() => true).catch(() => false)) {
|
|
@@ -2645,7 +2694,7 @@ async function runDeepImplementation(cwd, absoluteSpecPath, specPath, approvalPa
|
|
|
2645
2694
|
controller
|
|
2646
2695
|
};
|
|
2647
2696
|
}
|
|
2648
|
-
async function runMasterOrchestrator({ cwd, specPath, approvalPath, override, remediationExecutor = null }) {
|
|
2697
|
+
async function runMasterOrchestrator({ cwd, specPath, approvalPath, override, remediationExecutor = null, runtime }) {
|
|
2649
2698
|
if (process.env.AI_WORKFLOW_FINALIZATION_OWNER === "execute") {
|
|
2650
2699
|
throw new Error("[NESTED EXECUTION BLOCKED] The parent execute process owns orchestration and finalization.");
|
|
2651
2700
|
}
|
|
@@ -2669,7 +2718,7 @@ async function runMasterOrchestrator({ cwd, specPath, approvalPath, override, re
|
|
|
2669
2718
|
const profileDefinition = getWorkflowProfile(workflowProfile);
|
|
2670
2719
|
console.log(`[PASS] Specification: ${specTier.toUpperCase()} spec is APPROVED.`);
|
|
2671
2720
|
console.log(`[PROFILE] ${workflowProfile} -> ${profileDefinition.owner}; skills: ${profileDefinition.skills.join(", ") || "none"}.`);
|
|
2672
|
-
const deepContext = specTier === "deep" ? await runDeepImplementation(cwd, absoluteSpecPath, specPath, approvalPath, specContent) : null;
|
|
2721
|
+
const deepContext = specTier === "deep" ? await runDeepImplementation(cwd, absoluteSpecPath, specPath, approvalPath, specContent, runtime) : null;
|
|
2673
2722
|
const validateWorkflow = async () => {
|
|
2674
2723
|
const evidence = await runCollectEvidence({
|
|
2675
2724
|
cwd,
|
|
@@ -2735,8 +2784,7 @@ async function runMasterOrchestrator({ cwd, specPath, approvalPath, override, re
|
|
|
2735
2784
|
import fs13 from "fs/promises";
|
|
2736
2785
|
import path15 from "path";
|
|
2737
2786
|
import { execSync as execSync3 } from "child_process";
|
|
2738
|
-
function createRuntimeRemediationExecutor(cwd, ledger = null) {
|
|
2739
|
-
const adapter = new OpenCodeAdapter({ cwd });
|
|
2787
|
+
function createRuntimeRemediationExecutor(cwd, ledger = null, adapter = new OpenCodeAdapter({ cwd })) {
|
|
2740
2788
|
function getChangedFiles2() {
|
|
2741
2789
|
try {
|
|
2742
2790
|
const output = execSync3("git status --short", { cwd, encoding: "utf8" });
|
|
@@ -2761,9 +2809,9 @@ function createRuntimeRemediationExecutor(cwd, ledger = null) {
|
|
|
2761
2809
|
actor: "Phoenix",
|
|
2762
2810
|
actorType: "runtime-agent",
|
|
2763
2811
|
observed: true,
|
|
2764
|
-
runtime:
|
|
2812
|
+
runtime: adapter.runtime,
|
|
2765
2813
|
eventType: "remediation_start",
|
|
2766
|
-
provenance:
|
|
2814
|
+
provenance: adapter.provenance,
|
|
2767
2815
|
data: {
|
|
2768
2816
|
attempt,
|
|
2769
2817
|
affectedChecks,
|
|
@@ -3119,9 +3167,9 @@ async function runImplementation(plan, classification, delegationController, sta
|
|
|
3119
3167
|
}
|
|
3120
3168
|
const runResult = await delegationController.implement(promptMsg, plan.owner, { readOnly: !plan.branchNeeded, fastTrack, orchestratedChild: true });
|
|
3121
3169
|
if (!runResult.success) {
|
|
3122
|
-
console.error(`[EXECUTE]
|
|
3170
|
+
console.error(`[EXECUTE] Runtime execution reported failure: ${runResult.error || "Unknown error"}`);
|
|
3123
3171
|
stateMachine.transitionTo("BLOCKED");
|
|
3124
|
-
throw new Error(`[WORKFLOW BLOCKED]
|
|
3172
|
+
throw new Error(`[WORKFLOW BLOCKED] Runtime execution failed: ${runResult.error || "Unknown error"}`);
|
|
3125
3173
|
}
|
|
3126
3174
|
return runResult;
|
|
3127
3175
|
}
|
|
@@ -3170,13 +3218,13 @@ async function runValidation(plan, taskSlug, cwd, gateResult, naturalRequest, cl
|
|
|
3170
3218
|
const result = await delegationController.validate(taskSlug, plan.profile, validateWorkflow, plan.evidencePolicy);
|
|
3171
3219
|
return { result, validateWorkflow };
|
|
3172
3220
|
}
|
|
3173
|
-
async function runBoundedRemediation(plan, taskSlug, cwd, initialResult, validateWorkflow, ledger, delegationController, stateMachine, checkBranchSafety, updateSnapshot) {
|
|
3221
|
+
async function runBoundedRemediation(plan, taskSlug, cwd, initialResult, validateWorkflow, ledger, delegationController, runtimeAdapter, stateMachine, checkBranchSafety, updateSnapshot) {
|
|
3174
3222
|
if (!isRecoverableGateFailure(initialResult.overallStatus)) {
|
|
3175
3223
|
return initialResult;
|
|
3176
3224
|
}
|
|
3177
3225
|
console.log(`
|
|
3178
3226
|
[REMEDIATION REQUIRED] ${initialResult.overallStatus}. Starting bounded remediation (limit: ${plan.remediationLimit}).`);
|
|
3179
|
-
const executor = createRuntimeRemediationExecutor(cwd, ledger);
|
|
3227
|
+
const executor = createRuntimeRemediationExecutor(cwd, ledger, runtimeAdapter);
|
|
3180
3228
|
const healer = new HealerEngine({
|
|
3181
3229
|
cwd,
|
|
3182
3230
|
remediationLimit: plan.remediationLimit,
|
|
@@ -3303,7 +3351,7 @@ async function executeReadOnlyWorkflow(cwd, stateMachine, readOnlyStateBefore, f
|
|
|
3303
3351
|
stateHistory: stateMachine.getHistory()
|
|
3304
3352
|
};
|
|
3305
3353
|
}
|
|
3306
|
-
async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverride }) {
|
|
3354
|
+
async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverride, runtime }) {
|
|
3307
3355
|
if (process.env.AI_WORKFLOW_FINALIZATION_OWNER === "execute") {
|
|
3308
3356
|
throw new Error("[NESTED EXECUTION BLOCKED] The parent execute process owns orchestration and finalization.");
|
|
3309
3357
|
}
|
|
@@ -3315,7 +3363,9 @@ async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverride }) {
|
|
|
3315
3363
|
[AI WORKFLOW] Executing natural request intake [slug: ${taskSlug}]...
|
|
3316
3364
|
`);
|
|
3317
3365
|
const ledger = new EvidenceLedger({ cwd, workflowId: taskSlug });
|
|
3318
|
-
const
|
|
3366
|
+
const runtimeAdapter = await createRuntimeAdapter({ cwd, runtime });
|
|
3367
|
+
console.log(`[RUNTIME] ${runtimeAdapter.runtime}`);
|
|
3368
|
+
const delegationController = new DelegationController({ cwd, ledger, adapter: runtimeAdapter });
|
|
3319
3369
|
let plan = null;
|
|
3320
3370
|
let branchAuthorized = false;
|
|
3321
3371
|
try {
|
|
@@ -3380,6 +3430,7 @@ async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverride }) {
|
|
|
3380
3430
|
validateWorkflow,
|
|
3381
3431
|
ledger,
|
|
3382
3432
|
delegationController,
|
|
3433
|
+
runtimeAdapter,
|
|
3383
3434
|
stateMachine,
|
|
3384
3435
|
checkBranchSafety,
|
|
3385
3436
|
async () => {
|
|
@@ -3547,8 +3598,8 @@ function printHelp() {
|
|
|
3547
3598
|
console.log(`ai-workflow
|
|
3548
3599
|
|
|
3549
3600
|
Usage:
|
|
3550
|
-
ai-workflow execute "<request>" [--task=<slug>] [--request="<request>"]
|
|
3551
|
-
ai-workflow run --spec-path=<path> [--approval-path=<path>]
|
|
3601
|
+
ai-workflow execute "<request>" [--task=<slug>] [--request="<request>"] [--runtime=<opencode|codex>]
|
|
3602
|
+
ai-workflow run --spec-path=<path> [--approval-path=<path>] [--runtime=<opencode|codex>]
|
|
3552
3603
|
ai-workflow init [--yes] [--force] [--dry-run] [--no-install] [--no-overwrite] [--claude] [--codex] [--antigravity] [--profile=<profile>]
|
|
3553
3604
|
ai-workflow validate [--a11y] [--visual-dist=<path>] [--port=<port>]
|
|
3554
3605
|
ai-workflow collect-evidence [--task=<slug>] [--evidence-policy=<optional|required>] [--mode=<quick|standard|full>] [--dry-run] [--visual-dist=<path>] [--port=<port>]
|
|
@@ -3578,6 +3629,7 @@ function parseFlags(args) {
|
|
|
3578
3629
|
const requestArg = args.find((arg) => arg.startsWith("--request="));
|
|
3579
3630
|
const visualDistArg = args.find((arg) => arg.startsWith("--visual-dist="));
|
|
3580
3631
|
const portArg = args.find((arg) => arg.startsWith("--port="));
|
|
3632
|
+
const runtimeArg = args.find((arg) => arg.startsWith("--runtime="));
|
|
3581
3633
|
const profileIdx = args.indexOf("--profile");
|
|
3582
3634
|
const profileVal = profileEqArg ? profileEqArg.replace("--profile=", "") : profileIdx >= 0 && profileIdx + 1 < args.length && !args[profileIdx + 1].startsWith("--") ? args[profileIdx + 1] : void 0;
|
|
3583
3635
|
return {
|
|
@@ -3599,6 +3651,7 @@ function parseFlags(args) {
|
|
|
3599
3651
|
request: requestArg ? requestArg.replace("--request=", "") : void 0,
|
|
3600
3652
|
visualDist: visualDistArg ? visualDistArg.replace("--visual-dist=", "") : void 0,
|
|
3601
3653
|
port: portArg ? portArg.replace("--port=", "") : void 0,
|
|
3654
|
+
runtime: runtimeArg ? runtimeArg.replace("--runtime=", "") : void 0,
|
|
3602
3655
|
a11y: args.includes("--a11y"),
|
|
3603
3656
|
purgeAgents: args.includes("--purge-agents") || args.includes("--purge")
|
|
3604
3657
|
};
|
|
@@ -3610,7 +3663,8 @@ var commandMap = {
|
|
|
3610
3663
|
const result = await runExecute({
|
|
3611
3664
|
cwd: process.cwd(),
|
|
3612
3665
|
naturalRequest: request,
|
|
3613
|
-
taskSlug: flags.taskSlug
|
|
3666
|
+
taskSlug: flags.taskSlug,
|
|
3667
|
+
runtime: flags.runtime
|
|
3614
3668
|
});
|
|
3615
3669
|
if (result && (result.overallStatus === "FAIL_QUALITY_GATE" || result.overallStatus === "BLOCKED" || result.overallStatus === "FAIL")) {
|
|
3616
3670
|
process.exitCode = result.exitCode === 2 ? 2 : 1;
|