agentopology 0.1.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/LICENSE +191 -0
- package/README.md +450 -0
- package/dist/analyzer/__tests__/analyzer.test.d.ts +2 -0
- package/dist/analyzer/__tests__/analyzer.test.d.ts.map +1 -0
- package/dist/analyzer/__tests__/analyzer.test.js +258 -0
- package/dist/analyzer/__tests__/analyzer.test.js.map +1 -0
- package/dist/analyzer/index.d.ts +44 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +308 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/bindings/__tests__/bindings.test.d.ts +2 -0
- package/dist/bindings/__tests__/bindings.test.d.ts.map +1 -0
- package/dist/bindings/__tests__/bindings.test.js +2089 -0
- package/dist/bindings/__tests__/bindings.test.js.map +1 -0
- package/dist/bindings/claude-code.d.ts +12 -0
- package/dist/bindings/claude-code.d.ts.map +1 -0
- package/dist/bindings/claude-code.js +2063 -0
- package/dist/bindings/claude-code.js.map +1 -0
- package/dist/bindings/codex.d.ts +18 -0
- package/dist/bindings/codex.d.ts.map +1 -0
- package/dist/bindings/codex.js +1633 -0
- package/dist/bindings/codex.js.map +1 -0
- package/dist/bindings/copilot-cli.d.ts +20 -0
- package/dist/bindings/copilot-cli.d.ts.map +1 -0
- package/dist/bindings/copilot-cli.js +1515 -0
- package/dist/bindings/copilot-cli.js.map +1 -0
- package/dist/bindings/cursor.d.ts +17 -0
- package/dist/bindings/cursor.d.ts.map +1 -0
- package/dist/bindings/cursor.js +1715 -0
- package/dist/bindings/cursor.js.map +1 -0
- package/dist/bindings/gemini-cli.d.ts +18 -0
- package/dist/bindings/gemini-cli.d.ts.map +1 -0
- package/dist/bindings/gemini-cli.js +1327 -0
- package/dist/bindings/gemini-cli.js.map +1 -0
- package/dist/bindings/index.d.ts +20 -0
- package/dist/bindings/index.d.ts.map +1 -0
- package/dist/bindings/index.js +33 -0
- package/dist/bindings/index.js.map +1 -0
- package/dist/bindings/kiro.d.ts +17 -0
- package/dist/bindings/kiro.d.ts.map +1 -0
- package/dist/bindings/kiro.js +1624 -0
- package/dist/bindings/kiro.js.map +1 -0
- package/dist/bindings/openclaw.d.ts +18 -0
- package/dist/bindings/openclaw.d.ts.map +1 -0
- package/dist/bindings/openclaw.js +1578 -0
- package/dist/bindings/openclaw.js.map +1 -0
- package/dist/bindings/types.d.ts +33 -0
- package/dist/bindings/types.d.ts.map +1 -0
- package/dist/bindings/types.js +23 -0
- package/dist/bindings/types.js.map +1 -0
- package/dist/cli/index.d.ts +19 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +679 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/docs/__tests__/docs.test.d.ts +2 -0
- package/dist/docs/__tests__/docs.test.d.ts.map +1 -0
- package/dist/docs/__tests__/docs.test.js +438 -0
- package/dist/docs/__tests__/docs.test.js.map +1 -0
- package/dist/docs/content.d.ts +16 -0
- package/dist/docs/content.d.ts.map +1 -0
- package/dist/docs/content.js +3273 -0
- package/dist/docs/content.js.map +1 -0
- package/dist/docs/index.d.ts +17 -0
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +117 -0
- package/dist/docs/index.js.map +1 -0
- package/dist/exporters/__tests__/exporters.test.d.ts +2 -0
- package/dist/exporters/__tests__/exporters.test.d.ts.map +1 -0
- package/dist/exporters/__tests__/exporters.test.js +731 -0
- package/dist/exporters/__tests__/exporters.test.js.map +1 -0
- package/dist/exporters/index.d.ts +13 -0
- package/dist/exporters/index.d.ts.map +1 -0
- package/dist/exporters/index.js +18 -0
- package/dist/exporters/index.js.map +1 -0
- package/dist/exporters/json.d.ts +8 -0
- package/dist/exporters/json.d.ts.map +1 -0
- package/dist/exporters/json.js +25 -0
- package/dist/exporters/json.js.map +1 -0
- package/dist/exporters/markdown.d.ts +9 -0
- package/dist/exporters/markdown.d.ts.map +1 -0
- package/dist/exporters/markdown.js +686 -0
- package/dist/exporters/markdown.js.map +1 -0
- package/dist/exporters/mermaid.d.ts +11 -0
- package/dist/exporters/mermaid.d.ts.map +1 -0
- package/dist/exporters/mermaid.js +161 -0
- package/dist/exporters/mermaid.js.map +1 -0
- package/dist/exporters/types.d.ts +22 -0
- package/dist/exporters/types.d.ts.map +1 -0
- package/dist/exporters/types.js +10 -0
- package/dist/exporters/types.js.map +1 -0
- package/dist/import/__tests__/import.test.d.ts +2 -0
- package/dist/import/__tests__/import.test.d.ts.map +1 -0
- package/dist/import/__tests__/import.test.js +781 -0
- package/dist/import/__tests__/import.test.js.map +1 -0
- package/dist/import/claude-code.d.ts +38 -0
- package/dist/import/claude-code.d.ts.map +1 -0
- package/dist/import/claude-code.js +897 -0
- package/dist/import/claude-code.js.map +1 -0
- package/dist/import/index.d.ts +18 -0
- package/dist/import/index.d.ts.map +1 -0
- package/dist/import/index.js +28 -0
- package/dist/import/index.js.map +1 -0
- package/dist/import/serializer.d.ts +11 -0
- package/dist/import/serializer.d.ts.map +1 -0
- package/dist/import/serializer.js +960 -0
- package/dist/import/serializer.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/__tests__/parser.test.d.ts +2 -0
- package/dist/parser/__tests__/parser.test.d.ts.map +1 -0
- package/dist/parser/__tests__/parser.test.js +6514 -0
- package/dist/parser/__tests__/parser.test.js.map +1 -0
- package/dist/parser/ast.d.ts +708 -0
- package/dist/parser/ast.d.ts.map +1 -0
- package/dist/parser/ast.js +12 -0
- package/dist/parser/ast.js.map +1 -0
- package/dist/parser/index.d.ts +253 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +1985 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/lexer.d.ts +100 -0
- package/dist/parser/lexer.d.ts.map +1 -0
- package/dist/parser/lexer.js +304 -0
- package/dist/parser/lexer.js.map +1 -0
- package/dist/parser/validator.d.ts +42 -0
- package/dist/parser/validator.d.ts.map +1 -0
- package/dist/parser/validator.js +2568 -0
- package/dist/parser/validator.js.map +1 -0
- package/dist/scaffold/__tests__/scaffold.test.d.ts +7 -0
- package/dist/scaffold/__tests__/scaffold.test.d.ts.map +1 -0
- package/dist/scaffold/__tests__/scaffold.test.js +295 -0
- package/dist/scaffold/__tests__/scaffold.test.js.map +1 -0
- package/dist/scaffold/incremental.d.ts +43 -0
- package/dist/scaffold/incremental.d.ts.map +1 -0
- package/dist/scaffold/incremental.js +185 -0
- package/dist/scaffold/incremental.js.map +1 -0
- package/dist/scaffold/index.d.ts +13 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +13 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/manifest.d.ts +30 -0
- package/dist/scaffold/manifest.d.ts.map +1 -0
- package/dist/scaffold/manifest.js +71 -0
- package/dist/scaffold/manifest.js.map +1 -0
- package/dist/scaffold/merge.d.ts +52 -0
- package/dist/scaffold/merge.d.ts.map +1 -0
- package/dist/scaffold/merge.js +134 -0
- package/dist/scaffold/merge.js.map +1 -0
- package/dist/scaffold/types.d.ts +51 -0
- package/dist/scaffold/types.d.ts.map +1 -0
- package/dist/scaffold/types.js +10 -0
- package/dist/scaffold/types.js.map +1 -0
- package/dist/sync/index.d.ts +34 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +392 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/visualizer/index.d.ts +17 -0
- package/dist/visualizer/index.d.ts.map +1 -0
- package/dist/visualizer/index.js +1976 -0
- package/dist/visualizer/index.js.map +1 -0
- package/examples/code-review.at +167 -0
- package/examples/data-processing.at +296 -0
- package/examples/openclaw-assistant.at +142 -0
- package/examples/scheduled-monitor.at +193 -0
- package/examples/simple-pipeline.at +163 -0
- package/package.json +77 -0
- package/skill/SKILL.md +403 -0
- package/skill/scripts/emit.ts +853 -0
- package/skill/scripts/visualize.ts +89 -0
- package/spec/grammar.md +1466 -0
- package/spec/reserved-keywords.md +147 -0
- package/spec/validation.md +535 -0
|
@@ -0,0 +1,1633 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Codex CLI binding.
|
|
3
|
+
*
|
|
4
|
+
* Generates `.codex/config.toml`, `AGENTS.md`, and supporting scripts
|
|
5
|
+
* from a parsed {@link TopologyAST}.
|
|
6
|
+
*
|
|
7
|
+
* Codex CLI uses TOML-based configuration and reads markdown instruction
|
|
8
|
+
* files. Multi-agent topologies generate per-agent TOML files under
|
|
9
|
+
* `.codex/agents/<name>.toml` and an `[agents]` section in config.toml.
|
|
10
|
+
* AGENTS.md provides a high-level topology overview with references to
|
|
11
|
+
* individual agent TOML files.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { deduplicateFiles } from "./types.js";
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Helpers
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/** Convert a kebab-case id to Title Case. */
|
|
20
|
+
function toTitle(id) {
|
|
21
|
+
return id
|
|
22
|
+
.split(/[-_]/)
|
|
23
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
24
|
+
.join(" ");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Map topology permission strings to Codex approval_policy values.
|
|
28
|
+
*
|
|
29
|
+
* Codex CLI supports these approval policies:
|
|
30
|
+
* - "untrusted" — only run trusted commands without asking (default, most restrictive)
|
|
31
|
+
* - "on-request" — model decides when to ask the user
|
|
32
|
+
* - "never" — never ask for user approval
|
|
33
|
+
*
|
|
34
|
+
* Legacy/deprecated:
|
|
35
|
+
* - "on-failure" — deprecated, prefer "on-request" for interactive or "never" for non-interactive
|
|
36
|
+
*/
|
|
37
|
+
function mapApprovalPolicy(perm) {
|
|
38
|
+
// Codex-native values — pass through
|
|
39
|
+
const nativeValues = ["untrusted", "on-request", "on-failure", "never"];
|
|
40
|
+
if (nativeValues.includes(perm))
|
|
41
|
+
return perm;
|
|
42
|
+
// Topology-level semantic values
|
|
43
|
+
switch (perm) {
|
|
44
|
+
case "supervised":
|
|
45
|
+
return "untrusted";
|
|
46
|
+
case "autonomous":
|
|
47
|
+
return "on-request";
|
|
48
|
+
case "interactive":
|
|
49
|
+
return "untrusted";
|
|
50
|
+
case "unrestricted":
|
|
51
|
+
return "never";
|
|
52
|
+
// Legacy Codex values — map to current equivalents
|
|
53
|
+
case "suggest":
|
|
54
|
+
return "untrusted";
|
|
55
|
+
case "auto-edit":
|
|
56
|
+
return "on-request";
|
|
57
|
+
case "full-auto":
|
|
58
|
+
return "never";
|
|
59
|
+
// Claude Code native values mapped to Codex equivalents
|
|
60
|
+
case "plan":
|
|
61
|
+
return "untrusted";
|
|
62
|
+
case "auto":
|
|
63
|
+
return "on-request";
|
|
64
|
+
case "confirm":
|
|
65
|
+
return "untrusted";
|
|
66
|
+
case "bypassPermissions":
|
|
67
|
+
return "never";
|
|
68
|
+
default:
|
|
69
|
+
return "untrusted"; // safe default
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Escape a TOML string value.
|
|
74
|
+
* Wraps in double quotes, escaping internal quotes and backslashes.
|
|
75
|
+
*/
|
|
76
|
+
function tomlString(value) {
|
|
77
|
+
const escaped = value
|
|
78
|
+
.replace(/\\/g, "\\\\")
|
|
79
|
+
.replace(/"/g, '\\"')
|
|
80
|
+
.replace(/\n/g, "\\n");
|
|
81
|
+
return `"${escaped}"`;
|
|
82
|
+
}
|
|
83
|
+
/** Format a TOML array of strings on a single line. */
|
|
84
|
+
function tomlStringArray(values) {
|
|
85
|
+
return `[${values.map(tomlString).join(", ")}]`;
|
|
86
|
+
}
|
|
87
|
+
/** Generate a shell script stub with a header comment. */
|
|
88
|
+
function shellStub(description) {
|
|
89
|
+
return [
|
|
90
|
+
"#!/usr/bin/env bash",
|
|
91
|
+
`# ${description}`,
|
|
92
|
+
"# Auto-generated by agentopology scaffold — edit as needed.",
|
|
93
|
+
"set -euo pipefail",
|
|
94
|
+
"",
|
|
95
|
+
'echo "TODO: implement this script"',
|
|
96
|
+
"",
|
|
97
|
+
].join("\n");
|
|
98
|
+
}
|
|
99
|
+
/** Generate an empty directory marker (a .gitkeep file). */
|
|
100
|
+
function gitkeep(dirPath) {
|
|
101
|
+
return { path: `${dirPath}/.gitkeep`, content: "", category: "machine" };
|
|
102
|
+
}
|
|
103
|
+
/** Format a SchemaType to a human-readable string. */
|
|
104
|
+
function formatSchemaType(t) {
|
|
105
|
+
switch (t.kind) {
|
|
106
|
+
case "primitive":
|
|
107
|
+
return t.value;
|
|
108
|
+
case "array":
|
|
109
|
+
return `${formatSchemaType(t.itemType)}[]`;
|
|
110
|
+
case "enum":
|
|
111
|
+
return t.values.join(" | ");
|
|
112
|
+
case "ref":
|
|
113
|
+
return t.name;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Format a SchemaFieldDef to a readable line. */
|
|
117
|
+
function formatSchemaField(f) {
|
|
118
|
+
const opt = f.optional ? "?" : "";
|
|
119
|
+
return `- ${f.name}${opt}: ${formatSchemaType(f.type)}`;
|
|
120
|
+
}
|
|
121
|
+
/** Format a RetryConfig to a readable string. */
|
|
122
|
+
function formatRetryConfig(retry) {
|
|
123
|
+
if (typeof retry === "number")
|
|
124
|
+
return String(retry);
|
|
125
|
+
const parts = [`max ${retry.max}`];
|
|
126
|
+
if (retry.backoff)
|
|
127
|
+
parts.push(`backoff ${retry.backoff}`);
|
|
128
|
+
if (retry.interval)
|
|
129
|
+
parts.push(`interval ${retry.interval}`);
|
|
130
|
+
if (retry.maxInterval)
|
|
131
|
+
parts.push(`max-interval ${retry.maxInterval}`);
|
|
132
|
+
if (retry.jitter)
|
|
133
|
+
parts.push("jitter");
|
|
134
|
+
if (retry.nonRetryable && retry.nonRetryable.length > 0) {
|
|
135
|
+
parts.push(`non-retryable: ${retry.nonRetryable.join(", ")}`);
|
|
136
|
+
}
|
|
137
|
+
return parts.join(", ");
|
|
138
|
+
}
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// TOML generation — .codex/config.toml
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
/**
|
|
143
|
+
* Generate the main `.codex/config.toml` configuration file.
|
|
144
|
+
*
|
|
145
|
+
* Maps topology metadata, the most permissive agent's approval policy,
|
|
146
|
+
* model, sandbox, and provider settings. Extension fields from
|
|
147
|
+
* `extensions["codex"]` on the topology or agents are merged in.
|
|
148
|
+
*/
|
|
149
|
+
function generateCodexToml(ast) {
|
|
150
|
+
const lines = [];
|
|
151
|
+
lines.push("# Codex CLI configuration");
|
|
152
|
+
lines.push("# Auto-generated by agentopology scaffold — edit as needed.");
|
|
153
|
+
lines.push("");
|
|
154
|
+
// Determine the primary model and approval policy from agents.
|
|
155
|
+
// Use the first orchestrator or first agent's model, falling back to gpt-4o.
|
|
156
|
+
const orchestrator = ast.nodes.find((n) => n.type === "orchestrator");
|
|
157
|
+
const agents = ast.nodes.filter((n) => n.type === "agent");
|
|
158
|
+
const primaryModel = orchestrator?.model ?? agents[0]?.model ?? "o4-mini";
|
|
159
|
+
const primaryPermission = agents[0]?.permissions ?? "supervised";
|
|
160
|
+
lines.push(`model = ${tomlString(primaryModel)}`);
|
|
161
|
+
lines.push(`approval_policy = ${tomlString(mapApprovalPolicy(primaryPermission))}`);
|
|
162
|
+
// Disable response storage if env has a flag or topology explicitly sets it
|
|
163
|
+
const codexTopologyExt = getTopologyExtensions(ast);
|
|
164
|
+
if (codexTopologyExt?.disable_response_storage != null) {
|
|
165
|
+
lines.push(`disable_response_storage = ${codexTopologyExt.disable_response_storage === true}`);
|
|
166
|
+
}
|
|
167
|
+
lines.push("");
|
|
168
|
+
// Model params from defaults or primary agent
|
|
169
|
+
const temp = ast.defaults?.temperature ?? agents[0]?.temperature;
|
|
170
|
+
const maxTok = ast.defaults?.maxTokens ?? agents[0]?.maxTokens;
|
|
171
|
+
if (temp != null || maxTok != null) {
|
|
172
|
+
lines.push("# Model parameters");
|
|
173
|
+
if (temp != null)
|
|
174
|
+
lines.push(`# temperature = ${temp}`);
|
|
175
|
+
if (maxTok != null)
|
|
176
|
+
lines.push(`# max_tokens = ${maxTok}`);
|
|
177
|
+
lines.push("");
|
|
178
|
+
}
|
|
179
|
+
// History settings from extensions
|
|
180
|
+
if (codexTopologyExt?.history) {
|
|
181
|
+
const history = codexTopologyExt.history;
|
|
182
|
+
lines.push("[history]");
|
|
183
|
+
if (history.max_conversations != null) {
|
|
184
|
+
lines.push(`max_conversations = ${history.max_conversations}`);
|
|
185
|
+
}
|
|
186
|
+
if (history.persistence != null) {
|
|
187
|
+
lines.push(`persistence = ${tomlString(String(history.persistence))}`);
|
|
188
|
+
}
|
|
189
|
+
lines.push("");
|
|
190
|
+
}
|
|
191
|
+
// Sandbox settings — first-class AST field, then extension, then derived
|
|
192
|
+
// Codex supports: "read-only", "workspace-write", "danger-full-access"
|
|
193
|
+
const topSandbox = ast.settings?.sandbox;
|
|
194
|
+
const sandboxType = topSandbox != null
|
|
195
|
+
? (typeof topSandbox === 'boolean' ? (topSandbox ? 'read-only' : 'danger-full-access') : mapSandboxMode(String(topSandbox)))
|
|
196
|
+
: codexTopologyExt?.sandbox_type ?? deriveSandboxType(ast);
|
|
197
|
+
if (sandboxType) {
|
|
198
|
+
lines.push(`sandbox_mode = ${tomlString(String(sandboxType))}`);
|
|
199
|
+
lines.push("");
|
|
200
|
+
}
|
|
201
|
+
// Providers from extensions or ast.providers
|
|
202
|
+
const extProviders = codexTopologyExt?.providers;
|
|
203
|
+
if (extProviders && extProviders.length > 0) {
|
|
204
|
+
// Extensions path takes priority
|
|
205
|
+
for (const provider of extProviders) {
|
|
206
|
+
lines.push("[[providers]]");
|
|
207
|
+
if (provider.name)
|
|
208
|
+
lines.push(`name = ${tomlString(String(provider.name))}`);
|
|
209
|
+
if (provider.base_url)
|
|
210
|
+
lines.push(`base_url = ${tomlString(String(provider.base_url))}`);
|
|
211
|
+
if (provider.env_key)
|
|
212
|
+
lines.push(`env_key = ${tomlString(String(provider.env_key))}`);
|
|
213
|
+
lines.push("");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (ast.providers && ast.providers.length > 0) {
|
|
217
|
+
// Fallback: map ast.providers to Codex [[providers]] format
|
|
218
|
+
for (const p of ast.providers) {
|
|
219
|
+
lines.push("[[providers]]");
|
|
220
|
+
lines.push(`name = ${tomlString(p.name)}`);
|
|
221
|
+
if (p.baseUrl)
|
|
222
|
+
lines.push(`base_url = ${tomlString(p.baseUrl)}`);
|
|
223
|
+
if (p.apiKey) {
|
|
224
|
+
// Convert ${ENV_VAR} to just ENV_VAR for Codex env_key format
|
|
225
|
+
const envKey = p.apiKey.replace(/^\$\{(.+)\}$/, "$1");
|
|
226
|
+
lines.push(`env_key = ${tomlString(envKey)}`);
|
|
227
|
+
}
|
|
228
|
+
if (p.default)
|
|
229
|
+
lines.push(`default = true`);
|
|
230
|
+
if (p.models && p.models.length > 0) {
|
|
231
|
+
lines.push(`models = ${tomlStringArray(p.models)}`);
|
|
232
|
+
}
|
|
233
|
+
if (p.auth) {
|
|
234
|
+
lines.push(`# Auth type: ${p.auth.type}`);
|
|
235
|
+
if (p.auth.issuer)
|
|
236
|
+
lines.push(`# Auth issuer: ${p.auth.issuer}`);
|
|
237
|
+
if (p.auth.audience)
|
|
238
|
+
lines.push(`# Auth audience: ${p.auth.audience}`);
|
|
239
|
+
if (p.auth.tokenUrl)
|
|
240
|
+
lines.push(`# Auth token URL: ${p.auth.tokenUrl}`);
|
|
241
|
+
if (p.auth.clientId)
|
|
242
|
+
lines.push(`# Auth client ID: ${p.auth.clientId}`);
|
|
243
|
+
if (p.auth.scopes && p.auth.scopes.length > 0) {
|
|
244
|
+
lines.push(`# Auth scopes: ${p.auth.scopes.join(", ")}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (p.extra) {
|
|
248
|
+
for (const [ek, ev] of Object.entries(p.extra)) {
|
|
249
|
+
if (typeof ev === "string") {
|
|
250
|
+
lines.push(`${ek} = ${tomlString(ev)}`);
|
|
251
|
+
}
|
|
252
|
+
else if (typeof ev === "boolean") {
|
|
253
|
+
lines.push(`${ek} = ${ev}`);
|
|
254
|
+
}
|
|
255
|
+
else if (typeof ev === "number") {
|
|
256
|
+
lines.push(`${ek} = ${ev}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
lines.push("");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// MCP servers from ast.mcpServers — render as [mcp_servers.<name>] tables
|
|
264
|
+
if (ast.mcpServers && Object.keys(ast.mcpServers).length > 0) {
|
|
265
|
+
for (const [name, config] of Object.entries(ast.mcpServers)) {
|
|
266
|
+
lines.push(`[mcp_servers.${name}]`);
|
|
267
|
+
for (const [ck, cv] of Object.entries(config)) {
|
|
268
|
+
if (typeof cv === "string") {
|
|
269
|
+
lines.push(`${ck} = ${tomlString(cv)}`);
|
|
270
|
+
}
|
|
271
|
+
else if (typeof cv === "boolean") {
|
|
272
|
+
lines.push(`${ck} = ${cv}`);
|
|
273
|
+
}
|
|
274
|
+
else if (typeof cv === "number") {
|
|
275
|
+
lines.push(`${ck} = ${cv}`);
|
|
276
|
+
}
|
|
277
|
+
else if (Array.isArray(cv)) {
|
|
278
|
+
lines.push(`${ck} = ${tomlStringArray(cv.map(String))}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
lines.push("");
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Fallback chain — document if present (Codex doesn't natively support it)
|
|
285
|
+
const fallbackChain = ast.settings?.["fallback-chain"];
|
|
286
|
+
if (fallbackChain && fallbackChain.length > 0) {
|
|
287
|
+
lines.push(`# Model fallback chain: ${fallbackChain.join(" -> ")}`);
|
|
288
|
+
lines.push("");
|
|
289
|
+
}
|
|
290
|
+
// Merge any remaining extension fields as top-level TOML keys
|
|
291
|
+
if (codexTopologyExt) {
|
|
292
|
+
const handled = new Set([
|
|
293
|
+
"disable_response_storage",
|
|
294
|
+
"history",
|
|
295
|
+
"sandbox_type",
|
|
296
|
+
"providers",
|
|
297
|
+
]);
|
|
298
|
+
for (const [key, value] of Object.entries(codexTopologyExt)) {
|
|
299
|
+
if (handled.has(key))
|
|
300
|
+
continue;
|
|
301
|
+
if (typeof value === "string") {
|
|
302
|
+
lines.push(`${key} = ${tomlString(value)}`);
|
|
303
|
+
}
|
|
304
|
+
else if (typeof value === "boolean") {
|
|
305
|
+
lines.push(`${key} = ${value}`);
|
|
306
|
+
}
|
|
307
|
+
else if (typeof value === "number") {
|
|
308
|
+
lines.push(`${key} = ${value}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Multi-agent support: [agents] section with per-agent references
|
|
313
|
+
if (agents.length > 0) {
|
|
314
|
+
lines.push("");
|
|
315
|
+
lines.push("[agents]");
|
|
316
|
+
lines.push(`max_threads = ${Math.min(agents.length, 10)}`);
|
|
317
|
+
lines.push(`max_depth = 1`);
|
|
318
|
+
lines.push("");
|
|
319
|
+
for (const agent of agents) {
|
|
320
|
+
const desc = agent.description
|
|
321
|
+
?? ast.roles[agent.role ?? ""]
|
|
322
|
+
?? ast.roles[agent.id]
|
|
323
|
+
?? agent.role
|
|
324
|
+
?? toTitle(agent.id);
|
|
325
|
+
lines.push(`[agents.${agent.id}]`);
|
|
326
|
+
lines.push(`description = ${tomlString(desc)}`);
|
|
327
|
+
lines.push(`config_file = ${tomlString(`agents/${agent.id}.toml`)}`);
|
|
328
|
+
lines.push("");
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
path: ".codex/config.toml",
|
|
333
|
+
content: lines.join("\n") + "\n",
|
|
334
|
+
category: "machine",
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Map generic sandbox mode strings to Codex sandbox values.
|
|
339
|
+
* Codex supports: "read-only", "workspace-write", "danger-full-access"
|
|
340
|
+
*/
|
|
341
|
+
function mapSandboxMode(mode) {
|
|
342
|
+
// Pass through native Codex values
|
|
343
|
+
const nativeValues = ["read-only", "workspace-write", "danger-full-access"];
|
|
344
|
+
if (nativeValues.includes(mode))
|
|
345
|
+
return mode;
|
|
346
|
+
switch (mode) {
|
|
347
|
+
case "docker":
|
|
348
|
+
case "container":
|
|
349
|
+
case "strict":
|
|
350
|
+
return "read-only";
|
|
351
|
+
case "write":
|
|
352
|
+
case "workspace":
|
|
353
|
+
return "workspace-write";
|
|
354
|
+
case "none":
|
|
355
|
+
case "disabled":
|
|
356
|
+
case "off":
|
|
357
|
+
return "danger-full-access";
|
|
358
|
+
default:
|
|
359
|
+
return "read-only"; // safe default
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Map agent sandbox values to Codex sandbox_mode for per-agent TOML.
|
|
364
|
+
* true/"docker"/"strict" → "read-only", "write"/"workspace" → "workspace-write",
|
|
365
|
+
* false/"none" → "danger-full-access", "network-only" → "read-only"
|
|
366
|
+
*/
|
|
367
|
+
function mapAgentSandbox(sandbox) {
|
|
368
|
+
if (typeof sandbox === "boolean") {
|
|
369
|
+
return sandbox ? "read-only" : "danger-full-access";
|
|
370
|
+
}
|
|
371
|
+
switch (sandbox) {
|
|
372
|
+
case "docker":
|
|
373
|
+
case "strict":
|
|
374
|
+
case "container":
|
|
375
|
+
case "network-only":
|
|
376
|
+
case "read-only":
|
|
377
|
+
return "read-only";
|
|
378
|
+
case "write":
|
|
379
|
+
case "workspace":
|
|
380
|
+
case "workspace-write":
|
|
381
|
+
return "workspace-write";
|
|
382
|
+
case "none":
|
|
383
|
+
case "disabled":
|
|
384
|
+
case "off":
|
|
385
|
+
case "danger-full-access":
|
|
386
|
+
return "danger-full-access";
|
|
387
|
+
default:
|
|
388
|
+
return "read-only"; // safe default
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Map thinking/reasoning level to Codex model_reasoning_effort.
|
|
393
|
+
* off → "" (omit), low → "low", medium → "medium", high → "high", max → "high"
|
|
394
|
+
*/
|
|
395
|
+
function mapReasoningEffort(thinking) {
|
|
396
|
+
switch (thinking) {
|
|
397
|
+
case "off":
|
|
398
|
+
return null; // omit from TOML
|
|
399
|
+
case "low":
|
|
400
|
+
return "low";
|
|
401
|
+
case "medium":
|
|
402
|
+
return "medium";
|
|
403
|
+
case "high":
|
|
404
|
+
return "high";
|
|
405
|
+
case "max":
|
|
406
|
+
return "high";
|
|
407
|
+
default:
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Derive sandbox type from topology permissions.
|
|
413
|
+
* More restrictive permissions suggest sandboxing.
|
|
414
|
+
*/
|
|
415
|
+
function deriveSandboxType(ast) {
|
|
416
|
+
const agents = ast.nodes.filter((n) => n.type === "agent");
|
|
417
|
+
const hasUnrestricted = agents.some((a) => a.permissions === "unrestricted" ||
|
|
418
|
+
a.permissions === "full-auto" ||
|
|
419
|
+
a.permissions === "bypassPermissions");
|
|
420
|
+
// If any agent is unrestricted, no sandbox (user opted out of restrictions)
|
|
421
|
+
if (hasUnrestricted)
|
|
422
|
+
return "danger-full-access";
|
|
423
|
+
// Default: no explicit sandbox section needed
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Extract codex-specific extension fields from the topology level.
|
|
428
|
+
* Checks settings.extensions["codex"] and any top-level extension block.
|
|
429
|
+
*/
|
|
430
|
+
function getTopologyExtensions(ast) {
|
|
431
|
+
const settingsExt = ast.settings?.extensions;
|
|
432
|
+
if (settingsExt?.codex)
|
|
433
|
+
return settingsExt.codex;
|
|
434
|
+
// Also check top-level ast.extensions["codex"]
|
|
435
|
+
if (ast.extensions?.codex)
|
|
436
|
+
return ast.extensions.codex;
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
// AGENTS.md generation
|
|
441
|
+
// ---------------------------------------------------------------------------
|
|
442
|
+
/**
|
|
443
|
+
* Generate `AGENTS.md` — the primary instructions file for Codex CLI.
|
|
444
|
+
*
|
|
445
|
+
* Since Codex is a single-agent CLI, multi-agent topologies are expressed
|
|
446
|
+
* as instruction sections describing each agent's role, responsibilities,
|
|
447
|
+
* tools, and memory access patterns. The orchestrator reads this file to
|
|
448
|
+
* understand the full workflow.
|
|
449
|
+
*/
|
|
450
|
+
function generateAgentsMd(ast) {
|
|
451
|
+
const sections = [];
|
|
452
|
+
const name = ast.topology.name;
|
|
453
|
+
// Header
|
|
454
|
+
sections.push(`# ${toTitle(name)} — Agent Instructions`);
|
|
455
|
+
sections.push("");
|
|
456
|
+
// #17 — isFragment notice
|
|
457
|
+
if (ast.isFragment) {
|
|
458
|
+
sections.push("**Note:** This is a topology fragment — it defines a reusable sub-topology.");
|
|
459
|
+
sections.push("");
|
|
460
|
+
}
|
|
461
|
+
if (ast.topology.description) {
|
|
462
|
+
sections.push(ast.topology.description);
|
|
463
|
+
sections.push("");
|
|
464
|
+
}
|
|
465
|
+
sections.push(`Version: ${ast.topology.version}`);
|
|
466
|
+
if (ast.topology.patterns.length > 0) {
|
|
467
|
+
sections.push(`Patterns: ${ast.topology.patterns.join(", ")}`);
|
|
468
|
+
}
|
|
469
|
+
// #7 — topology-level domain, timeout, errorHandler, durable, foundations, advanced
|
|
470
|
+
if (ast.topology.domain) {
|
|
471
|
+
sections.push(`Domain: ${ast.topology.domain}`);
|
|
472
|
+
}
|
|
473
|
+
if (ast.topology.timeout) {
|
|
474
|
+
sections.push(`Timeout: ${ast.topology.timeout}`);
|
|
475
|
+
}
|
|
476
|
+
if (ast.topology.errorHandler) {
|
|
477
|
+
sections.push(`Error handler: ${ast.topology.errorHandler}`);
|
|
478
|
+
}
|
|
479
|
+
if (ast.topology.durable) {
|
|
480
|
+
sections.push(`Durable: true`);
|
|
481
|
+
}
|
|
482
|
+
if (ast.topology.foundations && ast.topology.foundations.length > 0) {
|
|
483
|
+
sections.push(`Foundations: ${ast.topology.foundations.join(", ")}`);
|
|
484
|
+
}
|
|
485
|
+
if (ast.topology.advanced && ast.topology.advanced.length > 0) {
|
|
486
|
+
sections.push(`Advanced: ${ast.topology.advanced.join(", ")}`);
|
|
487
|
+
}
|
|
488
|
+
sections.push("");
|
|
489
|
+
// Defaults section
|
|
490
|
+
if (ast.defaults) {
|
|
491
|
+
sections.push("## Defaults");
|
|
492
|
+
sections.push("");
|
|
493
|
+
const d = ast.defaults;
|
|
494
|
+
if (d.temperature != null)
|
|
495
|
+
sections.push(`- **Temperature:** ${d.temperature}`);
|
|
496
|
+
if (d.maxTokens != null)
|
|
497
|
+
sections.push(`- **Max tokens:** ${d.maxTokens}`);
|
|
498
|
+
if (d.topP != null)
|
|
499
|
+
sections.push(`- **Top-p:** ${d.topP}`);
|
|
500
|
+
if (d.topK != null)
|
|
501
|
+
sections.push(`- **Top-k:** ${d.topK}`);
|
|
502
|
+
if (d.stop && d.stop.length > 0)
|
|
503
|
+
sections.push(`- **Stop sequences:** ${d.stop.join(", ")}`);
|
|
504
|
+
if (d.seed != null)
|
|
505
|
+
sections.push(`- **Seed:** ${d.seed}`);
|
|
506
|
+
if (d.thinking)
|
|
507
|
+
sections.push(`- **Thinking:** ${d.thinking}`);
|
|
508
|
+
if (d.thinkingBudget != null)
|
|
509
|
+
sections.push(`- **Thinking budget:** ${d.thinkingBudget}`);
|
|
510
|
+
if (d.outputFormat)
|
|
511
|
+
sections.push(`- **Output format:** ${d.outputFormat}`);
|
|
512
|
+
if (d.timeout)
|
|
513
|
+
sections.push(`- **Timeout:** ${d.timeout}`);
|
|
514
|
+
if (d.logLevel)
|
|
515
|
+
sections.push(`- **Log level:** ${d.logLevel}`);
|
|
516
|
+
sections.push("");
|
|
517
|
+
}
|
|
518
|
+
// Orchestrator section (#14)
|
|
519
|
+
const orch = ast.nodes.find((n) => n.type === "orchestrator");
|
|
520
|
+
if (orch) {
|
|
521
|
+
const orchTitle = orch.label || toTitle(orch.id);
|
|
522
|
+
sections.push(`## Orchestrator — ${orchTitle}`);
|
|
523
|
+
sections.push("");
|
|
524
|
+
sections.push(`Model: ${orch.model}`);
|
|
525
|
+
if (orch.handles?.length > 0) {
|
|
526
|
+
sections.push(`Handles: ${orch.handles.join(", ")}`);
|
|
527
|
+
}
|
|
528
|
+
if (orch.generates) {
|
|
529
|
+
sections.push(`Generates: ${orch.generates}`);
|
|
530
|
+
}
|
|
531
|
+
if (orch.outputs) {
|
|
532
|
+
sections.push("Outputs:");
|
|
533
|
+
for (const [field, values] of Object.entries(orch.outputs)) {
|
|
534
|
+
sections.push(` - ${field}: ${values.join(" | ")}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
sections.push("");
|
|
538
|
+
}
|
|
539
|
+
// Flow section
|
|
540
|
+
if (ast.edges.length > 0) {
|
|
541
|
+
sections.push("## Flow");
|
|
542
|
+
sections.push("");
|
|
543
|
+
for (const edge of ast.edges) {
|
|
544
|
+
let line = `${edge.from} -> ${edge.to}`;
|
|
545
|
+
if (edge.condition)
|
|
546
|
+
line += ` [when ${edge.condition}]`;
|
|
547
|
+
if (edge.maxIterations)
|
|
548
|
+
line += ` [max ${edge.maxIterations}]`;
|
|
549
|
+
if (edge.per)
|
|
550
|
+
line += ` [per ${edge.per}]`;
|
|
551
|
+
if (edge.isError)
|
|
552
|
+
line += ` [error${edge.errorType ? `: ${edge.errorType}` : ""}]`;
|
|
553
|
+
if (edge.weight != null)
|
|
554
|
+
line += ` [weight ${edge.weight}]`;
|
|
555
|
+
if (edge.reflection)
|
|
556
|
+
line += ` [reflection]`;
|
|
557
|
+
if (edge.race)
|
|
558
|
+
line += ` [race]`;
|
|
559
|
+
if (edge.tolerance != null)
|
|
560
|
+
line += ` [tolerance: ${edge.tolerance}]`;
|
|
561
|
+
if (edge.wait)
|
|
562
|
+
line += ` [wait ${edge.wait}]`;
|
|
563
|
+
sections.push(`- ${line}`);
|
|
564
|
+
}
|
|
565
|
+
sections.push("");
|
|
566
|
+
}
|
|
567
|
+
// Schedules section (Codex doesn't natively support cron; document for reference)
|
|
568
|
+
if (ast.schedules && ast.schedules.length > 0) {
|
|
569
|
+
sections.push("## Schedules");
|
|
570
|
+
sections.push("");
|
|
571
|
+
for (const job of ast.schedules) {
|
|
572
|
+
const timing = job.cron ? `runs at \`${job.cron}\`` : `runs ${job.every}`;
|
|
573
|
+
const target = job.agent
|
|
574
|
+
? `triggers agent \`${job.agent}\``
|
|
575
|
+
: job.action
|
|
576
|
+
? `triggers action \`${job.action}\``
|
|
577
|
+
: "";
|
|
578
|
+
const suffix = target ? `, ${target}` : "";
|
|
579
|
+
const enabled = job.enabled === false ? " (disabled)" : "";
|
|
580
|
+
sections.push(`- **${job.id}**: ${timing}${suffix}${enabled}`);
|
|
581
|
+
}
|
|
582
|
+
sections.push("");
|
|
583
|
+
}
|
|
584
|
+
// Interfaces section (document external interfaces for reference)
|
|
585
|
+
if (ast.interfaces && ast.interfaces.length > 0) {
|
|
586
|
+
sections.push("## Interfaces");
|
|
587
|
+
sections.push("");
|
|
588
|
+
for (const iface of ast.interfaces) {
|
|
589
|
+
const details = [];
|
|
590
|
+
if (iface.type)
|
|
591
|
+
details.push(iface.type);
|
|
592
|
+
for (const [k, v] of Object.entries(iface.config)) {
|
|
593
|
+
details.push(`${k} ${v}`);
|
|
594
|
+
}
|
|
595
|
+
const desc = details.length > 0 ? details.join(", ") : "configured";
|
|
596
|
+
sections.push(`- **${iface.id}**: ${desc}`);
|
|
597
|
+
}
|
|
598
|
+
sections.push("");
|
|
599
|
+
}
|
|
600
|
+
// Agent sections
|
|
601
|
+
const agents = ast.nodes.filter((n) => n.type === "agent");
|
|
602
|
+
if (agents.length > 0) {
|
|
603
|
+
sections.push("## Agents");
|
|
604
|
+
sections.push("");
|
|
605
|
+
for (const agent of agents) {
|
|
606
|
+
sections.push(`### ${agent.label || toTitle(agent.id)}`);
|
|
607
|
+
sections.push("");
|
|
608
|
+
sections.push(`> See \`.codex/agents/${agent.id}.toml\` for full configuration.`);
|
|
609
|
+
sections.push("");
|
|
610
|
+
// Role
|
|
611
|
+
const roleText = ast.roles[agent.role ?? ""] ?? ast.roles[agent.id] ?? agent.role;
|
|
612
|
+
if (roleText) {
|
|
613
|
+
sections.push(`**Role:** ${roleText}`);
|
|
614
|
+
sections.push("");
|
|
615
|
+
}
|
|
616
|
+
// Description
|
|
617
|
+
if (agent.description) {
|
|
618
|
+
sections.push(agent.description);
|
|
619
|
+
sections.push("");
|
|
620
|
+
}
|
|
621
|
+
// Model and permissions
|
|
622
|
+
const model = agent.model ?? "o4-mini";
|
|
623
|
+
const policy = mapApprovalPolicy(agent.permissions ?? "supervised");
|
|
624
|
+
sections.push(`- **Model:** ${model}`);
|
|
625
|
+
sections.push(`- **Approval policy:** ${policy}`);
|
|
626
|
+
// Phase
|
|
627
|
+
if (agent.phase != null) {
|
|
628
|
+
sections.push(`- **Phase:** ${agent.phase}`);
|
|
629
|
+
}
|
|
630
|
+
// Sampling parameters
|
|
631
|
+
if (agent.temperature != null) {
|
|
632
|
+
sections.push(`- **Temperature:** ${agent.temperature}`);
|
|
633
|
+
}
|
|
634
|
+
if (agent.maxTokens != null) {
|
|
635
|
+
sections.push(`- **Max tokens:** ${agent.maxTokens}`);
|
|
636
|
+
}
|
|
637
|
+
if (agent.topP != null) {
|
|
638
|
+
sections.push(`- **Top-p:** ${agent.topP}`);
|
|
639
|
+
}
|
|
640
|
+
if (agent.topK != null) {
|
|
641
|
+
sections.push(`- **Top-k:** ${agent.topK}`);
|
|
642
|
+
}
|
|
643
|
+
if (agent.stop && agent.stop.length > 0) {
|
|
644
|
+
sections.push(`- **Stop sequences:** ${agent.stop.join(", ")}`);
|
|
645
|
+
}
|
|
646
|
+
if (agent.seed != null) {
|
|
647
|
+
sections.push(`- **Seed:** ${agent.seed}`);
|
|
648
|
+
}
|
|
649
|
+
// Thinking / reasoning
|
|
650
|
+
if (agent.thinking) {
|
|
651
|
+
sections.push(`- **Reasoning level:** ${agent.thinking}`);
|
|
652
|
+
}
|
|
653
|
+
if (agent.thinkingBudget != null) {
|
|
654
|
+
sections.push(`- **Thinking budget:** ${agent.thinkingBudget}`);
|
|
655
|
+
}
|
|
656
|
+
// Output format
|
|
657
|
+
if (agent.outputFormat) {
|
|
658
|
+
sections.push(`- **Output format:** ${agent.outputFormat}`);
|
|
659
|
+
}
|
|
660
|
+
// Log level
|
|
661
|
+
if (agent.logLevel) {
|
|
662
|
+
sections.push(`- **Log level:** ${agent.logLevel}`);
|
|
663
|
+
}
|
|
664
|
+
// Timeout
|
|
665
|
+
if (agent.timeout) {
|
|
666
|
+
sections.push(`- **Maximum execution time:** ${agent.timeout}`);
|
|
667
|
+
}
|
|
668
|
+
// On-fail
|
|
669
|
+
if (agent.onFail) {
|
|
670
|
+
sections.push(`- **On failure:** ${agent.onFail}`);
|
|
671
|
+
}
|
|
672
|
+
// Retry
|
|
673
|
+
if (agent.retry != null) {
|
|
674
|
+
sections.push(`- **Retry:** ${formatRetryConfig(agent.retry)}`);
|
|
675
|
+
}
|
|
676
|
+
// Join semantics
|
|
677
|
+
if (agent.join) {
|
|
678
|
+
sections.push(`- **Wait for:** ${agent.join}`);
|
|
679
|
+
}
|
|
680
|
+
// Rate limit
|
|
681
|
+
if (agent.rateLimit) {
|
|
682
|
+
sections.push(`- **Rate limit:** ${agent.rateLimit}`);
|
|
683
|
+
}
|
|
684
|
+
// Circuit breaker
|
|
685
|
+
if (agent.circuitBreaker) {
|
|
686
|
+
const cb = agent.circuitBreaker;
|
|
687
|
+
sections.push(`- **Circuit breaker:** threshold ${cb.threshold}, window ${cb.window}, cooldown ${cb.cooldown}`);
|
|
688
|
+
}
|
|
689
|
+
// Compensates (saga)
|
|
690
|
+
if (agent.compensates) {
|
|
691
|
+
sections.push(`- **Compensates:** ${agent.compensates} (saga rollback)`);
|
|
692
|
+
}
|
|
693
|
+
// Prompt
|
|
694
|
+
if (agent.prompt) {
|
|
695
|
+
sections.push("");
|
|
696
|
+
sections.push("#### Instructions");
|
|
697
|
+
sections.push("");
|
|
698
|
+
sections.push(agent.prompt);
|
|
699
|
+
sections.push("");
|
|
700
|
+
}
|
|
701
|
+
// Prompt variants
|
|
702
|
+
if (agent.variants && agent.variants.length > 0) {
|
|
703
|
+
sections.push("");
|
|
704
|
+
sections.push("#### Prompt Variants");
|
|
705
|
+
sections.push("");
|
|
706
|
+
for (const v of agent.variants) {
|
|
707
|
+
const parts = [`**${v.id}** (weight ${v.weight})`];
|
|
708
|
+
if (v.model)
|
|
709
|
+
parts.push(`model: ${v.model}`);
|
|
710
|
+
if (v.temperature != null)
|
|
711
|
+
parts.push(`temperature: ${v.temperature}`);
|
|
712
|
+
sections.push(`- ${parts.join(", ")}`);
|
|
713
|
+
if (v.prompt) {
|
|
714
|
+
sections.push(` > ${v.prompt.replace(/\n/g, "\n > ")}`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
sections.push("");
|
|
718
|
+
}
|
|
719
|
+
// Tools
|
|
720
|
+
if (agent.tools && agent.tools.length > 0) {
|
|
721
|
+
sections.push(`- **Tools:** ${agent.tools.join(", ")}`);
|
|
722
|
+
}
|
|
723
|
+
if (agent.disallowedTools && agent.disallowedTools.length > 0) {
|
|
724
|
+
sections.push(`- **Disallowed tools:** ${agent.disallowedTools.join(", ")}`);
|
|
725
|
+
}
|
|
726
|
+
// Skills
|
|
727
|
+
if (agent.skills && agent.skills.length > 0) {
|
|
728
|
+
sections.push(`- **Skills:** ${agent.skills.join(", ")}`);
|
|
729
|
+
}
|
|
730
|
+
// Memory access
|
|
731
|
+
if (agent.reads && agent.reads.length > 0) {
|
|
732
|
+
sections.push(`- **Reads:** ${agent.reads.join(", ")}`);
|
|
733
|
+
}
|
|
734
|
+
if (agent.writes && agent.writes.length > 0) {
|
|
735
|
+
sections.push(`- **Writes:** ${agent.writes.join(", ")}`);
|
|
736
|
+
}
|
|
737
|
+
// Input/Output schemas
|
|
738
|
+
if (agent.inputSchema && agent.inputSchema.length > 0) {
|
|
739
|
+
sections.push("- **Input schema:**");
|
|
740
|
+
for (const field of agent.inputSchema) {
|
|
741
|
+
sections.push(` ${formatSchemaField(field)}`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (agent.outputSchema && agent.outputSchema.length > 0) {
|
|
745
|
+
sections.push("- **Output schema:**");
|
|
746
|
+
for (const field of agent.outputSchema) {
|
|
747
|
+
sections.push(` ${formatSchemaField(field)}`);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
// Outputs
|
|
751
|
+
if (agent.outputs) {
|
|
752
|
+
sections.push("- **Outputs:**");
|
|
753
|
+
for (const [field, values] of Object.entries(agent.outputs)) {
|
|
754
|
+
sections.push(` - ${field}: ${values.join(" | ")}`);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
// Artifact lineage
|
|
758
|
+
if (agent.produces && agent.produces.length > 0) {
|
|
759
|
+
sections.push(`- **Produces:** ${agent.produces.join(", ")}`);
|
|
760
|
+
}
|
|
761
|
+
if (agent.consumes && agent.consumes.length > 0) {
|
|
762
|
+
sections.push(`- **Consumes:** ${agent.consumes.join(", ")}`);
|
|
763
|
+
}
|
|
764
|
+
// Behavior modifiers
|
|
765
|
+
if (agent.behavior) {
|
|
766
|
+
sections.push(`- **Behavior:** ${agent.behavior}`);
|
|
767
|
+
}
|
|
768
|
+
if (agent.skip) {
|
|
769
|
+
sections.push(`- **Skip when:** ${agent.skip}`);
|
|
770
|
+
}
|
|
771
|
+
if (agent.isolation) {
|
|
772
|
+
sections.push(`- **Isolation:** ${agent.isolation}`);
|
|
773
|
+
}
|
|
774
|
+
if (agent.background) {
|
|
775
|
+
sections.push(`- **Background:** true`);
|
|
776
|
+
}
|
|
777
|
+
if (agent.maxTurns != null) {
|
|
778
|
+
sections.push(`- **Max turns:** ${agent.maxTurns}`);
|
|
779
|
+
}
|
|
780
|
+
// Scale (#10 — include batchSize)
|
|
781
|
+
if (agent.scale) {
|
|
782
|
+
let scaleLine = `- **Scale:** ${agent.scale.mode} by ${agent.scale.by} (${agent.scale.min}-${agent.scale.max}`;
|
|
783
|
+
if (agent.scale.batchSize != null) {
|
|
784
|
+
scaleLine += `, batch ${agent.scale.batchSize}`;
|
|
785
|
+
}
|
|
786
|
+
scaleLine += `)`;
|
|
787
|
+
sections.push(scaleLine);
|
|
788
|
+
}
|
|
789
|
+
// #4 — per-agent MCP servers
|
|
790
|
+
if (agent.mcpServers && agent.mcpServers.length > 0) {
|
|
791
|
+
sections.push(`- **MCP servers:** ${agent.mcpServers.join(", ")}`);
|
|
792
|
+
}
|
|
793
|
+
// #5 — sandbox
|
|
794
|
+
if (agent.sandbox != null) {
|
|
795
|
+
sections.push(`- **Sandbox:** ${agent.sandbox}`);
|
|
796
|
+
}
|
|
797
|
+
// #5 — fallbackChain
|
|
798
|
+
if (agent.fallbackChain && agent.fallbackChain.length > 0) {
|
|
799
|
+
sections.push(`- **Fallback chain:** ${agent.fallbackChain.join(" -> ")}`);
|
|
800
|
+
}
|
|
801
|
+
// #5 — per-agent hooks
|
|
802
|
+
if (agent.hooks && agent.hooks.length > 0) {
|
|
803
|
+
const hookDesc = agent.hooks.map((h) => `${h.name} (on ${h.on})`).join(", ");
|
|
804
|
+
sections.push(`- **Hooks:** ${hookDesc}`);
|
|
805
|
+
}
|
|
806
|
+
// Codex-specific extensions
|
|
807
|
+
if (agent.extensions?.codex) {
|
|
808
|
+
sections.push("- **Codex config:**");
|
|
809
|
+
for (const [k, v] of Object.entries(agent.extensions.codex)) {
|
|
810
|
+
sections.push(` - ${k}: ${v}`);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
sections.push("");
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
// Human nodes section
|
|
817
|
+
const humans = ast.nodes.filter((n) => n.type === "human");
|
|
818
|
+
if (humans.length > 0) {
|
|
819
|
+
sections.push("## Human Checkpoints");
|
|
820
|
+
sections.push("");
|
|
821
|
+
for (const human of humans) {
|
|
822
|
+
sections.push(`### ${human.label || toTitle(human.id)}`);
|
|
823
|
+
sections.push("");
|
|
824
|
+
if (human.description) {
|
|
825
|
+
sections.push(human.description);
|
|
826
|
+
sections.push("");
|
|
827
|
+
}
|
|
828
|
+
sections.push(`- **Type:** Human pause point — requires manual input or approval`);
|
|
829
|
+
if (human.timeout) {
|
|
830
|
+
sections.push(`- **Timeout:** ${human.timeout}`);
|
|
831
|
+
}
|
|
832
|
+
if (human.onTimeout) {
|
|
833
|
+
sections.push(`- **On timeout:** ${human.onTimeout}`);
|
|
834
|
+
}
|
|
835
|
+
sections.push("");
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
// Group nodes section
|
|
839
|
+
const groups = ast.nodes.filter((n) => n.type === "group");
|
|
840
|
+
if (groups.length > 0) {
|
|
841
|
+
sections.push("## Group Chats");
|
|
842
|
+
sections.push("");
|
|
843
|
+
for (const group of groups) {
|
|
844
|
+
sections.push(`### ${group.label || toTitle(group.id)}`);
|
|
845
|
+
sections.push("");
|
|
846
|
+
if (group.description) {
|
|
847
|
+
sections.push(group.description);
|
|
848
|
+
sections.push("");
|
|
849
|
+
}
|
|
850
|
+
sections.push(`- **Members:** ${group.members.join(", ")}`);
|
|
851
|
+
if (group.speakerSelection) {
|
|
852
|
+
sections.push(`- **Speaker selection:** ${group.speakerSelection}`);
|
|
853
|
+
}
|
|
854
|
+
if (group.maxRounds != null) {
|
|
855
|
+
sections.push(`- **Max rounds:** ${group.maxRounds}`);
|
|
856
|
+
}
|
|
857
|
+
if (group.termination) {
|
|
858
|
+
sections.push(`- **Termination:** ${group.termination}`);
|
|
859
|
+
}
|
|
860
|
+
if (group.timeout) {
|
|
861
|
+
sections.push(`- **Timeout:** ${group.timeout}`);
|
|
862
|
+
}
|
|
863
|
+
sections.push("");
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
// Gates section
|
|
867
|
+
const gates = ast.nodes.filter((n) => n.type === "gate");
|
|
868
|
+
if (gates.length > 0) {
|
|
869
|
+
sections.push("## Gates");
|
|
870
|
+
sections.push("");
|
|
871
|
+
for (const gate of gates) {
|
|
872
|
+
sections.push(`### ${gate.label || toTitle(gate.id)}`);
|
|
873
|
+
sections.push("");
|
|
874
|
+
if (gate.after)
|
|
875
|
+
sections.push(`- **After:** ${gate.after}`);
|
|
876
|
+
if (gate.before)
|
|
877
|
+
sections.push(`- **Before:** ${gate.before}`);
|
|
878
|
+
if (gate.run)
|
|
879
|
+
sections.push(`- **Run:** ${gate.run}`);
|
|
880
|
+
if (gate.checks && gate.checks.length > 0) {
|
|
881
|
+
sections.push(`- **Checks:** ${gate.checks.join(", ")}`);
|
|
882
|
+
}
|
|
883
|
+
if (gate.onFail)
|
|
884
|
+
sections.push(`- **On fail:** ${gate.onFail}`);
|
|
885
|
+
if (gate.retry != null)
|
|
886
|
+
sections.push(`- **Retry:** ${gate.retry}`);
|
|
887
|
+
if (gate.timeout)
|
|
888
|
+
sections.push(`- **Timeout:** ${gate.timeout}`);
|
|
889
|
+
sections.push(`- **Enforcement:** script-level — run gate script manually between agents`);
|
|
890
|
+
sections.push("");
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
// #1 — Action nodes section
|
|
894
|
+
const actions = ast.nodes.filter((n) => n.type === "action");
|
|
895
|
+
if (actions.length > 0) {
|
|
896
|
+
sections.push("## Actions");
|
|
897
|
+
sections.push("");
|
|
898
|
+
for (const action of actions) {
|
|
899
|
+
sections.push(`### ${action.label || toTitle(action.id)}`);
|
|
900
|
+
sections.push("");
|
|
901
|
+
if (action.description) {
|
|
902
|
+
sections.push(action.description);
|
|
903
|
+
sections.push("");
|
|
904
|
+
}
|
|
905
|
+
if (action.kind)
|
|
906
|
+
sections.push(`- **Kind:** ${action.kind}`);
|
|
907
|
+
if (action.source)
|
|
908
|
+
sections.push(`- **Source:** ${action.source}`);
|
|
909
|
+
if (action.commands && action.commands.length > 0) {
|
|
910
|
+
sections.push(`- **Commands:** ${action.commands.join("; ")}`);
|
|
911
|
+
}
|
|
912
|
+
if (action.timeout)
|
|
913
|
+
sections.push(`- **Timeout:** ${action.timeout}`);
|
|
914
|
+
if (action.onFail)
|
|
915
|
+
sections.push(`- **On fail:** ${action.onFail}`);
|
|
916
|
+
if (action.join)
|
|
917
|
+
sections.push(`- **Join:** ${action.join}`);
|
|
918
|
+
sections.push("");
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
// Triggers section
|
|
922
|
+
if (ast.triggers.length > 0) {
|
|
923
|
+
sections.push("## Triggers");
|
|
924
|
+
sections.push("");
|
|
925
|
+
for (const trigger of ast.triggers) {
|
|
926
|
+
sections.push(`### ${trigger.name}`);
|
|
927
|
+
sections.push(`Pattern: \`${trigger.pattern}\``);
|
|
928
|
+
if (trigger.argument)
|
|
929
|
+
sections.push(`Argument: ${trigger.argument}`);
|
|
930
|
+
sections.push("");
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
// Memory paths section
|
|
934
|
+
if (Object.keys(ast.memory).length > 0) {
|
|
935
|
+
sections.push("## Memory");
|
|
936
|
+
sections.push("");
|
|
937
|
+
if (ast.memory.domains) {
|
|
938
|
+
sections.push(`- **Domains:** .codex/domains/`);
|
|
939
|
+
}
|
|
940
|
+
if (ast.memory.references) {
|
|
941
|
+
sections.push(`- **References:** .codex/references/`);
|
|
942
|
+
}
|
|
943
|
+
if (ast.memory.metrics) {
|
|
944
|
+
sections.push(`- **Metrics:** .codex/metrics.jsonl`);
|
|
945
|
+
}
|
|
946
|
+
if (ast.memory.workspace) {
|
|
947
|
+
sections.push(`- **Workspace:** .codex/runs/`);
|
|
948
|
+
}
|
|
949
|
+
sections.push("");
|
|
950
|
+
}
|
|
951
|
+
// Observability section
|
|
952
|
+
if (ast.observability) {
|
|
953
|
+
const o = ast.observability;
|
|
954
|
+
sections.push("## Observability");
|
|
955
|
+
sections.push("");
|
|
956
|
+
sections.push(`- **Enabled:** ${o.enabled}`);
|
|
957
|
+
sections.push(`- **Level:** ${o.level}`);
|
|
958
|
+
sections.push(`- **Exporter:** ${o.exporter}`);
|
|
959
|
+
if (o.endpoint)
|
|
960
|
+
sections.push(`- **Endpoint:** ${o.endpoint}`);
|
|
961
|
+
if (o.service)
|
|
962
|
+
sections.push(`- **Service:** ${o.service}`);
|
|
963
|
+
sections.push(`- **Sample rate:** ${o.sampleRate}`);
|
|
964
|
+
sections.push(`- **Capture:** prompts=${o.capture.prompts}, completions=${o.capture.completions}, toolArgs=${o.capture.toolArgs}, toolResults=${o.capture.toolResults}`);
|
|
965
|
+
sections.push(`- **Spans:** agents=${o.spans.agents}, tools=${o.spans.tools}, gates=${o.spans.gates}, memory=${o.spans.memory}`);
|
|
966
|
+
sections.push("");
|
|
967
|
+
}
|
|
968
|
+
// Checkpoint section
|
|
969
|
+
if (ast.checkpoint) {
|
|
970
|
+
const cp = ast.checkpoint;
|
|
971
|
+
sections.push("## Checkpoint");
|
|
972
|
+
sections.push("");
|
|
973
|
+
sections.push(`- **Backend:** ${cp.backend}`);
|
|
974
|
+
sections.push(`- **Strategy:** ${cp.strategy}`);
|
|
975
|
+
if (cp.connection)
|
|
976
|
+
sections.push(`- **Connection:** ${cp.connection}`);
|
|
977
|
+
if (cp.ttl)
|
|
978
|
+
sections.push(`- **TTL:** ${cp.ttl}`);
|
|
979
|
+
if (cp.replay) {
|
|
980
|
+
sections.push(`- **Replay:** enabled=${cp.replay.enabled}${cp.replay.maxHistory != null ? `, maxHistory=${cp.replay.maxHistory}` : ""}${cp.replay.branch != null ? `, branch=${cp.replay.branch}` : ""}`);
|
|
981
|
+
}
|
|
982
|
+
sections.push("");
|
|
983
|
+
}
|
|
984
|
+
// Artifacts section
|
|
985
|
+
if (ast.artifacts && ast.artifacts.length > 0) {
|
|
986
|
+
sections.push("## Artifacts");
|
|
987
|
+
sections.push("");
|
|
988
|
+
for (const art of ast.artifacts) {
|
|
989
|
+
const parts = [`type: ${art.type}`];
|
|
990
|
+
if (art.path)
|
|
991
|
+
parts.push(`path: ${art.path}`);
|
|
992
|
+
if (art.retention)
|
|
993
|
+
parts.push(`retention: ${art.retention}`);
|
|
994
|
+
if (art.dependsOn && art.dependsOn.length > 0) {
|
|
995
|
+
parts.push(`depends-on: ${art.dependsOn.join(", ")}`);
|
|
996
|
+
}
|
|
997
|
+
sections.push(`- **${art.id}**: ${parts.join(", ")}`);
|
|
998
|
+
}
|
|
999
|
+
sections.push("");
|
|
1000
|
+
}
|
|
1001
|
+
// #8 — Batch section
|
|
1002
|
+
if (ast.batch && Object.keys(ast.batch).length > 0) {
|
|
1003
|
+
sections.push("## Batch");
|
|
1004
|
+
sections.push("");
|
|
1005
|
+
for (const [key, val] of Object.entries(ast.batch)) {
|
|
1006
|
+
sections.push(`- **${key}:** ${JSON.stringify(val)}`);
|
|
1007
|
+
}
|
|
1008
|
+
sections.push("");
|
|
1009
|
+
}
|
|
1010
|
+
// #9 — Environments section
|
|
1011
|
+
if (ast.environments && Object.keys(ast.environments).length > 0) {
|
|
1012
|
+
sections.push("## Environments");
|
|
1013
|
+
sections.push("");
|
|
1014
|
+
for (const [envName, overrides] of Object.entries(ast.environments)) {
|
|
1015
|
+
sections.push(`### ${toTitle(envName)}`);
|
|
1016
|
+
sections.push("");
|
|
1017
|
+
for (const [key, val] of Object.entries(overrides)) {
|
|
1018
|
+
sections.push(`- **${key}:** ${JSON.stringify(val)}`);
|
|
1019
|
+
}
|
|
1020
|
+
sections.push("");
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
// Schemas section
|
|
1024
|
+
if (ast.schemas && ast.schemas.length > 0) {
|
|
1025
|
+
sections.push("## Schemas");
|
|
1026
|
+
sections.push("");
|
|
1027
|
+
for (const schema of ast.schemas) {
|
|
1028
|
+
sections.push(`### ${toTitle(schema.id)}`);
|
|
1029
|
+
sections.push("");
|
|
1030
|
+
for (const field of schema.fields) {
|
|
1031
|
+
sections.push(formatSchemaField(field));
|
|
1032
|
+
}
|
|
1033
|
+
sections.push("");
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
// Depth levels
|
|
1037
|
+
if (ast.depth && ast.depth.levels && ast.depth.levels.length > 0) {
|
|
1038
|
+
sections.push("## Depth Levels");
|
|
1039
|
+
sections.push("");
|
|
1040
|
+
if (ast.depth.factors.length > 0) {
|
|
1041
|
+
sections.push(`Factors: ${ast.depth.factors.join(", ")}`);
|
|
1042
|
+
}
|
|
1043
|
+
for (const level of ast.depth.levels) {
|
|
1044
|
+
let line = `- **Level ${level.level}**: ${level.label}`;
|
|
1045
|
+
if (level.omit.length > 0)
|
|
1046
|
+
line += ` (omit: ${level.omit.join(", ")})`;
|
|
1047
|
+
sections.push(line);
|
|
1048
|
+
}
|
|
1049
|
+
sections.push("");
|
|
1050
|
+
}
|
|
1051
|
+
// Composition: params
|
|
1052
|
+
if (ast.params && ast.params.length > 0) {
|
|
1053
|
+
sections.push("## Parameters");
|
|
1054
|
+
sections.push("");
|
|
1055
|
+
for (const p of ast.params) {
|
|
1056
|
+
const req = p.required ? " (required)" : "";
|
|
1057
|
+
const def = p.default != null ? ` = ${p.default}` : "";
|
|
1058
|
+
sections.push(`- **${p.name}**: ${p.type}${req}${def}`);
|
|
1059
|
+
}
|
|
1060
|
+
sections.push("");
|
|
1061
|
+
}
|
|
1062
|
+
// Composition: interface endpoints
|
|
1063
|
+
if (ast.interfaceEndpoints) {
|
|
1064
|
+
sections.push("## Interface");
|
|
1065
|
+
sections.push("");
|
|
1066
|
+
sections.push(`- Entry: ${ast.interfaceEndpoints.entry}`);
|
|
1067
|
+
sections.push(`- Exit: ${ast.interfaceEndpoints.exit}`);
|
|
1068
|
+
sections.push("");
|
|
1069
|
+
}
|
|
1070
|
+
// Composition: imports
|
|
1071
|
+
if (ast.imports && ast.imports.length > 0) {
|
|
1072
|
+
sections.push("## Imports");
|
|
1073
|
+
sections.push("");
|
|
1074
|
+
for (const imp of ast.imports) {
|
|
1075
|
+
let line = `- **${imp.alias}** from \`${imp.source}\``;
|
|
1076
|
+
if (imp.sha256)
|
|
1077
|
+
line += ` (sha256: ${imp.sha256})`;
|
|
1078
|
+
if (imp.registry)
|
|
1079
|
+
line += ` [registry: ${imp.registryPackage}@${imp.registryVersion}]`;
|
|
1080
|
+
sections.push(line);
|
|
1081
|
+
if (Object.keys(imp.params).length > 0) {
|
|
1082
|
+
for (const [k, v] of Object.entries(imp.params)) {
|
|
1083
|
+
sections.push(` - ${k}: ${v}`);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
sections.push("");
|
|
1088
|
+
}
|
|
1089
|
+
// Composition: includes
|
|
1090
|
+
if (ast.includes && ast.includes.length > 0) {
|
|
1091
|
+
sections.push("## Includes");
|
|
1092
|
+
sections.push("");
|
|
1093
|
+
for (const inc of ast.includes) {
|
|
1094
|
+
sections.push(`- ${inc.source}`);
|
|
1095
|
+
}
|
|
1096
|
+
sections.push("");
|
|
1097
|
+
}
|
|
1098
|
+
// Skills summary (#11 — additional skill fields)
|
|
1099
|
+
if (ast.skills.length > 0) {
|
|
1100
|
+
sections.push("## Skills");
|
|
1101
|
+
sections.push("");
|
|
1102
|
+
for (const skill of ast.skills) {
|
|
1103
|
+
sections.push(`### ${toTitle(skill.id)}`);
|
|
1104
|
+
if (skill.description) {
|
|
1105
|
+
sections.push(skill.description);
|
|
1106
|
+
}
|
|
1107
|
+
if (skill.scripts && skill.scripts.length > 0) {
|
|
1108
|
+
sections.push(`Scripts: ${skill.scripts.join(", ")}`);
|
|
1109
|
+
}
|
|
1110
|
+
if (skill.prompt) {
|
|
1111
|
+
sections.push(`Prompt file: ${skill.prompt}`);
|
|
1112
|
+
}
|
|
1113
|
+
if (skill.userInvocable != null) {
|
|
1114
|
+
sections.push(`User-invocable: ${skill.userInvocable ? "yes" : "no"}`);
|
|
1115
|
+
}
|
|
1116
|
+
if (skill.allowedTools && skill.allowedTools.length > 0) {
|
|
1117
|
+
sections.push(`Allowed tools: ${skill.allowedTools.join(", ")}`);
|
|
1118
|
+
}
|
|
1119
|
+
if (skill.context) {
|
|
1120
|
+
sections.push(`Context: ${skill.context}`);
|
|
1121
|
+
}
|
|
1122
|
+
if (skill.agent) {
|
|
1123
|
+
sections.push(`Agent: ${skill.agent}`);
|
|
1124
|
+
}
|
|
1125
|
+
if (skill.disableModelInvocation) {
|
|
1126
|
+
sections.push(`Model invocation: disabled`);
|
|
1127
|
+
}
|
|
1128
|
+
sections.push("");
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
// Context includes (#16 — also show context.file)
|
|
1132
|
+
if ((ast.context.includes && ast.context.includes.length > 0) || ast.context.file) {
|
|
1133
|
+
sections.push("## References");
|
|
1134
|
+
sections.push("");
|
|
1135
|
+
if (ast.context.file) {
|
|
1136
|
+
sections.push(`- Context file: ${ast.context.file}`);
|
|
1137
|
+
}
|
|
1138
|
+
if (ast.context.includes) {
|
|
1139
|
+
for (const inc of ast.context.includes) {
|
|
1140
|
+
sections.push(`- ${inc}`);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
sections.push("");
|
|
1144
|
+
}
|
|
1145
|
+
// Environment variables (#2 — handle sensitive, secretRef, encrypted)
|
|
1146
|
+
if (Object.keys(ast.env).length > 0) {
|
|
1147
|
+
sections.push("## Environment");
|
|
1148
|
+
sections.push("");
|
|
1149
|
+
for (const [key, val] of Object.entries(ast.env)) {
|
|
1150
|
+
if (typeof val === "string") {
|
|
1151
|
+
sections.push(`- \`${key}\`: ${val}`);
|
|
1152
|
+
}
|
|
1153
|
+
else {
|
|
1154
|
+
const sv = val;
|
|
1155
|
+
let displayVal;
|
|
1156
|
+
if (sv.secretRef) {
|
|
1157
|
+
displayVal = `[secret: ${sv.secretRef.scheme}:${sv.secretRef.uri}]`;
|
|
1158
|
+
}
|
|
1159
|
+
else if (sv.encrypted) {
|
|
1160
|
+
displayVal = `[encrypted]`;
|
|
1161
|
+
}
|
|
1162
|
+
else if (sv.sensitive) {
|
|
1163
|
+
displayVal = `\${${key}}`;
|
|
1164
|
+
}
|
|
1165
|
+
else {
|
|
1166
|
+
displayVal = sv.value;
|
|
1167
|
+
}
|
|
1168
|
+
sections.push(`- \`${key}\`: ${displayVal}`);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
sections.push("");
|
|
1172
|
+
}
|
|
1173
|
+
return {
|
|
1174
|
+
path: "AGENTS.md",
|
|
1175
|
+
content: sections.join("\n") + "\n",
|
|
1176
|
+
category: "agent",
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
// ---------------------------------------------------------------------------
|
|
1180
|
+
// .codex/instructions.md — REMOVED
|
|
1181
|
+
// ---------------------------------------------------------------------------
|
|
1182
|
+
// Codex CLI does NOT use .codex/instructions.md. It reads AGENTS.md files
|
|
1183
|
+
// from the workspace root and nested directories. Skills, context includes,
|
|
1184
|
+
// and environment info are now folded into AGENTS.md generation above.
|
|
1185
|
+
// ---------------------------------------------------------------------------
|
|
1186
|
+
// Memory directories
|
|
1187
|
+
// ---------------------------------------------------------------------------
|
|
1188
|
+
/** Generate memory structure directories under .codex/. */
|
|
1189
|
+
function generateMemory(ast) {
|
|
1190
|
+
const files = [];
|
|
1191
|
+
if (ast.memory.domains) {
|
|
1192
|
+
files.push(gitkeep(".codex/domains"));
|
|
1193
|
+
}
|
|
1194
|
+
if (ast.memory.references) {
|
|
1195
|
+
files.push(gitkeep(".codex/references"));
|
|
1196
|
+
}
|
|
1197
|
+
if (ast.memory.metrics) {
|
|
1198
|
+
files.push({
|
|
1199
|
+
path: ".codex/metrics.jsonl",
|
|
1200
|
+
content: "",
|
|
1201
|
+
category: "machine",
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
if (ast.memory.workspace) {
|
|
1205
|
+
files.push(gitkeep(".codex/runs"));
|
|
1206
|
+
}
|
|
1207
|
+
return files;
|
|
1208
|
+
}
|
|
1209
|
+
// ---------------------------------------------------------------------------
|
|
1210
|
+
// Gate scripts
|
|
1211
|
+
// ---------------------------------------------------------------------------
|
|
1212
|
+
/** Generate gate script stubs under .codex/scripts/. */
|
|
1213
|
+
function generateGateScripts(ast) {
|
|
1214
|
+
const files = [];
|
|
1215
|
+
for (const node of ast.nodes) {
|
|
1216
|
+
if (node.type !== "gate")
|
|
1217
|
+
continue;
|
|
1218
|
+
const gate = node;
|
|
1219
|
+
if (gate.run) {
|
|
1220
|
+
const scriptName = gate.run.replace(/^.*\//, "").replace(/\s.*$/, "");
|
|
1221
|
+
files.push({
|
|
1222
|
+
path: `.codex/scripts/${scriptName}`,
|
|
1223
|
+
content: shellStub(`Gate: ${gate.id} — ${gate.run}`),
|
|
1224
|
+
category: "script",
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return files;
|
|
1229
|
+
}
|
|
1230
|
+
// ---------------------------------------------------------------------------
|
|
1231
|
+
// Hook scripts
|
|
1232
|
+
// ---------------------------------------------------------------------------
|
|
1233
|
+
/** Generate hook script stubs under .codex/scripts/. */
|
|
1234
|
+
function generateHookScripts(ast) {
|
|
1235
|
+
const files = [];
|
|
1236
|
+
// Global hooks
|
|
1237
|
+
for (const hook of ast.hooks) {
|
|
1238
|
+
if (!hook.run)
|
|
1239
|
+
continue;
|
|
1240
|
+
const scriptName = hook.run.replace(/^.*\//, "").replace(/\s.*$/, "");
|
|
1241
|
+
const hookLines = [
|
|
1242
|
+
"#!/usr/bin/env bash",
|
|
1243
|
+
`# Hook: ${hook.name} — fires on ${hook.on}`,
|
|
1244
|
+
];
|
|
1245
|
+
// #12 — include matcher, type, timeout as comments
|
|
1246
|
+
if (hook.matcher)
|
|
1247
|
+
hookLines.push(`# Matcher: ${hook.matcher}`);
|
|
1248
|
+
if (hook.type)
|
|
1249
|
+
hookLines.push(`# Type: ${hook.type}`);
|
|
1250
|
+
if (hook.timeout != null)
|
|
1251
|
+
hookLines.push(`# Timeout: ${hook.timeout}ms`);
|
|
1252
|
+
hookLines.push("# Auto-generated by agentopology scaffold — edit as needed.", "set -euo pipefail", "", `echo "TODO: implement ${hook.name} hook"`, "");
|
|
1253
|
+
files.push({
|
|
1254
|
+
path: `.codex/scripts/${scriptName}`,
|
|
1255
|
+
content: hookLines.join("\n"),
|
|
1256
|
+
category: "script",
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
// #5 — per-agent hook script stubs
|
|
1260
|
+
for (const node of ast.nodes) {
|
|
1261
|
+
if (node.type !== "agent")
|
|
1262
|
+
continue;
|
|
1263
|
+
const agent = node;
|
|
1264
|
+
if (!agent.hooks)
|
|
1265
|
+
continue;
|
|
1266
|
+
for (const hook of agent.hooks) {
|
|
1267
|
+
if (!hook.run)
|
|
1268
|
+
continue;
|
|
1269
|
+
const scriptName = hook.run.replace(/^.*\//, "").replace(/\s.*$/, "");
|
|
1270
|
+
const hookLines = [
|
|
1271
|
+
"#!/usr/bin/env bash",
|
|
1272
|
+
`# Hook: ${hook.name} — fires on ${hook.on} (agent: ${agent.id})`,
|
|
1273
|
+
];
|
|
1274
|
+
if (hook.matcher)
|
|
1275
|
+
hookLines.push(`# Matcher: ${hook.matcher}`);
|
|
1276
|
+
if (hook.type)
|
|
1277
|
+
hookLines.push(`# Type: ${hook.type}`);
|
|
1278
|
+
if (hook.timeout != null)
|
|
1279
|
+
hookLines.push(`# Timeout: ${hook.timeout}ms`);
|
|
1280
|
+
hookLines.push("# Auto-generated by agentopology scaffold — edit as needed.", "set -euo pipefail", "", `echo "TODO: implement ${hook.name} hook"`, "");
|
|
1281
|
+
files.push({
|
|
1282
|
+
path: `.codex/scripts/${scriptName}`,
|
|
1283
|
+
content: hookLines.join("\n"),
|
|
1284
|
+
category: "script",
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
return files;
|
|
1289
|
+
}
|
|
1290
|
+
// ---------------------------------------------------------------------------
|
|
1291
|
+
// Tool scripts
|
|
1292
|
+
// ---------------------------------------------------------------------------
|
|
1293
|
+
/** Generate tool script stubs under .codex/scripts/. */
|
|
1294
|
+
function generateToolScripts(ast) {
|
|
1295
|
+
const files = [];
|
|
1296
|
+
for (const tool of ast.toolDefs) {
|
|
1297
|
+
const ext = tool.lang === "python" ? "py" : tool.lang === "node" ? "js" : "sh";
|
|
1298
|
+
const shebang = tool.lang === "python"
|
|
1299
|
+
? "#!/usr/bin/env python3"
|
|
1300
|
+
: tool.lang === "node"
|
|
1301
|
+
? "#!/usr/bin/env node"
|
|
1302
|
+
: "#!/usr/bin/env bash";
|
|
1303
|
+
const lines = [
|
|
1304
|
+
shebang,
|
|
1305
|
+
`# Tool: ${tool.id} — ${tool.description}`,
|
|
1306
|
+
"# Auto-generated by agentopology scaffold — edit as needed.",
|
|
1307
|
+
"",
|
|
1308
|
+
];
|
|
1309
|
+
if (tool.lang === "bash" || !tool.lang) {
|
|
1310
|
+
lines.push("set -euo pipefail");
|
|
1311
|
+
lines.push("");
|
|
1312
|
+
}
|
|
1313
|
+
if (tool.args && tool.args.length > 0) {
|
|
1314
|
+
lines.push(`# Args: ${tool.args.join(", ")}`);
|
|
1315
|
+
lines.push("");
|
|
1316
|
+
}
|
|
1317
|
+
if (tool.lang === "python") {
|
|
1318
|
+
lines.push(`print("TODO: implement ${tool.id}")`);
|
|
1319
|
+
}
|
|
1320
|
+
else if (tool.lang === "node") {
|
|
1321
|
+
lines.push(`console.log("TODO: implement ${tool.id}");`);
|
|
1322
|
+
}
|
|
1323
|
+
else {
|
|
1324
|
+
lines.push(`echo "TODO: implement ${tool.id}"`);
|
|
1325
|
+
}
|
|
1326
|
+
lines.push("");
|
|
1327
|
+
files.push({
|
|
1328
|
+
path: `.codex/scripts/${tool.id}.${ext}`,
|
|
1329
|
+
content: lines.join("\n"),
|
|
1330
|
+
category: "script",
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
return files;
|
|
1334
|
+
}
|
|
1335
|
+
// ---------------------------------------------------------------------------
|
|
1336
|
+
// Metering script
|
|
1337
|
+
// ---------------------------------------------------------------------------
|
|
1338
|
+
/** Generate metering script stub. */
|
|
1339
|
+
function generateMetering(ast) {
|
|
1340
|
+
if (!ast.metering)
|
|
1341
|
+
return null;
|
|
1342
|
+
const m = ast.metering;
|
|
1343
|
+
const lines = [
|
|
1344
|
+
"#!/usr/bin/env bash",
|
|
1345
|
+
"# Metering / cost tracking script",
|
|
1346
|
+
"# Auto-generated by agentopology scaffold — edit as needed.",
|
|
1347
|
+
"set -euo pipefail",
|
|
1348
|
+
"",
|
|
1349
|
+
`# Track: ${m.track.join(", ")}`,
|
|
1350
|
+
`# Per: ${m.per.join(", ")}`,
|
|
1351
|
+
`# Output: ${m.output} (${m.format})`,
|
|
1352
|
+
`# Pricing: ${m.pricing}`,
|
|
1353
|
+
"",
|
|
1354
|
+
'echo "TODO: implement metrics collection"',
|
|
1355
|
+
"",
|
|
1356
|
+
];
|
|
1357
|
+
return {
|
|
1358
|
+
path: "scripts/collect-metrics.sh",
|
|
1359
|
+
content: lines.join("\n"),
|
|
1360
|
+
category: "script",
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
// ---------------------------------------------------------------------------
|
|
1364
|
+
// Workspace protocol
|
|
1365
|
+
// ---------------------------------------------------------------------------
|
|
1366
|
+
/** Generate workspace-protocol.md if workspace memory has a protocol field. */
|
|
1367
|
+
function generateWorkspaceProtocol(ast) {
|
|
1368
|
+
const workspace = ast.memory.workspace;
|
|
1369
|
+
if (!workspace || !workspace.protocol)
|
|
1370
|
+
return null;
|
|
1371
|
+
const protocol = workspace.protocol;
|
|
1372
|
+
const sections = [];
|
|
1373
|
+
sections.push("# Workspace Protocol");
|
|
1374
|
+
sections.push("");
|
|
1375
|
+
// Directory structure from workspace.structure
|
|
1376
|
+
const structure = workspace.structure;
|
|
1377
|
+
if (structure && Array.isArray(structure)) {
|
|
1378
|
+
sections.push("## Directory Structure");
|
|
1379
|
+
for (const item of structure) {
|
|
1380
|
+
sections.push(`- ${item}`);
|
|
1381
|
+
}
|
|
1382
|
+
sections.push("");
|
|
1383
|
+
}
|
|
1384
|
+
// Read/Write rules from agent nodes
|
|
1385
|
+
const agents = ast.nodes.filter((n) => n.type === "agent");
|
|
1386
|
+
if (agents.length > 0) {
|
|
1387
|
+
sections.push("## Read/Write Rules");
|
|
1388
|
+
for (const agent of agents) {
|
|
1389
|
+
const reads = agent.reads ?? [];
|
|
1390
|
+
const writes = agent.writes ?? [];
|
|
1391
|
+
if (reads.length > 0 || writes.length > 0) {
|
|
1392
|
+
sections.push(`### ${toTitle(agent.id)}`);
|
|
1393
|
+
if (reads.length > 0) {
|
|
1394
|
+
sections.push(`- Reads: ${reads.join(", ")}`);
|
|
1395
|
+
}
|
|
1396
|
+
if (writes.length > 0) {
|
|
1397
|
+
sections.push(`- Writes: ${writes.join(", ")}`);
|
|
1398
|
+
}
|
|
1399
|
+
sections.push("");
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
return {
|
|
1404
|
+
path: `.codex/${protocol}`,
|
|
1405
|
+
content: sections.join("\n") + "\n",
|
|
1406
|
+
category: "machine",
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
// ---------------------------------------------------------------------------
|
|
1410
|
+
// Skill documentation
|
|
1411
|
+
// ---------------------------------------------------------------------------
|
|
1412
|
+
/** Generate skill documentation files under .codex/skills/. */
|
|
1413
|
+
function generateSkills(ast) {
|
|
1414
|
+
const files = [];
|
|
1415
|
+
for (const skill of ast.skills) {
|
|
1416
|
+
const sections = [];
|
|
1417
|
+
sections.push(`# ${toTitle(skill.id)} Skill`);
|
|
1418
|
+
sections.push("");
|
|
1419
|
+
if (skill.description) {
|
|
1420
|
+
sections.push(skill.description);
|
|
1421
|
+
sections.push("");
|
|
1422
|
+
}
|
|
1423
|
+
if (skill.scripts && skill.scripts.length > 0) {
|
|
1424
|
+
sections.push("## Scripts");
|
|
1425
|
+
for (const s of skill.scripts) {
|
|
1426
|
+
sections.push(`- ${s}`);
|
|
1427
|
+
}
|
|
1428
|
+
sections.push("");
|
|
1429
|
+
}
|
|
1430
|
+
if (skill.domains && skill.domains.length > 0) {
|
|
1431
|
+
sections.push("## Domains");
|
|
1432
|
+
for (const d of skill.domains) {
|
|
1433
|
+
sections.push(`- ${d}`);
|
|
1434
|
+
}
|
|
1435
|
+
sections.push("");
|
|
1436
|
+
}
|
|
1437
|
+
if (skill.references && skill.references.length > 0) {
|
|
1438
|
+
sections.push("## References");
|
|
1439
|
+
for (const r of skill.references) {
|
|
1440
|
+
sections.push(`- ${r}`);
|
|
1441
|
+
}
|
|
1442
|
+
sections.push("");
|
|
1443
|
+
}
|
|
1444
|
+
// #11 — additional skill fields in skill docs
|
|
1445
|
+
if (skill.prompt) {
|
|
1446
|
+
sections.push(`Prompt file: ${skill.prompt}`);
|
|
1447
|
+
sections.push("");
|
|
1448
|
+
}
|
|
1449
|
+
if (skill.userInvocable != null) {
|
|
1450
|
+
sections.push(`User-invocable: ${skill.userInvocable ? "yes" : "no"}`);
|
|
1451
|
+
sections.push("");
|
|
1452
|
+
}
|
|
1453
|
+
if (skill.allowedTools && skill.allowedTools.length > 0) {
|
|
1454
|
+
sections.push(`Allowed tools: ${skill.allowedTools.join(", ")}`);
|
|
1455
|
+
sections.push("");
|
|
1456
|
+
}
|
|
1457
|
+
if (skill.context) {
|
|
1458
|
+
sections.push(`Context: ${skill.context}`);
|
|
1459
|
+
sections.push("");
|
|
1460
|
+
}
|
|
1461
|
+
if (skill.agent) {
|
|
1462
|
+
sections.push(`Agent: ${skill.agent}`);
|
|
1463
|
+
sections.push("");
|
|
1464
|
+
}
|
|
1465
|
+
if (skill.disableModelInvocation) {
|
|
1466
|
+
sections.push(`Model invocation: disabled`);
|
|
1467
|
+
sections.push("");
|
|
1468
|
+
}
|
|
1469
|
+
files.push({
|
|
1470
|
+
path: `.codex/skills/${skill.id}.md`,
|
|
1471
|
+
content: sections.join("\n") + "\n",
|
|
1472
|
+
category: "agent",
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
return files;
|
|
1476
|
+
}
|
|
1477
|
+
// ---------------------------------------------------------------------------
|
|
1478
|
+
// Per-agent TOML files — .codex/agents/<name>.toml
|
|
1479
|
+
// ---------------------------------------------------------------------------
|
|
1480
|
+
/**
|
|
1481
|
+
* Build developer_instructions for an agent that has no explicit prompt.
|
|
1482
|
+
* Constructs instructions from role description, tools, reads/writes, and outputs.
|
|
1483
|
+
*/
|
|
1484
|
+
function buildDeveloperInstructions(agent, ast) {
|
|
1485
|
+
const parts = [];
|
|
1486
|
+
// Role description
|
|
1487
|
+
const roleText = ast.roles[agent.role ?? ""] ?? ast.roles[agent.id] ?? agent.role;
|
|
1488
|
+
if (roleText) {
|
|
1489
|
+
parts.push(`You are a ${toTitle(agent.id)} agent. ${roleText}`);
|
|
1490
|
+
}
|
|
1491
|
+
else {
|
|
1492
|
+
parts.push(`You are the ${toTitle(agent.id)} agent.`);
|
|
1493
|
+
}
|
|
1494
|
+
// Tools
|
|
1495
|
+
if (agent.tools && agent.tools.length > 0) {
|
|
1496
|
+
parts.push(`\nAvailable tools: ${agent.tools.join(", ")}`);
|
|
1497
|
+
}
|
|
1498
|
+
if (agent.disallowedTools && agent.disallowedTools.length > 0) {
|
|
1499
|
+
parts.push(`Disallowed tools: ${agent.disallowedTools.join(", ")}`);
|
|
1500
|
+
}
|
|
1501
|
+
// Memory access
|
|
1502
|
+
if (agent.reads && agent.reads.length > 0) {
|
|
1503
|
+
parts.push(`\nReads from: ${agent.reads.join(", ")}`);
|
|
1504
|
+
}
|
|
1505
|
+
if (agent.writes && agent.writes.length > 0) {
|
|
1506
|
+
parts.push(`Writes to: ${agent.writes.join(", ")}`);
|
|
1507
|
+
}
|
|
1508
|
+
// Outputs
|
|
1509
|
+
if (agent.outputs) {
|
|
1510
|
+
const outputLines = [];
|
|
1511
|
+
for (const [field, values] of Object.entries(agent.outputs)) {
|
|
1512
|
+
outputLines.push(` ${field}: ${values.join(" | ")}`);
|
|
1513
|
+
}
|
|
1514
|
+
parts.push(`\nOutputs:\n${outputLines.join("\n")}`);
|
|
1515
|
+
}
|
|
1516
|
+
return parts.join("\n");
|
|
1517
|
+
}
|
|
1518
|
+
/**
|
|
1519
|
+
* Generate per-agent TOML files under `.codex/agents/<name>.toml`.
|
|
1520
|
+
*
|
|
1521
|
+
* Each agent in the topology gets its own TOML configuration file with
|
|
1522
|
+
* name, description, developer_instructions, model, sandbox_mode,
|
|
1523
|
+
* model_reasoning_effort, nickname_candidates, and optional MCP servers.
|
|
1524
|
+
*/
|
|
1525
|
+
function generateAgentTomlFiles(ast) {
|
|
1526
|
+
const files = [];
|
|
1527
|
+
const agents = ast.nodes.filter((n) => n.type === "agent");
|
|
1528
|
+
for (const agent of agents) {
|
|
1529
|
+
const lines = [];
|
|
1530
|
+
// name
|
|
1531
|
+
lines.push(`name = ${tomlString(agent.id)}`);
|
|
1532
|
+
// description
|
|
1533
|
+
const desc = agent.description
|
|
1534
|
+
?? ast.roles[agent.role ?? ""]
|
|
1535
|
+
?? ast.roles[agent.id]
|
|
1536
|
+
?? agent.role
|
|
1537
|
+
?? toTitle(agent.id);
|
|
1538
|
+
lines.push(`description = ${tomlString(desc)}`);
|
|
1539
|
+
// developer_instructions — use prompt if available, otherwise construct
|
|
1540
|
+
const instructions = agent.prompt ?? buildDeveloperInstructions(agent, ast);
|
|
1541
|
+
lines.push(`developer_instructions = """\n${instructions}\n"""`);
|
|
1542
|
+
// model
|
|
1543
|
+
if (agent.model) {
|
|
1544
|
+
lines.push(`model = ${tomlString(agent.model)}`);
|
|
1545
|
+
}
|
|
1546
|
+
// sandbox_mode
|
|
1547
|
+
if (agent.sandbox != null) {
|
|
1548
|
+
lines.push(`sandbox_mode = ${tomlString(mapAgentSandbox(agent.sandbox))}`);
|
|
1549
|
+
}
|
|
1550
|
+
// model_reasoning_effort
|
|
1551
|
+
if (agent.thinking) {
|
|
1552
|
+
const effort = mapReasoningEffort(agent.thinking);
|
|
1553
|
+
if (effort) {
|
|
1554
|
+
lines.push(`model_reasoning_effort = ${tomlString(effort)}`);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
// nickname_candidates — derive from agent id
|
|
1558
|
+
const nickname = agent.id
|
|
1559
|
+
.split(/[-_]/)
|
|
1560
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
1561
|
+
.join("");
|
|
1562
|
+
lines.push(`nickname_candidates = [${tomlString(nickname)}]`);
|
|
1563
|
+
// MCP servers — include sections for agent-specific MCP servers
|
|
1564
|
+
if (agent.mcpServers && agent.mcpServers.length > 0 && ast.mcpServers) {
|
|
1565
|
+
lines.push("");
|
|
1566
|
+
for (const serverName of agent.mcpServers) {
|
|
1567
|
+
const config = ast.mcpServers[serverName];
|
|
1568
|
+
if (!config)
|
|
1569
|
+
continue;
|
|
1570
|
+
lines.push(`[mcp_servers.${serverName}]`);
|
|
1571
|
+
for (const [ck, cv] of Object.entries(config)) {
|
|
1572
|
+
if (typeof cv === "string") {
|
|
1573
|
+
lines.push(`${ck} = ${tomlString(cv)}`);
|
|
1574
|
+
}
|
|
1575
|
+
else if (typeof cv === "boolean") {
|
|
1576
|
+
lines.push(`${ck} = ${cv}`);
|
|
1577
|
+
}
|
|
1578
|
+
else if (typeof cv === "number") {
|
|
1579
|
+
lines.push(`${ck} = ${cv}`);
|
|
1580
|
+
}
|
|
1581
|
+
else if (Array.isArray(cv)) {
|
|
1582
|
+
lines.push(`${ck} = ${tomlStringArray(cv.map(String))}`);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
lines.push("");
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
files.push({
|
|
1589
|
+
path: `.codex/agents/${agent.id}.toml`,
|
|
1590
|
+
content: lines.join("\n") + "\n",
|
|
1591
|
+
category: "agent",
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
return files;
|
|
1595
|
+
}
|
|
1596
|
+
// ---------------------------------------------------------------------------
|
|
1597
|
+
// Binding export
|
|
1598
|
+
// ---------------------------------------------------------------------------
|
|
1599
|
+
/** OpenAI Codex CLI binding — full implementation. */
|
|
1600
|
+
export const codexBinding = {
|
|
1601
|
+
name: "codex",
|
|
1602
|
+
description: "OpenAI Codex CLI — generates .codex/config.toml, AGENTS.md, .codex/ directory structure, and scripts.",
|
|
1603
|
+
scaffold(ast) {
|
|
1604
|
+
const files = [];
|
|
1605
|
+
// 1. .codex/config.toml — main configuration
|
|
1606
|
+
files.push(generateCodexToml(ast));
|
|
1607
|
+
// 2. AGENTS.md — agent instructions with roles, flow, memory paths
|
|
1608
|
+
// (Codex reads AGENTS.md from workspace root; no separate instructions.md)
|
|
1609
|
+
files.push(generateAgentsMd(ast));
|
|
1610
|
+
// 3. Per-agent TOML files under .codex/agents/
|
|
1611
|
+
files.push(...generateAgentTomlFiles(ast));
|
|
1612
|
+
// 4. Memory directories
|
|
1613
|
+
files.push(...generateMemory(ast));
|
|
1614
|
+
// 5. Skill documentation
|
|
1615
|
+
files.push(...generateSkills(ast));
|
|
1616
|
+
// 6. Gate scripts
|
|
1617
|
+
files.push(...generateGateScripts(ast));
|
|
1618
|
+
// 7. Hook scripts
|
|
1619
|
+
files.push(...generateHookScripts(ast));
|
|
1620
|
+
// 8. Tool scripts
|
|
1621
|
+
files.push(...generateToolScripts(ast));
|
|
1622
|
+
// 9. Workspace protocol
|
|
1623
|
+
const wsProtocol = generateWorkspaceProtocol(ast);
|
|
1624
|
+
if (wsProtocol)
|
|
1625
|
+
files.push(wsProtocol);
|
|
1626
|
+
// 10. Metering
|
|
1627
|
+
const meteringFile = generateMetering(ast);
|
|
1628
|
+
if (meteringFile)
|
|
1629
|
+
files.push(meteringFile);
|
|
1630
|
+
return deduplicateFiles(files);
|
|
1631
|
+
},
|
|
1632
|
+
};
|
|
1633
|
+
//# sourceMappingURL=codex.js.map
|