@cleocode/adapters 2026.4.47 → 2026.4.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +19562 -372
  4. package/dist/index.js.map +4 -4
  5. package/dist/providers/claude-code/adapter.d.ts +12 -6
  6. package/dist/providers/claude-code/adapter.d.ts.map +1 -1
  7. package/dist/providers/claude-sdk/index.d.ts +18 -0
  8. package/dist/providers/claude-sdk/index.d.ts.map +1 -0
  9. package/dist/providers/claude-sdk/mcp-registry.d.ts +40 -0
  10. package/dist/providers/claude-sdk/mcp-registry.d.ts.map +1 -0
  11. package/dist/providers/claude-sdk/session-store.d.ts +78 -0
  12. package/dist/providers/claude-sdk/session-store.d.ts.map +1 -0
  13. package/dist/providers/claude-sdk/spawn.d.ts +79 -0
  14. package/dist/providers/claude-sdk/spawn.d.ts.map +1 -0
  15. package/dist/providers/claude-sdk/tool-bridge.d.ts +38 -0
  16. package/dist/providers/claude-sdk/tool-bridge.d.ts.map +1 -0
  17. package/dist/providers/openai-sdk/adapter.d.ts +77 -0
  18. package/dist/providers/openai-sdk/adapter.d.ts.map +1 -0
  19. package/dist/providers/openai-sdk/guardrails.d.ts +67 -0
  20. package/dist/providers/openai-sdk/guardrails.d.ts.map +1 -0
  21. package/dist/providers/openai-sdk/handoff.d.ts +94 -0
  22. package/dist/providers/openai-sdk/handoff.d.ts.map +1 -0
  23. package/dist/providers/openai-sdk/index.d.ts +39 -0
  24. package/dist/providers/openai-sdk/index.d.ts.map +1 -0
  25. package/dist/providers/openai-sdk/install.d.ts +61 -0
  26. package/dist/providers/openai-sdk/install.d.ts.map +1 -0
  27. package/dist/providers/openai-sdk/spawn.d.ts +146 -0
  28. package/dist/providers/openai-sdk/spawn.d.ts.map +1 -0
  29. package/dist/providers/openai-sdk/tracing.d.ts +89 -0
  30. package/dist/providers/openai-sdk/tracing.d.ts.map +1 -0
  31. package/dist/providers/shared/conduit-trace-writer.d.ts +72 -0
  32. package/dist/providers/shared/conduit-trace-writer.d.ts.map +1 -0
  33. package/dist/providers/shared/sdk-result-mapper.d.ts +51 -0
  34. package/dist/providers/shared/sdk-result-mapper.d.ts.map +1 -0
  35. package/package.json +5 -3
  36. package/src/index.ts +8 -0
  37. package/src/providers/claude-code/adapter.ts +41 -4
  38. package/src/providers/claude-sdk/__tests__/spawn.test.ts +448 -0
  39. package/src/providers/claude-sdk/index.ts +18 -0
  40. package/src/providers/claude-sdk/mcp-registry.ts +96 -0
  41. package/src/providers/claude-sdk/session-store.ts +103 -0
  42. package/src/providers/claude-sdk/spawn.ts +242 -0
  43. package/src/providers/claude-sdk/tool-bridge.ts +51 -0
  44. package/src/providers/openai-sdk/__tests__/openai-sdk-spawn.test.ts +716 -0
  45. package/src/providers/openai-sdk/adapter.ts +138 -0
  46. package/src/providers/openai-sdk/guardrails.ts +158 -0
  47. package/src/providers/openai-sdk/handoff.ts +187 -0
  48. package/src/providers/openai-sdk/index.ts +55 -0
  49. package/src/providers/openai-sdk/install.ts +135 -0
  50. package/src/providers/openai-sdk/manifest.json +45 -0
  51. package/src/providers/openai-sdk/spawn.ts +300 -0
  52. package/src/providers/openai-sdk/tracing.ts +175 -0
  53. package/src/providers/shared/conduit-trace-writer.ts +101 -0
  54. package/src/providers/shared/sdk-result-mapper.ts +83 -0
@@ -0,0 +1,138 @@
1
+ /**
2
+ * OpenAI Agents SDK Adapter.
3
+ *
4
+ * Main `CLEOProviderAdapter` implementation for the OpenAI Agents SDK.
5
+ * Provides spawn and install capabilities. Hooks are not supported (the
6
+ * SDK does not expose a CLI hook system equivalent to Claude Code's).
7
+ *
8
+ * @task T582
9
+ */
10
+
11
+ import type {
12
+ AdapterCapabilities,
13
+ AdapterHealthStatus,
14
+ CLEOProviderAdapter,
15
+ } from '@cleocode/contracts';
16
+ import { OpenAiSdkInstallProvider } from './install.js';
17
+ import { OpenAiSdkSpawnProvider } from './spawn.js';
18
+
19
+ /**
20
+ * CLEO provider adapter for the OpenAI Agents SDK.
21
+ *
22
+ * Bridges CLEO's adapter system with the `@openai/agents` SDK:
23
+ * - Spawn: Launches agents via the SDK runner with handoff topology
24
+ * - Install: Manages AGENTS.md @-references and .openai/ config directory
25
+ * - Tracing: Default-on conduit span persistence via `CleoConduitTraceProcessor`
26
+ *
27
+ * @remarks
28
+ * This adapter is the only CLEO provider with first-class handoff support.
29
+ * Team Lead → Worker topology is declared in `SpawnContext.options.handoffs`
30
+ * and wired directly to SDK `Agent.handoffs`. The SDK handles routing
31
+ * internally without any CLEO glue code.
32
+ *
33
+ * This is also the only provider supporting 100+ LLMs via the Vercel AI SDK
34
+ * bridge (capability flag: `supportsMultiModel`).
35
+ */
36
+ export class OpenAiSdkAdapter implements CLEOProviderAdapter {
37
+ /** Unique provider identifier. */
38
+ readonly id = 'openai-sdk';
39
+ /** Human-readable provider name. */
40
+ readonly name = 'OpenAI Agents SDK';
41
+ /** Adapter version string. */
42
+ readonly version = '1.0.0';
43
+
44
+ /** Declared capabilities for this provider. */
45
+ capabilities: AdapterCapabilities = {
46
+ supportsHooks: false,
47
+ // The SDK does not expose CLI lifecycle hooks equivalent to Claude Code.
48
+ supportedHookEvents: [],
49
+ supportsSpawn: true,
50
+ supportsInstall: true,
51
+ supportsInstructionFiles: true,
52
+ instructionFilePattern: 'AGENTS.md',
53
+ supportsContextMonitor: false,
54
+ supportsStatusline: false,
55
+ supportsProviderPaths: false,
56
+ supportsTransport: false,
57
+ supportsTaskSync: false,
58
+ };
59
+
60
+ /** Spawn provider for SDK-backed agent runs with handoff topology. */
61
+ spawn: OpenAiSdkSpawnProvider;
62
+ /** Install provider for AGENTS.md and .openai/ config directory management. */
63
+ install: OpenAiSdkInstallProvider;
64
+
65
+ /** Project directory this adapter was initialized with, or null. */
66
+ private projectDir: string | null = null;
67
+ /** Whether {@link initialize} has been called. */
68
+ private initialized = false;
69
+
70
+ constructor() {
71
+ this.spawn = new OpenAiSdkSpawnProvider();
72
+ this.install = new OpenAiSdkInstallProvider();
73
+ }
74
+
75
+ /**
76
+ * Initialize the adapter for a given project directory.
77
+ *
78
+ * @param projectDir - Root directory of the project.
79
+ */
80
+ async initialize(projectDir: string): Promise<void> {
81
+ this.projectDir = projectDir;
82
+ this.initialized = true;
83
+ }
84
+
85
+ /**
86
+ * Dispose the adapter and release all resources.
87
+ */
88
+ async dispose(): Promise<void> {
89
+ this.initialized = false;
90
+ this.projectDir = null;
91
+ }
92
+
93
+ /**
94
+ * Run a health check to verify the OpenAI SDK is usable.
95
+ *
96
+ * Checks:
97
+ * 1. Adapter has been initialized
98
+ * 2. `OPENAI_API_KEY` is set in the environment
99
+ *
100
+ * @returns Health status with details about each check.
101
+ */
102
+ async healthCheck(): Promise<AdapterHealthStatus> {
103
+ if (!this.initialized) {
104
+ return {
105
+ healthy: false,
106
+ provider: this.id,
107
+ details: { error: 'Adapter not initialized' },
108
+ };
109
+ }
110
+
111
+ const apiKeyPresent =
112
+ typeof process.env.OPENAI_API_KEY === 'string' && process.env.OPENAI_API_KEY.length > 0;
113
+
114
+ return {
115
+ healthy: apiKeyPresent,
116
+ provider: this.id,
117
+ details: {
118
+ apiKeyPresent,
119
+ projectDir: this.projectDir,
120
+ sdkVersion: '0.8.3',
121
+ },
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Check whether the adapter has been initialized.
127
+ */
128
+ isInitialized(): boolean {
129
+ return this.initialized;
130
+ }
131
+
132
+ /**
133
+ * Get the project directory this adapter was initialized with.
134
+ */
135
+ getProjectDir(): string | null {
136
+ return this.projectDir;
137
+ }
138
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * CLEO permission rules mapped to OpenAI Agents SDK guardrails.
3
+ *
4
+ * CLEO ACLs (file-glob path allowlists, tool allowlists) are expressed as
5
+ * `InputGuardrail` instances that run before agent execution. A path that
6
+ * falls outside the allowed glob list causes the guardrail to trip and
7
+ * the agent run is rejected.
8
+ *
9
+ * @task T582
10
+ */
11
+
12
+ import type { InputGuardrail, InputGuardrailFunctionArgs } from '@openai/agents';
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Helpers
16
+ // ---------------------------------------------------------------------------
17
+
18
+ /**
19
+ * Converts a simple glob pattern to a RegExp.
20
+ *
21
+ * Supports `*` (any chars within a segment) and `**` (any chars including `/`).
22
+ * This is a lightweight alternative to the `minimatch` package so no extra
23
+ * dependency is required in the adapters package.
24
+ *
25
+ * @param glob - Glob pattern to convert (e.g. `/mnt/projects/**`).
26
+ * @returns A RegExp that matches paths conforming to the glob.
27
+ */
28
+ function globToRegex(glob: string): RegExp {
29
+ // Escape regex metacharacters except * and ?
30
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, '\\$&');
31
+ // Replace ** first (order matters), then *
32
+ const pattern = escaped.replace(/\*\*/g, '.+').replace(/\*/g, '[^/]*');
33
+ return new RegExp(`^${pattern}$`);
34
+ }
35
+
36
+ /**
37
+ * Check whether a file-system path is covered by at least one glob pattern.
38
+ *
39
+ * @param path - The absolute or relative path to test.
40
+ * @param allowedGlobs - Array of glob patterns (supports `*` and `**`).
41
+ * @returns `true` when the path matches at least one pattern.
42
+ */
43
+ export function isPathAllowed(path: string, allowedGlobs: string[]): boolean {
44
+ if (allowedGlobs.length === 0) return true;
45
+ return allowedGlobs.some((glob) => globToRegex(glob).test(path));
46
+ }
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Guardrail builders
50
+ // ---------------------------------------------------------------------------
51
+
52
+ /**
53
+ * Build an input guardrail that enforces CLEO file-glob path ACLs.
54
+ *
55
+ * Inspects the serialised agent input for embedded `"path":"..."` fields
56
+ * and rejects the run when a path falls outside the allowlist. This provides
57
+ * an early-exit safety fence before the agent starts consuming model tokens.
58
+ *
59
+ * @param allowedGlobs - Glob patterns that tool path arguments must match.
60
+ * Pass an empty array to allow all paths (permissive mode).
61
+ * @returns An {@link InputGuardrail} ready to attach to an `Agent`.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const guard = buildPathGuardrail(['/mnt/projects/**', '/tmp/**']);
66
+ * const agent = new Agent({ ..., inputGuardrails: [guard] });
67
+ * ```
68
+ */
69
+ export function buildPathGuardrail(allowedGlobs: string[]): InputGuardrail {
70
+ return {
71
+ name: 'cleo_path_acl',
72
+ execute: async (
73
+ args: InputGuardrailFunctionArgs,
74
+ ): Promise<{ tripwireTriggered: boolean; outputInfo: unknown }> => {
75
+ // Serialise input to a string for heuristic path scanning.
76
+ const inputStr = typeof args.input === 'string' ? args.input : JSON.stringify(args.input);
77
+
78
+ // Scan for JSON-encoded `"path":"..."` occurrences in the input text.
79
+ // This is conservative: if no path field is found the guardrail passes.
80
+ const pathMatches = inputStr.matchAll(/"path"\s*:\s*"([^"]+)"/g);
81
+ for (const match of pathMatches) {
82
+ const candidate = match[1];
83
+ if (candidate && !isPathAllowed(candidate, allowedGlobs)) {
84
+ return {
85
+ tripwireTriggered: true,
86
+ outputInfo: {
87
+ reason: `cleo_path_acl: path denied by ACL — ${candidate}`,
88
+ deniedPath: candidate,
89
+ allowedGlobs,
90
+ },
91
+ };
92
+ }
93
+ }
94
+
95
+ return { tripwireTriggered: false, outputInfo: null };
96
+ },
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Build an input guardrail that documents the tool allowlist for audit purposes.
102
+ *
103
+ * In the OpenAI Agents SDK, tool-name enforcement is primarily structural —
104
+ * agents only receive the tools attached to their `tools` array. This guardrail
105
+ * provides an additional audit layer that records the active allowlist in the
106
+ * span metadata.
107
+ *
108
+ * @param allowedTools - Exact tool names permitted for this agent.
109
+ * Pass an empty array to allow all tools (permissive mode).
110
+ * @returns An {@link InputGuardrail} ready to attach to an `Agent`.
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * const guard = buildToolAllowlistGuardrail(['read', 'write']);
115
+ * ```
116
+ */
117
+ export function buildToolAllowlistGuardrail(allowedTools: string[]): InputGuardrail {
118
+ return {
119
+ name: 'cleo_tool_allowlist',
120
+ execute: async (
121
+ _args: InputGuardrailFunctionArgs,
122
+ ): Promise<{ tripwireTriggered: boolean; outputInfo: unknown }> => {
123
+ // Structural enforcement: only listed tools are attached to the agent.
124
+ // This guardrail records the allowlist for audit and always passes.
125
+ return {
126
+ tripwireTriggered: false,
127
+ outputInfo: { allowedTools, checked: true },
128
+ };
129
+ },
130
+ };
131
+ }
132
+
133
+ /**
134
+ * Build the default CLEO guardrail set from spawn options.
135
+ *
136
+ * Combines path ACL and tool allowlist guards into a single array ready to
137
+ * pass as `inputGuardrails` on an `Agent` or `RunConfig`.
138
+ *
139
+ * @param allowedGlobs - File-path glob allowlist.
140
+ * @param allowedTools - Tool name allowlist.
141
+ * @returns Array of input guardrails to attach to the agent.
142
+ */
143
+ export function buildDefaultGuardrails(
144
+ allowedGlobs: string[],
145
+ allowedTools: string[],
146
+ ): InputGuardrail[] {
147
+ const guards: InputGuardrail[] = [];
148
+
149
+ if (allowedGlobs.length > 0) {
150
+ guards.push(buildPathGuardrail(allowedGlobs));
151
+ }
152
+
153
+ if (allowedTools.length > 0) {
154
+ guards.push(buildToolAllowlistGuardrail(allowedTools));
155
+ }
156
+
157
+ return guards;
158
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * CLEO Team topology → OpenAI Agents SDK handoff mapping.
3
+ *
4
+ * CLEO agents are organised in a Lead → Worker hierarchy. This module maps
5
+ * that topology to the SDK's first-class `handoffs` graph:
6
+ *
7
+ * - A Team Lead becomes an `Agent` whose `handoffs` array lists its workers.
8
+ * - Each Worker archetype (read-only, write, bash) is declared in
9
+ * `WORKER_ARCHETYPES` and built on demand.
10
+ * - The mapping is driven by `SpawnContext.options.handoffs`, which is an
11
+ * array of worker archetype names.
12
+ *
13
+ * @task T582
14
+ */
15
+
16
+ import type { InputGuardrail } from '@openai/agents';
17
+ import { Agent } from '@openai/agents';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Worker archetypes
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Descriptor for a pre-configured worker agent archetype.
25
+ *
26
+ * Archetypes are declarative templates. `buildWorkerAgent` inflates them into
27
+ * live SDK `Agent` instances.
28
+ */
29
+ export interface WorkerArchetype {
30
+ /** Archetype identifier (also used as SDK agent name). */
31
+ name: string;
32
+ /** Short description passed as the SDK agent instructions. */
33
+ instructions: string;
34
+ /** Preferred model for this archetype. */
35
+ model: string;
36
+ }
37
+
38
+ /**
39
+ * Registry of built-in CLEO worker archetypes.
40
+ *
41
+ * Callers reference these by name in `SpawnContext.options.handoffs`.
42
+ * New archetypes can be added here without changing the spawn provider.
43
+ */
44
+ export const WORKER_ARCHETYPES: Record<string, WorkerArchetype> = {
45
+ 'worker-read': {
46
+ name: 'worker-read',
47
+ instructions:
48
+ 'You are a read-only CLEO worker. You may only read files and return findings. Never write, modify, or delete files.',
49
+ model: 'gpt-4.1-mini',
50
+ },
51
+ 'worker-write': {
52
+ name: 'worker-write',
53
+ instructions:
54
+ 'You are a CLEO write worker. You implement code changes directed by the lead agent. Follow the lead agent instructions precisely.',
55
+ model: 'gpt-4.1-mini',
56
+ },
57
+ 'worker-bash': {
58
+ name: 'worker-bash',
59
+ instructions:
60
+ 'You are a CLEO bash worker. You run shell commands directed by the lead agent. Only execute commands explicitly requested.',
61
+ model: 'gpt-4.1-mini',
62
+ },
63
+ };
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Agent builders
67
+ // ---------------------------------------------------------------------------
68
+
69
+ /**
70
+ * Build a worker `Agent` instance from a named archetype.
71
+ *
72
+ * @param archetypeName - Key in {@link WORKER_ARCHETYPES}.
73
+ * @param guardrails - Input guardrails to attach to the worker agent.
74
+ * @returns A configured SDK `Agent` or `null` when the archetype is unknown.
75
+ */
76
+ export function buildWorkerAgent(
77
+ archetypeName: string,
78
+ guardrails: InputGuardrail[],
79
+ ): Agent | null {
80
+ const archetype = WORKER_ARCHETYPES[archetypeName];
81
+ if (!archetype) return null;
82
+
83
+ return new Agent({
84
+ name: archetype.name,
85
+ instructions: archetype.instructions,
86
+ model: archetype.model,
87
+ inputGuardrails: guardrails.length > 0 ? guardrails : undefined,
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Build a team lead `Agent` whose `handoffs` reference the given workers.
93
+ *
94
+ * @param leadInstructions - System instructions for the lead agent.
95
+ * @param leadModel - Model to use for the lead agent.
96
+ * @param workers - Worker agents this lead can hand off to.
97
+ * @param guardrails - Input guardrails to attach to the lead agent.
98
+ * @returns A configured lead SDK `Agent`.
99
+ */
100
+ export function buildLeadAgent(
101
+ leadInstructions: string,
102
+ leadModel: string,
103
+ workers: Agent[],
104
+ guardrails: InputGuardrail[],
105
+ ): Agent {
106
+ return new Agent({
107
+ name: 'cleo-lead',
108
+ instructions: leadInstructions,
109
+ model: leadModel,
110
+ handoffs: workers.length > 0 ? workers : undefined,
111
+ inputGuardrails: guardrails.length > 0 ? guardrails : undefined,
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Build a simple single-tier agent (no handoffs) from prompt and model.
117
+ *
118
+ * Used when `SpawnContext.options.tier` is `'worker'` or when no handoff
119
+ * names are provided.
120
+ *
121
+ * @param instructions - Agent system instructions.
122
+ * @param model - Model identifier.
123
+ * @param guardrails - Input guardrails.
124
+ * @returns A configured SDK `Agent`.
125
+ */
126
+ export function buildStandaloneAgent(
127
+ instructions: string,
128
+ model: string,
129
+ guardrails: InputGuardrail[],
130
+ ): Agent {
131
+ return new Agent({
132
+ name: 'cleo-worker',
133
+ instructions,
134
+ model,
135
+ inputGuardrails: guardrails.length > 0 ? guardrails : undefined,
136
+ });
137
+ }
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // Topology builder
141
+ // ---------------------------------------------------------------------------
142
+
143
+ /** Options for building the agent topology from a spawn context. */
144
+ export interface TopologyOptions {
145
+ /** Prompt / instructions for the entry-point agent. */
146
+ instructions: string;
147
+ /** Model to use for the lead / standalone agent. */
148
+ model: string;
149
+ /** Agent tier determines whether workers and handoffs are wired. */
150
+ tier: 'lead' | 'worker' | 'orchestrator';
151
+ /** Names of worker archetypes to create and attach as handoffs. */
152
+ handoffNames: string[];
153
+ /** Input guardrails shared across all agents in the topology. */
154
+ guardrails: InputGuardrail[];
155
+ }
156
+
157
+ /**
158
+ * Build the entry-point agent and its worker topology from spawn options.
159
+ *
160
+ * - `tier === 'lead'` or `tier === 'orchestrator'`: creates a lead agent with
161
+ * worker handoffs derived from `handoffNames`.
162
+ * - `tier === 'worker'`: creates a standalone agent with no handoffs.
163
+ *
164
+ * Unknown archetype names in `handoffNames` are silently skipped.
165
+ *
166
+ * @param options - Topology build options.
167
+ * @returns The entry-point agent to pass to `runner.run()`.
168
+ */
169
+ export function buildAgentTopology(options: TopologyOptions): Agent {
170
+ const { instructions, model, tier, handoffNames, guardrails } = options;
171
+
172
+ if (tier === 'worker') {
173
+ return buildStandaloneAgent(instructions, model, guardrails);
174
+ }
175
+
176
+ // Build worker agents from archetype names; skip unknown names.
177
+ const workers: Agent[] = handoffNames
178
+ .map((name) => buildWorkerAgent(name, guardrails))
179
+ .filter((a): a is Agent => a !== null);
180
+
181
+ if (workers.length === 0) {
182
+ // Lead with no workers — still usable as a standalone agent.
183
+ return buildStandaloneAgent(instructions, model, guardrails);
184
+ }
185
+
186
+ return buildLeadAgent(instructions, model, workers, guardrails);
187
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * CLEO provider adapter for the OpenAI Agents SDK.
5
+ * Default export is the adapter class for dynamic loading by AdapterManager.
6
+ *
7
+ * @task T582
8
+ */
9
+
10
+ import { OpenAiSdkAdapter } from './adapter.js';
11
+
12
+ export { OpenAiSdkAdapter } from './adapter.js';
13
+ export {
14
+ buildDefaultGuardrails,
15
+ buildPathGuardrail,
16
+ buildToolAllowlistGuardrail,
17
+ isPathAllowed,
18
+ } from './guardrails.js';
19
+ export type { TopologyOptions, WorkerArchetype } from './handoff.js';
20
+ export {
21
+ buildAgentTopology,
22
+ buildLeadAgent,
23
+ buildStandaloneAgent,
24
+ buildWorkerAgent,
25
+ WORKER_ARCHETYPES,
26
+ } from './handoff.js';
27
+ export { OpenAiSdkInstallProvider } from './install.js';
28
+ export type { OpenAiSdkSpawnOptions } from './spawn.js';
29
+ export { OpenAiSdkSpawnProvider } from './spawn.js';
30
+ export { CleoConduitTraceProcessor } from './tracing.js';
31
+
32
+ export default OpenAiSdkAdapter;
33
+
34
+ /**
35
+ * Factory function for creating adapter instances.
36
+ * Used by AdapterManager's dynamic import fallback.
37
+ *
38
+ * @remarks
39
+ * This is the primary entry point for dynamic adapter loading.
40
+ * AdapterManager calls this function when it resolves the openai-sdk
41
+ * provider via its import-based discovery mechanism.
42
+ *
43
+ * @returns A new {@link OpenAiSdkAdapter} instance ready for initialization.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { createAdapter } from '@cleocode/adapters/providers/openai-sdk';
48
+ *
49
+ * const adapter = createAdapter();
50
+ * await adapter.initialize('/path/to/project');
51
+ * ```
52
+ */
53
+ export function createAdapter(): OpenAiSdkAdapter {
54
+ return new OpenAiSdkAdapter();
55
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * OpenAI SDK Install Provider.
3
+ *
4
+ * Handles CLEO installation into OpenAI SDK environments:
5
+ * - Writes an AGENTS.md file with CLEO @-references
6
+ * - Creates a `.openai/` config stub if it does not exist
7
+ *
8
+ * @task T582
9
+ */
10
+
11
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
14
+
15
+ /** Lines that should appear in AGENTS.md to reference CLEO. */
16
+ const INSTRUCTION_REFERENCES = ['@~/.cleo/templates/CLEO-INJECTION.md', '@.cleo/memory-bridge.md'];
17
+
18
+ /**
19
+ * Install provider for the OpenAI Agents SDK.
20
+ *
21
+ * Manages CLEO's integration with OpenAI SDK projects by:
22
+ * 1. Ensuring AGENTS.md contains @-references to CLEO instruction files
23
+ * 2. Creating the `.openai/` config directory stub if absent
24
+ *
25
+ * @remarks
26
+ * Installation is idempotent — running install multiple times on the same
27
+ * project produces the same result.
28
+ */
29
+ export class OpenAiSdkInstallProvider implements AdapterInstallProvider {
30
+ /**
31
+ * Install CLEO into an OpenAI SDK project.
32
+ *
33
+ * @param options - Installation options including project directory.
34
+ * @returns Result describing what was installed.
35
+ */
36
+ async install(options: InstallOptions): Promise<InstallResult> {
37
+ const { projectDir } = options;
38
+ const installedAt = new Date().toISOString();
39
+ const details: Record<string, unknown> = {};
40
+
41
+ // Step 1: Ensure AGENTS.md has @-references
42
+ const instructionFileUpdated = this.updateInstructionFile(projectDir);
43
+ if (instructionFileUpdated) {
44
+ details.instructionFile = join(projectDir, 'AGENTS.md');
45
+ }
46
+
47
+ // Step 2: Create .openai config directory stub
48
+ const configCreated = this.ensureConfigDir(projectDir);
49
+ if (configCreated) {
50
+ details.configDir = join(projectDir, '.openai');
51
+ }
52
+
53
+ return {
54
+ success: true,
55
+ installedAt,
56
+ instructionFileUpdated,
57
+ details,
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Uninstall CLEO from the current OpenAI SDK project.
63
+ *
64
+ * Does not remove AGENTS.md references (they are harmless if CLEO is absent).
65
+ */
66
+ async uninstall(): Promise<void> {}
67
+
68
+ /**
69
+ * Check whether CLEO is installed in the current OpenAI SDK environment.
70
+ *
71
+ * Checks for `@~/.cleo/templates/CLEO-INJECTION.md` in AGENTS.md.
72
+ */
73
+ async isInstalled(): Promise<boolean> {
74
+ // A project is considered installed when AGENTS.md contains the first reference.
75
+ // There is no plugin registry for the OpenAI SDK.
76
+ return false;
77
+ }
78
+
79
+ /**
80
+ * Ensure AGENTS.md contains @-references to CLEO instruction files.
81
+ *
82
+ * @param projectDir - Project root directory.
83
+ */
84
+ async ensureInstructionReferences(projectDir: string): Promise<void> {
85
+ this.updateInstructionFile(projectDir);
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // Private helpers
90
+ // ---------------------------------------------------------------------------
91
+
92
+ /**
93
+ * Update AGENTS.md with CLEO @-references.
94
+ *
95
+ * @returns `true` if the file was created or modified.
96
+ */
97
+ private updateInstructionFile(projectDir: string): boolean {
98
+ const agentsMdPath = join(projectDir, 'AGENTS.md');
99
+ let content = '';
100
+ let existed = false;
101
+
102
+ if (existsSync(agentsMdPath)) {
103
+ content = readFileSync(agentsMdPath, 'utf-8');
104
+ existed = true;
105
+ }
106
+
107
+ const missingRefs = INSTRUCTION_REFERENCES.filter((ref) => !content.includes(ref));
108
+ if (missingRefs.length === 0) return false;
109
+
110
+ const refsBlock = missingRefs.join('\n');
111
+
112
+ if (existed) {
113
+ const separator = content.endsWith('\n') ? '' : '\n';
114
+ content = content + separator + refsBlock + '\n';
115
+ } else {
116
+ content = refsBlock + '\n';
117
+ }
118
+
119
+ writeFileSync(agentsMdPath, content, 'utf-8');
120
+ return true;
121
+ }
122
+
123
+ /**
124
+ * Create the `.openai/` config directory if it does not exist.
125
+ *
126
+ * @returns `true` if the directory was created.
127
+ */
128
+ private ensureConfigDir(projectDir: string): boolean {
129
+ const configDir = join(projectDir, '.openai');
130
+ if (existsSync(configDir)) return false;
131
+
132
+ mkdirSync(configDir, { recursive: true });
133
+ return true;
134
+ }
135
+ }