@janole/ai-sdk-provider-codex-asp
v0.3.0
Published
Vercel AI SDK v6 custom provider for the Codex App Server Protocol.
Maintainers
Readme
@janole/ai-sdk-provider-codex-asp
@janole/ai-sdk-provider-codex-asp is a Vercel AI SDK v6 custom provider for the Codex App Server Protocol.
Status: POC feature-complete for language model usage. Currently tested with codex-cli 0.106.0.
LanguageModelV3provider implementation- Streaming (
streamText) and non-streaming (generateText) - Standard AI SDK
tool()support via Codex dynamic tools injection - Provider-executed tool protocol for Codex command executions
stdioandwebsockettransports- Persistent worker pool with thread management
Installation
npm install @janole/ai-sdk-provider-codex-asp aiQuick Start
1. Non-streaming (generateText)
import { generateText } from 'ai';
import { createCodexAppServer } from '@janole/ai-sdk-provider-codex-asp';
const codex = createCodexAppServer({
defaultModel: 'gpt-5.3-codex',
clientInfo: { name: 'my-app', version: '0.1.0' },
});
const result = await generateText({
model: codex.languageModel('gpt-5.3-codex'),
prompt: 'Write a short release note title for websocket support.',
});
console.log(result.text);2. Streaming (streamText)
import { streamText } from 'ai';
import { createCodexAppServer } from '@janole/ai-sdk-provider-codex-asp';
const codex = createCodexAppServer({
defaultModel: 'gpt-5.3-codex',
clientInfo: { name: 'my-app', version: '0.1.0' },
});
const result = streamText({
model: codex('gpt-5.3-codex'),
prompt: 'Explain JSON-RPC in one paragraph.',
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}Tools
Use standard AI SDK tool() definitions — the provider automatically injects them into Codex as dynamic tools and routes results back. No Codex-specific API needed.
Requires a persistent transport so tool results can be fed back within the same session:
import { stepCountIs, streamText, tool } from 'ai';
import { z } from 'zod';
import { createCodexAppServer } from '@janole/ai-sdk-provider-codex-asp';
const codex = createCodexAppServer({
persistent: { scope: 'global', poolSize: 1, idleTimeoutMs: 60_000 },
});
const result = streamText({
model: codex('gpt-5.3-codex'),
prompt: 'Can you check ticket 15 and also the weather in Berlin?',
tools: {
lookup_ticket: tool({
description: 'Look up the current status of a support ticket by its ID.',
inputSchema: z.object({
id: z.string().describe('The ticket ID, e.g. "TICK-42".'),
}),
execute: async ({ id }) => `Ticket ${id} is open and assigned to team Alpha.`,
}),
check_weather: tool({
description: 'Get the current weather for a given location.',
inputSchema: z.object({
location: z.string().describe('City name or coordinates.'),
}),
execute: async ({ location }) => `Weather in ${location}: 22°C, sunny`,
}),
},
stopWhen: stepCountIs(5),
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
await codex.shutdown();API Reference
const codex = createCodexAppServer({
defaultModel?: string,
clientInfo?: { name, version, title? }, // defaults to package.json
transport?: { type: 'stdio' | 'websocket', stdio?, websocket? },
persistent?: { poolSize?, idleTimeoutMs?, scope?, key? },
compaction?: { shouldCompactOnResume?, strict? }, // optional thread/compact/start before resumed turns
debug?: { logPackets?, logger? }, // packet-level JSON-RPC debug logging
defaultThreadSettings?: { cwd?, approvalPolicy?, sandbox? },
defaultTurnSettings?: { cwd?, approvalPolicy?, sandboxPolicy?, model?, effort?, summary? },
approvals?: { onCommandApproval?, onFileChangeApproval? },
toolTimeoutMs?: number, // default: 30000
interruptTimeoutMs?: number, // default: 10000
});
codex(modelId) // returns a language model instance
codex.languageModel(modelId) // explicit alias
codex.chat(modelId) // explicit alias
codex.shutdown() // clean up persistent workersRich sandbox policy example (turn-level):
const codex = createCodexAppServer({
defaultTurnSettings: {
approvalPolicy: "on-request",
sandboxPolicy: {
type: "externalSandbox",
networkAccess: "enabled",
},
},
});See src/provider.ts for full type definitions.
Examples
See the examples/ directory:
generate-text.ts— Non-streaming text generationgenerate-object.ts— Structured output withgenerateText+Output.objectstream-text.ts— Streaming text generationcross-call-tools.ts— Standard AI SDK tools via Codexdynamic-tools.ts— Provider-level dynamic toolsthread-continuation.ts— Multi-turn thread resumptionapprovals.ts— Command and file-change approval handling
Run any example with:
npx tsx examples/stream-text.tsTroubleshooting
No such file or command: codex:- Install Codex CLI and ensure
codexis inPATH.
- Install Codex CLI and ensure
WebSocket is not available in this runtime:- Use Node.js 18+ with global WebSocket support, or use
stdiotransport.
- Use Node.js 18+ with global WebSocket support, or use
- Request timeouts:
- Increase
toolTimeoutMsfor long-running dynamic tools. - Increase
interruptTimeoutMsifturn/interruptacks are slow under heavy load.
- Increase
- Empty generated text:
- Verify Codex emits
item/agentMessage/deltaandturn/completednotifications.
- Verify Codex emits
- Compaction fails on resumed threads:
- Set
compaction.shouldCompactOnResume: trueto always compact resumed threads. - Or provide
compaction.shouldCompactOnResume: (ctx) => boolean | Promise<boolean>for dynamic decisions. - Leave
compaction.strictunset/false to continue the turn whenthread/compact/startfails. - Set
compaction.strict: trueif you want compaction failures to fail fast.
- Set
Development
npm install
npm run build # ESM + CJS + .d.ts via tsup
npm run qa # lint + typecheck + test (all-in-one)Generated Protocol Types
src/protocol/app-server-protocol/ is gitignored, but selected generated files are intentionally tracked with git add -f so protocol shape changes stay visible in PRs.
Important: for every tracked generated file, all imported generated type dependencies (direct + transitive) must also be tracked.
Use the local skill .codex/skills/codex-protocol-type-upgrade/SKILL.md for the exact workflow.
When protocol shapes change, clean and regenerate:
rm -rf src/protocol/app-server-protocol
npm run codex:generate-typesThen follow the skill workflow to:
- adapt runtime mappings if needed
- add missing generated dependencies with
git add -f - run
npm run typecheck(and focused tests)
License
MIT
