@stdiobus/workers-registry 1.4.11 → 1.4.12

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 (33) hide show
  1. package/launch/index.js +1 -1
  2. package/launch/index.js.map +2 -2
  3. package/out/dist/workers-registry/acp-registry/index.js +2 -2
  4. package/out/dist/workers-registry/acp-registry/index.js.map +3 -3
  5. package/out/dist/workers-registry/acp-worker/index.js +1 -1
  6. package/out/dist/workers-registry/acp-worker/index.js.map +2 -2
  7. package/out/dist/workers-registry/index.d.ts +1 -0
  8. package/out/dist/workers-registry/index.js +6 -0
  9. package/out/dist/workers-registry/openai-agent/index.js +4 -0
  10. package/out/dist/workers-registry/openai-agent/index.js.map +7 -0
  11. package/out/tsc/workers-registry/acp-worker/src/registry-launcher/config/types.d.ts +2 -0
  12. package/out/tsc/workers-registry/acp-worker/src/registry-launcher/registry/index.d.ts +45 -0
  13. package/out/tsc/workers-registry/openai-agent/src/agent.d.ts +26 -0
  14. package/out/tsc/workers-registry/openai-agent/src/client.d.ts +7 -0
  15. package/out/tsc/workers-registry/openai-agent/src/config.d.ts +2 -0
  16. package/out/tsc/workers-registry/openai-agent/src/index.d.ts +11 -0
  17. package/out/tsc/workers-registry/openai-agent/src/session-id-router.d.ts +21 -0
  18. package/out/tsc/workers-registry/openai-agent/src/session-manager.d.ts +7 -0
  19. package/out/tsc/workers-registry/openai-agent/src/session.d.ts +18 -0
  20. package/out/tsc/workers-registry/openai-agent/src/sse-parser.d.ts +2 -0
  21. package/out/tsc/workers-registry/openai-agent/src/types.d.ts +43 -0
  22. package/out/tsc/workers-registry/openai-agent/tests/agent.property.test.d.ts +1 -0
  23. package/out/tsc/workers-registry/openai-agent/tests/agent.test.d.ts +1 -0
  24. package/out/tsc/workers-registry/openai-agent/tests/client.test.d.ts +1 -0
  25. package/out/tsc/workers-registry/openai-agent/tests/config.property.test.d.ts +1 -0
  26. package/out/tsc/workers-registry/openai-agent/tests/config.test.d.ts +1 -0
  27. package/out/tsc/workers-registry/openai-agent/tests/conversion.property.test.d.ts +1 -0
  28. package/out/tsc/workers-registry/openai-agent/tests/error-handling.property.test.d.ts +1 -0
  29. package/out/tsc/workers-registry/openai-agent/tests/session.property.test.d.ts +1 -0
  30. package/out/tsc/workers-registry/openai-agent/tests/session.test.d.ts +1 -0
  31. package/out/tsc/workers-registry/openai-agent/tests/sse-parser.property.test.d.ts +1 -0
  32. package/out/tsc/workers-registry/openai-agent/tests/sse-parser.test.d.ts +1 -0
  33. package/package.json +5 -1
@@ -13,4 +13,5 @@ export const workers: {
13
13
  'launch': WorkerInfo;
14
14
  'mcp-echo-server': WorkerInfo;
15
15
  'mcp-to-acp-proxy': WorkerInfo;
16
+ 'openai-agent': WorkerInfo;
16
17
  };
@@ -35,5 +35,11 @@ export const workers = {
35
35
  "types": null,
36
36
  "config": null,
37
37
  "type": "javascript"
38
+ },
39
+ "openai-agent": {
40
+ "entrypoint": "./openai-agent/index.js",
41
+ "types": "../tsc/workers-registry/openai-agent/src/index.d.ts",
42
+ "config": null,
43
+ "type": "typescript"
38
44
  }
39
45
  };
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import{Readable,Writable,Transform}from"node:stream";import{AgentSideConnection,ndJsonStream}from"@agentclientprotocol/sdk";import{PROTOCOL_VERSION}from"@agentclientprotocol/sdk";function loadConfig(){const baseUrl=process.env.OPENAI_BASE_URL||"https://api.openai.com/v1";const apiKey=process.env.OPENAI_API_KEY||"";const model=process.env.OPENAI_MODEL||"gpt-4o";const systemPrompt=process.env.OPENAI_SYSTEM_PROMPT||void 0;let maxTokens;const maxTokensStr=process.env.OPENAI_MAX_TOKENS;if(maxTokensStr!==void 0){const parsed=parseInt(maxTokensStr,10);maxTokens=Number.isNaN(parsed)?void 0:parsed}let temperature;const temperatureStr=process.env.OPENAI_TEMPERATURE;if(temperatureStr!==void 0){const parsed=parseFloat(temperatureStr);temperature=Number.isNaN(parsed)?void 0:parsed}if(!apiKey){console.error("[openai-agent] Warning: OPENAI_API_KEY is not set. This may be fine for local endpoints like Ollama.")}return{baseUrl,apiKey,model,systemPrompt,maxTokens,temperature}}import crypto from"node:crypto";var Session=class{id;cwd;_history=[];_abortController;_cancelled=false;constructor(id,cwd){this.id=id;this.cwd=cwd;this._abortController=new AbortController}getAbortSignal(){return this._abortController.signal}cancel(){this._cancelled=true;this._abortController.abort()}isCancelled(){return this._cancelled}resetCancellation(){this._cancelled=false;this._abortController=new AbortController}addHistoryEntry(role,content){this._history.push({role,content})}getHistory(){return[...this._history]}};var SessionManager=class{sessions=new Map;createSession(cwd){const id=crypto.randomUUID();const session=new Session(id,cwd);this.sessions.set(id,session);return session}getSession(id){return this.sessions.get(id)}cancelSession(id){const session=this.sessions.get(id);if(session){session.cancel();return true}return false}};var DATA_PREFIX="data: ";var DONE_MARKER="[DONE]";function parseLine(line){if(!line.trim()){return{type:"skip"}}if(line.startsWith(":")){return{type:"skip"}}if(!line.startsWith(DATA_PREFIX)){return{type:"skip"}}const data=line.slice(DATA_PREFIX.length);if(data===DONE_MARKER){return{type:"done"}}try{const payload=JSON.parse(data);return{type:"data",payload}}catch{console.error("[openai-agent] Failed to parse SSE JSON:",data);return{type:"skip"}}}function classifyHttpError(status,url){if(status===401||status===403){return`Authentication error (HTTP ${status}) calling ${url}. Check your OPENAI_API_KEY.`}if(status===429){return`Rate limit exceeded (HTTP 429) calling ${url}. Please retry later.`}if(status>=500){return`Server error (HTTP ${status}) from ${url}.`}return`HTTP error (${status}) from ${url}.`}var ChatCompletionsClient=class{config;constructor(config){this.config=config}async streamCompletion(messages,signal,onChunk){const url=`${this.config.baseUrl}/chat/completions`;const body={model:this.config.model,messages,stream:true};if(this.config.maxTokens!==void 0){body.max_tokens=this.config.maxTokens}if(this.config.temperature!==void 0){body.temperature=this.config.temperature}let response;try{response=await fetch(url,{method:"POST",headers:{"Authorization":`Bearer ${this.config.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(body),signal})}catch(error){if(error instanceof Error&&error.name==="AbortError"){return{stopReason:"cancelled",fullResponse:""}}const message=error instanceof Error?error.message:String(error);throw new Error(`Network error connecting to ${url}: ${message}`)}if(!response.ok){throw new Error(classifyHttpError(response.status,url))}if(!response.body){throw new Error(`No response body from ${url}.`)}let fullResponse="";const reader=response.body.getReader();const decoder=new TextDecoder;let buffer="";try{while(true){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:true});const lines=buffer.split("\n");buffer=lines.pop()||"";for(const line of lines){const event=parseLine(line);if(event.type==="done"){return{stopReason:"end_turn",fullResponse}}if(event.type==="data"){const chunk=event.payload;const content=chunk.choices?.[0]?.delta?.content;if(content){fullResponse+=content;await onChunk(content)}}}}}catch(error){if(error instanceof Error&&error.name==="AbortError"){return{stopReason:"cancelled",fullResponse}}throw error}return{stopReason:"end_turn",fullResponse}}};function convertContentBlocks(blocks){const parts=[];for(const block of blocks){if(block.type==="text"){parts.push(block.text)}else if(block.type==="resource_link"){parts.push(`[Resource: ${block.name}] ${block.uri}`)}else if(block.type==="resource"){const text="text"in block.resource?block.resource.text:"";parts.push(`[Resource: ${block.resource.uri}]
3
+ ${text}`)}else if(block.type==="image"){parts.push(`[Image: ${block.mimeType}]`)}}return parts.join("\n")}function buildMessages(systemPrompt,history,userMessage){const messages=[];if(systemPrompt){messages.push({role:"system",content:systemPrompt})}for(const entry of history){messages.push({role:entry.role,content:entry.content})}messages.push({role:"user",content:userMessage});return messages}var OpenAIAgent=class{connection;sessionManager;client;config;constructor(connection2){this.connection=connection2;this.sessionManager=new SessionManager;this.config=loadConfig();this.client=new ChatCompletionsClient(this.config)}async initialize(_params){return{protocolVersion:PROTOCOL_VERSION,agentInfo:{name:"openai-agent",version:"1.0.0"},agentCapabilities:{promptCapabilities:{embeddedContext:true}},authMethods:[]}}async newSession(params){const session=this.sessionManager.createSession(params.cwd);return{sessionId:session.id}}async loadSession(_params){throw new Error("Session loading is not supported")}async authenticate(_params){}async prompt(params){const session=this.sessionManager.getSession(params.sessionId);if(!session){throw new Error(`Session not found: ${params.sessionId}`)}if(session.isCancelled()){return{stopReason:"cancelled"}}session.resetCancellation();const userMessage=convertContentBlocks(params.prompt);session.addHistoryEntry("user",userMessage);const messages=buildMessages(this.config.systemPrompt,session.getHistory().slice(0,-1),userMessage);try{const result=await this.client.streamCompletion(messages,session.getAbortSignal(),async text=>{await this.connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text}}})});if(result.stopReason==="cancelled"){return{stopReason:"cancelled"}}if(result.fullResponse){session.addHistoryEntry("assistant",result.fullResponse)}return{stopReason:"end_turn"}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);await this.connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:errorMessage}}});return{stopReason:"end_turn"}}}async cancel(params){this.sessionManager.cancelSession(params.sessionId)}};var SessionIdRouter=class{requestSessionIdMap=new Map;acpSessionIdMap=new Map;processIncomingLine(line){if(!line.trim()){return line}try{const msg=JSON.parse(line);const routingSessionId=this.readSessionId(msg.sessionId);const hasId=msg.id!==void 0&&msg.id!==null;if(hasId&&routingSessionId){this.requestSessionIdMap.set(msg.id,routingSessionId);console.error(`[worker] Saved sessionId="${routingSessionId}" for request id=${msg.id}`)}const paramsSessionId=this.readSessionId(msg.params?.sessionId);if(routingSessionId&&paramsSessionId){this.setAcpSessionMapping(paramsSessionId,routingSessionId,"request")}if(hasId&&routingSessionId){const{sessionId:_sessionId,...msgWithoutSession}=msg;return JSON.stringify(msgWithoutSession)}return line}catch{return line}}processOutgoingLine(line){if(!line.trim()){return line}try{const msg=JSON.parse(line);const hasId=msg.id!==void 0&&msg.id!==null;if(hasId&&this.requestSessionIdMap.has(msg.id)){const routingSessionId=this.requestSessionIdMap.get(msg.id);this.requestSessionIdMap.delete(msg.id);if(routingSessionId){const resultSessionId=this.readSessionId(msg.result?.sessionId);if(resultSessionId){this.setAcpSessionMapping(resultSessionId,routingSessionId,"response")}const msgWithSession={...msg,sessionId:routingSessionId};console.error(`[worker] Restored sessionId="${routingSessionId}" for response id=${msg.id}`);return JSON.stringify(msgWithSession)}}if(!hasId&&!this.readSessionId(msg.sessionId)){const paramsSessionId=this.readSessionId(msg.params?.sessionId);if(paramsSessionId){const routingSessionId=this.acpSessionIdMap.get(paramsSessionId);if(routingSessionId){const msgWithSession={...msg,sessionId:routingSessionId};return JSON.stringify(msgWithSession)}}}return line}catch{return line}}readSessionId(value){return typeof value==="string"&&value.length>0?value:null}setAcpSessionMapping(acpSessionId,routingSessionId,source){const existing=this.acpSessionIdMap.get(acpSessionId);if(existing===routingSessionId){return}this.acpSessionIdMap.set(acpSessionId,routingSessionId);console.error(`[worker] Mapped ACP sessionId="${acpSessionId}" to routing sessionId="${routingSessionId}" (${source})`)}};console.error("[openai-agent] Starting OpenAI Agent Worker...");var sessionIdRouter=new SessionIdRouter;var stdinTransform=new Transform({objectMode:false,transform(chunk,_encoding,callback){const lines=chunk.toString().split("\n");const processedLines=[];for(const line of lines){processedLines.push(sessionIdRouter.processIncomingLine(line))}callback(null,Buffer.from(processedLines.join("\n")))}});var stdoutTransform=new Transform({objectMode:false,transform(chunk,_encoding,callback){const lines=chunk.toString().split("\n");const processedLines=[];for(const line of lines){processedLines.push(sessionIdRouter.processOutgoingLine(line))}callback(null,Buffer.from(processedLines.join("\n")))}});process.stdin.pipe(stdinTransform);var inputStream=Readable.toWeb(stdinTransform);var outputStream=Writable.toWeb(stdoutTransform);stdoutTransform.pipe(process.stdout);var stream=ndJsonStream(outputStream,inputStream);var connection=new AgentSideConnection(conn=>new OpenAIAgent(conn),stream);console.error("[openai-agent] AgentSideConnection established, ready for messages");process.on("SIGTERM",async()=>{console.error("[openai-agent] Received SIGTERM, shutting down...");await connection.closed;process.exit(0)});process.on("SIGINT",async()=>{console.error("[openai-agent] Received SIGINT, shutting down...");await connection.closed;process.exit(0)});process.on("uncaughtException",error=>{console.error("[openai-agent] Uncaught exception:",error);process.exit(1)});process.on("unhandledRejection",(reason,promise)=>{console.error("[openai-agent] Unhandled rejection at:",promise,"reason:",reason)});connection.closed.then(()=>{console.error("[openai-agent] Connection closed");process.exit(0)}).catch(error=>{console.error("[openai-agent] Connection error:",error);process.exit(1)});
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../workers-registry/openai-agent/src/index.ts", "../../../../workers-registry/openai-agent/src/agent.ts", "../../../../workers-registry/openai-agent/src/config.ts", "../../../../workers-registry/openai-agent/src/session-manager.ts", "../../../../workers-registry/openai-agent/src/session.ts", "../../../../workers-registry/openai-agent/src/sse-parser.ts", "../../../../workers-registry/openai-agent/src/client.ts", "../../../../workers-registry/openai-agent/src/session-id-router.ts"],
4
+ "sourcesContent": ["/**\n * OpenAI Agent Worker for stdio Bus kernel\n *\n * This worker implements the Agent Client Protocol (ACP) and bridges\n * to any OpenAI Chat Completions API-compatible endpoint.\n *\n * It runs as a child process of stdio Bus kernel, communicating via stdin/stdout NDJSON.\n *\n * @module index\n */\n\nimport { Readable, Writable, Transform } from 'node:stream';\nimport { AgentSideConnection, ndJsonStream } from '@agentclientprotocol/sdk';\nimport { OpenAIAgent } from './agent.js';\nimport { SessionIdRouter } from './session-id-router.js';\n\n// Log startup message to stderr (not stdout - stdout is for protocol messages)\nconsole.error('[openai-agent] Starting OpenAI Agent Worker...');\n\nconst sessionIdRouter = new SessionIdRouter();\n\n/**\n * Transform stream to intercept stdin and save sessionId from requests.\n * Removes sessionId before passing to ACP SDK (SDK doesn't know about it).\n */\nconst stdinTransform = new Transform({\n objectMode: false,\n transform(chunk: Buffer, _encoding, callback) {\n const lines = chunk.toString().split('\\n');\n const processedLines: string[] = [];\n\n for (const line of lines) {\n processedLines.push(sessionIdRouter.processIncomingLine(line));\n }\n\n callback(null, Buffer.from(processedLines.join('\\n')));\n },\n});\n\n/**\n * Transform stream to intercept stdout and restore sessionId in responses.\n * Adds sessionId back for stdio_bus routing.\n */\nconst stdoutTransform = new Transform({\n objectMode: false,\n transform(chunk: Buffer, _encoding, callback) {\n const lines = chunk.toString().split('\\n');\n const processedLines: string[] = [];\n\n for (const line of lines) {\n processedLines.push(sessionIdRouter.processOutgoingLine(line));\n }\n\n callback(null, Buffer.from(processedLines.join('\\n')));\n },\n});\n\n// Pipe stdin through transform before SDK\nprocess.stdin.pipe(stdinTransform);\n\n/**\n * Convert transformed stdin to web ReadableStream for SDK.\n */\nconst inputStream = Readable.toWeb(stdinTransform) as ReadableStream<Uint8Array>;\n\n/**\n * Convert stdout transform to web WritableStream for SDK.\n */\nconst outputStream = Writable.toWeb(stdoutTransform) as WritableStream<Uint8Array>;\n\n// Pipe transform output to actual stdout\nstdoutTransform.pipe(process.stdout);\n\n/**\n * Create the NDJSON stream for ACP communication.\n * The SDK handles all NDJSON framing and JSON-RPC protocol details automatically.\n */\nconst stream = ndJsonStream(outputStream, inputStream);\n\n/**\n * Create the AgentSideConnection with stdio transport.\n *\n * The SDK pattern uses a factory function that receives the connection\n * and returns an Agent instance. The SDK handles all NDJSON framing\n * and JSON-RPC protocol details automatically.\n */\nconst connection = new AgentSideConnection(\n (conn) => new OpenAIAgent(conn),\n stream,\n);\n\n// Log that connection is established\nconsole.error('[openai-agent] AgentSideConnection established, ready for messages');\n\n/**\n * Handle graceful shutdown on SIGTERM.\n *\n * When stdio Bus kernel sends SIGTERM, we should wait for the connection to close\n * and allow pending operations to complete.\n */\nprocess.on('SIGTERM', async () => {\n console.error('[openai-agent] Received SIGTERM, shutting down...');\n await connection.closed;\n process.exit(0);\n});\n\n/**\n * Handle SIGINT for development convenience.\n */\nprocess.on('SIGINT', async () => {\n console.error('[openai-agent] Received SIGINT, shutting down...');\n await connection.closed;\n process.exit(0);\n});\n\n/**\n * Handle uncaught exceptions to prevent silent failures.\n */\nprocess.on('uncaughtException', (error) => {\n console.error('[openai-agent] Uncaught exception:', error);\n process.exit(1);\n});\n\n/**\n * Handle unhandled promise rejections.\n */\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error('[openai-agent] Unhandled rejection at:', promise, 'reason:', reason);\n});\n\n/**\n * Wait for the connection to close (either normally or due to error).\n * This keeps the process running until the connection ends.\n */\nconnection.closed.then(() => {\n console.error('[openai-agent] Connection closed');\n process.exit(0);\n}).catch((error) => {\n console.error('[openai-agent] Connection error:', error);\n process.exit(1);\n});\n", "import type {\n Agent,\n AgentSideConnection,\n AuthenticateRequest,\n AuthenticateResponse,\n CancelNotification,\n ContentBlock,\n InitializeRequest,\n InitializeResponse,\n LoadSessionRequest,\n LoadSessionResponse,\n NewSessionRequest,\n NewSessionResponse,\n PromptRequest,\n PromptResponse,\n} from '@agentclientprotocol/sdk';\nimport { PROTOCOL_VERSION } from '@agentclientprotocol/sdk';\nimport { loadConfig } from './config.js';\nimport { SessionManager } from './session-manager.js';\nimport { ChatCompletionsClient } from './client.js';\nimport type { OpenAIMessage } from './types.js';\n\n/**\n * Convert ACP content blocks to a single user message string.\n */\nexport function convertContentBlocks(blocks: ContentBlock[]): string {\n const parts: string[] = [];\n for (const block of blocks) {\n if (block.type === 'text') {\n parts.push(block.text);\n } else if (block.type === 'resource_link') {\n parts.push(`[Resource: ${block.name}] ${block.uri}`);\n } else if (block.type === 'resource') {\n const text = 'text' in block.resource ? block.resource.text : '';\n parts.push(`[Resource: ${block.resource.uri}]\\n${text}`);\n } else if (block.type === 'image') {\n parts.push(`[Image: ${block.mimeType}]`);\n }\n }\n return parts.join('\\n');\n}\n\n/**\n * Build the full messages array for the Chat Completions API.\n */\nexport function buildMessages(\n systemPrompt: string | undefined,\n history: Array<{ role: 'user' | 'assistant'; content: string }>,\n userMessage: string,\n): OpenAIMessage[] {\n const messages: OpenAIMessage[] = [];\n if (systemPrompt) {\n messages.push({ role: 'system', content: systemPrompt });\n }\n for (const entry of history) {\n messages.push({ role: entry.role, content: entry.content });\n }\n messages.push({ role: 'user', content: userMessage });\n return messages;\n}\n\nexport class OpenAIAgent implements Agent {\n private readonly connection: AgentSideConnection;\n private readonly sessionManager: SessionManager;\n private readonly client: ChatCompletionsClient;\n private readonly config;\n\n constructor(connection: AgentSideConnection) {\n this.connection = connection;\n this.sessionManager = new SessionManager();\n this.config = loadConfig();\n this.client = new ChatCompletionsClient(this.config);\n }\n\n async initialize(_params: InitializeRequest): Promise<InitializeResponse> {\n return {\n protocolVersion: PROTOCOL_VERSION,\n agentInfo: {\n name: 'openai-agent',\n version: '1.0.0',\n },\n agentCapabilities: {\n promptCapabilities: {\n embeddedContext: true,\n },\n },\n authMethods: [],\n };\n }\n\n async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {\n const session = this.sessionManager.createSession(params.cwd);\n return { sessionId: session.id };\n }\n\n async loadSession(_params: LoadSessionRequest): Promise<LoadSessionResponse> {\n throw new Error('Session loading is not supported');\n }\n\n async authenticate(_params: AuthenticateRequest): Promise<AuthenticateResponse | void> {\n // No authentication needed at ACP level\n }\n\n async prompt(params: PromptRequest): Promise<PromptResponse> {\n const session = this.sessionManager.getSession(params.sessionId);\n if (!session) {\n throw new Error(`Session not found: ${params.sessionId}`);\n }\n\n if (session.isCancelled()) {\n return { stopReason: 'cancelled' };\n }\n\n session.resetCancellation();\n\n const userMessage = convertContentBlocks(params.prompt);\n session.addHistoryEntry('user', userMessage);\n\n const messages = buildMessages(\n this.config.systemPrompt,\n session.getHistory().slice(0, -1),\n userMessage,\n );\n\n try {\n const result = await this.client.streamCompletion(\n messages,\n session.getAbortSignal(),\n async (text) => {\n await this.connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: { type: 'text', text },\n },\n });\n },\n );\n\n if (result.stopReason === 'cancelled') {\n return { stopReason: 'cancelled' };\n }\n\n if (result.fullResponse) {\n session.addHistoryEntry('assistant', result.fullResponse);\n }\n\n return { stopReason: 'end_turn' };\n } catch (error: unknown) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n await this.connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: { type: 'text', text: errorMessage },\n },\n });\n return { stopReason: 'end_turn' };\n }\n }\n\n async cancel(params: CancelNotification): Promise<void> {\n this.sessionManager.cancelSession(params.sessionId);\n }\n}\n", "import { AgentConfig } from './types.js';\n\nexport function loadConfig(): AgentConfig {\n const baseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';\n const apiKey = process.env.OPENAI_API_KEY || '';\n const model = process.env.OPENAI_MODEL || 'gpt-4o';\n const systemPrompt = process.env.OPENAI_SYSTEM_PROMPT || undefined;\n\n let maxTokens: number | undefined;\n const maxTokensStr = process.env.OPENAI_MAX_TOKENS;\n if (maxTokensStr !== undefined) {\n const parsed = parseInt(maxTokensStr, 10);\n maxTokens = Number.isNaN(parsed) ? undefined : parsed;\n }\n\n let temperature: number | undefined;\n const temperatureStr = process.env.OPENAI_TEMPERATURE;\n if (temperatureStr !== undefined) {\n const parsed = parseFloat(temperatureStr);\n temperature = Number.isNaN(parsed) ? undefined : parsed;\n }\n\n if (!apiKey) {\n console.error('[openai-agent] Warning: OPENAI_API_KEY is not set. This may be fine for local endpoints like Ollama.');\n }\n\n return { baseUrl, apiKey, model, systemPrompt, maxTokens, temperature };\n}\n", "import crypto from 'node:crypto';\nimport { Session } from './session.js';\n\nexport class SessionManager {\n private readonly sessions = new Map<string, Session>();\n\n createSession(cwd: string): Session {\n const id = crypto.randomUUID();\n const session = new Session(id, cwd);\n this.sessions.set(id, session);\n return session;\n }\n\n getSession(id: string): Session | undefined {\n return this.sessions.get(id);\n }\n\n cancelSession(id: string): boolean {\n const session = this.sessions.get(id);\n if (session) {\n session.cancel();\n return true;\n }\n return false;\n }\n}\n", "export interface HistoryEntry {\n role: 'user' | 'assistant';\n content: string;\n}\n\nexport class Session {\n readonly id: string;\n readonly cwd: string;\n private _history: HistoryEntry[] = [];\n private _abortController: AbortController;\n private _cancelled = false;\n\n constructor(id: string, cwd: string) {\n this.id = id;\n this.cwd = cwd;\n this._abortController = new AbortController();\n }\n\n getAbortSignal(): AbortSignal {\n return this._abortController.signal;\n }\n\n cancel(): void {\n this._cancelled = true;\n this._abortController.abort();\n }\n\n isCancelled(): boolean {\n return this._cancelled;\n }\n\n resetCancellation(): void {\n this._cancelled = false;\n this._abortController = new AbortController();\n }\n\n addHistoryEntry(role: 'user' | 'assistant', content: string): void {\n this._history.push({ role, content });\n }\n\n getHistory(): HistoryEntry[] {\n return [...this._history];\n }\n}\n", "import { SSEEvent } from './types.js';\n\nconst DATA_PREFIX = 'data: ';\nconst DONE_MARKER = '[DONE]';\n\nexport function parseLine(line: string): SSEEvent {\n // Skip empty lines and whitespace-only lines\n if (!line.trim()) {\n return { type: 'skip' };\n }\n\n // Skip SSE comments (lines starting with :)\n if (line.startsWith(':')) {\n return { type: 'skip' };\n }\n\n // Check for data: prefix\n if (!line.startsWith(DATA_PREFIX)) {\n return { type: 'skip' };\n }\n\n const data = line.slice(DATA_PREFIX.length);\n\n // Check for [DONE] marker\n if (data === DONE_MARKER) {\n return { type: 'done' };\n }\n\n // Try to parse JSON\n try {\n const payload = JSON.parse(data);\n return { type: 'data', payload };\n } catch {\n console.error('[openai-agent] Failed to parse SSE JSON:', data);\n return { type: 'skip' };\n }\n}\n", "import { AgentConfig, ChatCompletionResult, ChatCompletionsRequest, OpenAIMessage, SSEChunk } from './types.js';\nimport { parseLine } from './sse-parser.js';\n\nexport function classifyHttpError(status: number, url: string): string {\n if (status === 401 || status === 403) {\n return `Authentication error (HTTP ${status}) calling ${url}. Check your OPENAI_API_KEY.`;\n }\n if (status === 429) {\n return `Rate limit exceeded (HTTP 429) calling ${url}. Please retry later.`;\n }\n if (status >= 500) {\n return `Server error (HTTP ${status}) from ${url}.`;\n }\n return `HTTP error (${status}) from ${url}.`;\n}\n\nexport class ChatCompletionsClient {\n private readonly config: AgentConfig;\n\n constructor(config: AgentConfig) {\n this.config = config;\n }\n\n async streamCompletion(\n messages: OpenAIMessage[],\n signal: AbortSignal,\n onChunk: (text: string) => Promise<void>,\n ): Promise<ChatCompletionResult> {\n const url = `${this.config.baseUrl}/chat/completions`;\n const body: ChatCompletionsRequest = {\n model: this.config.model,\n messages,\n stream: true,\n };\n if (this.config.maxTokens !== undefined) {\n body.max_tokens = this.config.maxTokens;\n }\n if (this.config.temperature !== undefined) {\n body.temperature = this.config.temperature;\n }\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n });\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n return { stopReason: 'cancelled', fullResponse: '' };\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Network error connecting to ${url}: ${message}`);\n }\n\n if (!response.ok) {\n throw new Error(classifyHttpError(response.status, url));\n }\n\n if (!response.body) {\n throw new Error(`No response body from ${url}.`);\n }\n\n let fullResponse = '';\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n // Keep the last potentially incomplete line in the buffer\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n const event = parseLine(line);\n if (event.type === 'done') {\n return { stopReason: 'end_turn', fullResponse };\n }\n if (event.type === 'data') {\n const chunk = event.payload as SSEChunk;\n const content = chunk.choices?.[0]?.delta?.content;\n if (content) {\n fullResponse += content;\n await onChunk(content);\n }\n }\n }\n }\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n return { stopReason: 'cancelled', fullResponse };\n }\n throw error;\n }\n\n return { stopReason: 'end_turn', fullResponse };\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Session ID routing helper for stdio Bus \u2194 ACP messages.\n *\n * Tracks request IDs to restore stdio Bus sessionId on responses,\n * and maps ACP sessionId to stdio Bus sessionId for notifications.\n */\n\ntype JsonRecord = Record<string, unknown>;\n\nexport class SessionIdRouter {\n private readonly requestSessionIdMap = new Map<string | number, string>();\n private readonly acpSessionIdMap = new Map<string, string>();\n\n /**\n * Process a single inbound (stdin) line.\n *\n * - Saves stdio Bus sessionId for request/response correlation\n * - Tracks ACP sessionId \u2194 stdio Bus sessionId mapping when available\n * - Strips stdio Bus sessionId before passing to ACP SDK\n */\n processIncomingLine(line: string): string {\n if (!line.trim()) {\n return line;\n }\n\n try {\n const msg = JSON.parse(line) as JsonRecord;\n const routingSessionId = this.readSessionId(msg.sessionId);\n const hasId = msg.id !== undefined && msg.id !== null;\n\n if (hasId && routingSessionId) {\n this.requestSessionIdMap.set(msg.id as string | number, routingSessionId);\n console.error(`[worker] Saved sessionId=\"${routingSessionId}\" for request id=${msg.id}`);\n }\n\n const paramsSessionId = this.readSessionId((msg.params as JsonRecord | undefined)?.sessionId);\n if (routingSessionId && paramsSessionId) {\n this.setAcpSessionMapping(paramsSessionId, routingSessionId, 'request');\n }\n\n if (hasId && routingSessionId) {\n const { sessionId: _sessionId, ...msgWithoutSession } = msg;\n return JSON.stringify(msgWithoutSession);\n }\n\n return line;\n } catch {\n return line;\n }\n }\n\n /**\n * Process a single outbound (stdout) line.\n *\n * - Restores stdio Bus sessionId on responses using request mapping\n * - Maps ACP sessionId to stdio Bus sessionId for notifications\n */\n processOutgoingLine(line: string): string {\n if (!line.trim()) {\n return line;\n }\n\n try {\n const msg = JSON.parse(line) as JsonRecord;\n const hasId = msg.id !== undefined && msg.id !== null;\n\n if (hasId && this.requestSessionIdMap.has(msg.id as string | number)) {\n const routingSessionId = this.requestSessionIdMap.get(msg.id as string | number);\n this.requestSessionIdMap.delete(msg.id as string | number);\n\n if (routingSessionId) {\n const resultSessionId = this.readSessionId(\n (msg.result as JsonRecord | undefined)?.sessionId,\n );\n if (resultSessionId) {\n this.setAcpSessionMapping(resultSessionId, routingSessionId, 'response');\n }\n\n const msgWithSession = { ...msg, sessionId: routingSessionId };\n console.error(\n `[worker] Restored sessionId=\"${routingSessionId}\" for response id=${msg.id}`,\n );\n return JSON.stringify(msgWithSession);\n }\n }\n\n if (!hasId && !this.readSessionId(msg.sessionId)) {\n const paramsSessionId = this.readSessionId(\n (msg.params as JsonRecord | undefined)?.sessionId,\n );\n if (paramsSessionId) {\n const routingSessionId = this.acpSessionIdMap.get(paramsSessionId);\n if (routingSessionId) {\n const msgWithSession = { ...msg, sessionId: routingSessionId };\n return JSON.stringify(msgWithSession);\n }\n }\n }\n\n return line;\n } catch {\n return line;\n }\n }\n\n private readSessionId(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n }\n\n private setAcpSessionMapping(\n acpSessionId: string,\n routingSessionId: string,\n source: 'request' | 'response',\n ): void {\n const existing = this.acpSessionIdMap.get(acpSessionId);\n if (existing === routingSessionId) {\n return;\n }\n\n this.acpSessionIdMap.set(acpSessionId, routingSessionId);\n console.error(\n `[worker] Mapped ACP sessionId=\"${acpSessionId}\" ` +\n `to routing sessionId=\"${routingSessionId}\" (${source})`,\n );\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["connection"]
7
+ }
@@ -8,6 +8,8 @@ export interface LauncherConfig {
8
8
  apiKeysPath: string;
9
9
  /** Agent shutdown timeout in seconds (default: 5) */
10
10
  shutdownTimeoutSec: number;
11
+ /** Path to custom agents JSON file (optional, loaded via --custom-agents CLI arg) */
12
+ customAgentsPath?: string;
11
13
  }
12
14
  /**
13
15
  * Default configuration values.
@@ -106,4 +106,49 @@ export declare class RegistryIndex implements IRegistryIndex {
106
106
  * @returns The parsed registry or null if not yet fetched
107
107
  */
108
108
  getRegistry(): Registry | null;
109
+ /**
110
+ * Merge custom agents into the registry.
111
+ *
112
+ * Custom agents take precedence over remote registry agents with the same ID.
113
+ * This allows users to override or extend the official ACP Registry with
114
+ * locally-defined agents (e.g., AWS Bedrock, custom internal agents).
115
+ *
116
+ * @param agents - Array of custom RegistryAgent entries to merge
117
+ */
118
+ mergeCustomAgents(agents: RegistryAgent[]): void;
119
+ }
120
+ /**
121
+ * Error thrown when custom agents file cannot be loaded.
122
+ */
123
+ export declare class CustomAgentsLoadError extends Error {
124
+ readonly cause?: Error | undefined;
125
+ constructor(message: string, cause?: Error | undefined);
109
126
  }
127
+ /**
128
+ * Load and validate custom agents from a JSON file.
129
+ *
130
+ * The file must contain a JSON object with an "agents" array field.
131
+ * Each agent entry is validated using the same rules as the remote registry.
132
+ *
133
+ * Expected file format:
134
+ * ```json
135
+ * {
136
+ * "agents": [
137
+ * {
138
+ * "id": "my-custom-agent",
139
+ * "name": "My Custom Agent",
140
+ * "version": "1.0.0",
141
+ * "distribution": {
142
+ * "npx": { "package": "@my-org/my-agent@latest" }
143
+ * }
144
+ * }
145
+ * ]
146
+ * }
147
+ * ```
148
+ *
149
+ * @param filePath - Path to the custom agents JSON file
150
+ * @returns Array of validated RegistryAgent entries
151
+ * @throws CustomAgentsLoadError if file cannot be read or parsed
152
+ * @throws RegistryParseError if agent entries are malformed
153
+ */
154
+ export declare function loadCustomAgents(filePath: string): RegistryAgent[];
@@ -0,0 +1,26 @@
1
+ import type { Agent, AgentSideConnection, AuthenticateRequest, AuthenticateResponse, CancelNotification, ContentBlock, InitializeRequest, InitializeResponse, LoadSessionRequest, LoadSessionResponse, NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse } from '@agentclientprotocol/sdk';
2
+ import type { OpenAIMessage } from './types.js';
3
+ /**
4
+ * Convert ACP content blocks to a single user message string.
5
+ */
6
+ export declare function convertContentBlocks(blocks: ContentBlock[]): string;
7
+ /**
8
+ * Build the full messages array for the Chat Completions API.
9
+ */
10
+ export declare function buildMessages(systemPrompt: string | undefined, history: Array<{
11
+ role: 'user' | 'assistant';
12
+ content: string;
13
+ }>, userMessage: string): OpenAIMessage[];
14
+ export declare class OpenAIAgent implements Agent {
15
+ private readonly connection;
16
+ private readonly sessionManager;
17
+ private readonly client;
18
+ private readonly config;
19
+ constructor(connection: AgentSideConnection);
20
+ initialize(_params: InitializeRequest): Promise<InitializeResponse>;
21
+ newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
22
+ loadSession(_params: LoadSessionRequest): Promise<LoadSessionResponse>;
23
+ authenticate(_params: AuthenticateRequest): Promise<AuthenticateResponse | void>;
24
+ prompt(params: PromptRequest): Promise<PromptResponse>;
25
+ cancel(params: CancelNotification): Promise<void>;
26
+ }
@@ -0,0 +1,7 @@
1
+ import { AgentConfig, ChatCompletionResult, OpenAIMessage } from './types.js';
2
+ export declare function classifyHttpError(status: number, url: string): string;
3
+ export declare class ChatCompletionsClient {
4
+ private readonly config;
5
+ constructor(config: AgentConfig);
6
+ streamCompletion(messages: OpenAIMessage[], signal: AbortSignal, onChunk: (text: string) => Promise<void>): Promise<ChatCompletionResult>;
7
+ }
@@ -0,0 +1,2 @@
1
+ import { AgentConfig } from './types.js';
2
+ export declare function loadConfig(): AgentConfig;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OpenAI Agent Worker for stdio Bus kernel
3
+ *
4
+ * This worker implements the Agent Client Protocol (ACP) and bridges
5
+ * to any OpenAI Chat Completions API-compatible endpoint.
6
+ *
7
+ * It runs as a child process of stdio Bus kernel, communicating via stdin/stdout NDJSON.
8
+ *
9
+ * @module index
10
+ */
11
+ export {};
@@ -0,0 +1,21 @@
1
+ export declare class SessionIdRouter {
2
+ private readonly requestSessionIdMap;
3
+ private readonly acpSessionIdMap;
4
+ /**
5
+ * Process a single inbound (stdin) line.
6
+ *
7
+ * - Saves stdio Bus sessionId for request/response correlation
8
+ * - Tracks ACP sessionId ↔ stdio Bus sessionId mapping when available
9
+ * - Strips stdio Bus sessionId before passing to ACP SDK
10
+ */
11
+ processIncomingLine(line: string): string;
12
+ /**
13
+ * Process a single outbound (stdout) line.
14
+ *
15
+ * - Restores stdio Bus sessionId on responses using request mapping
16
+ * - Maps ACP sessionId to stdio Bus sessionId for notifications
17
+ */
18
+ processOutgoingLine(line: string): string;
19
+ private readSessionId;
20
+ private setAcpSessionMapping;
21
+ }
@@ -0,0 +1,7 @@
1
+ import { Session } from './session.js';
2
+ export declare class SessionManager {
3
+ private readonly sessions;
4
+ createSession(cwd: string): Session;
5
+ getSession(id: string): Session | undefined;
6
+ cancelSession(id: string): boolean;
7
+ }
@@ -0,0 +1,18 @@
1
+ export interface HistoryEntry {
2
+ role: 'user' | 'assistant';
3
+ content: string;
4
+ }
5
+ export declare class Session {
6
+ readonly id: string;
7
+ readonly cwd: string;
8
+ private _history;
9
+ private _abortController;
10
+ private _cancelled;
11
+ constructor(id: string, cwd: string);
12
+ getAbortSignal(): AbortSignal;
13
+ cancel(): void;
14
+ isCancelled(): boolean;
15
+ resetCancellation(): void;
16
+ addHistoryEntry(role: 'user' | 'assistant', content: string): void;
17
+ getHistory(): HistoryEntry[];
18
+ }
@@ -0,0 +1,2 @@
1
+ import { SSEEvent } from './types.js';
2
+ export declare function parseLine(line: string): SSEEvent;
@@ -0,0 +1,43 @@
1
+ export interface OpenAIMessage {
2
+ role: 'system' | 'user' | 'assistant';
3
+ content: string;
4
+ }
5
+ export interface ChatCompletionsRequest {
6
+ model: string;
7
+ messages: OpenAIMessage[];
8
+ stream: true;
9
+ max_tokens?: number;
10
+ temperature?: number;
11
+ }
12
+ export interface SSEChunk {
13
+ id?: string;
14
+ object?: string;
15
+ choices: Array<{
16
+ index: number;
17
+ delta: {
18
+ role?: string;
19
+ content?: string;
20
+ };
21
+ finish_reason: string | null;
22
+ }>;
23
+ }
24
+ export interface AgentConfig {
25
+ baseUrl: string;
26
+ apiKey: string;
27
+ model: string;
28
+ systemPrompt?: string;
29
+ maxTokens?: number;
30
+ temperature?: number;
31
+ }
32
+ export interface ChatCompletionResult {
33
+ stopReason: 'end_turn' | 'cancelled';
34
+ fullResponse: string;
35
+ }
36
+ export type SSEEvent = {
37
+ type: 'data';
38
+ payload: unknown;
39
+ } | {
40
+ type: 'done';
41
+ } | {
42
+ type: 'skip';
43
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stdiobus/workers-registry",
3
- "version": "1.4.11",
3
+ "version": "1.4.12",
4
4
  "description": "Worker implementations for stdio Bus kernel - ACP, MCP, and protocol bridges",
5
5
  "type": "module",
6
6
  "main": "./out/dist/workers-registry/acp-registry/index.js",
@@ -44,6 +44,10 @@
44
44
  "types": "./out/tsc/workers-registry/mcp-echo-server/index.d.ts"
45
45
  },
46
46
  "./workers/mcp-to-acp-proxy": "./out/dist/workers-registry/mcp-to-acp-proxy/proxy.js",
47
+ "./workers/openai-agent": {
48
+ "import": "./out/dist/workers-registry/openai-agent/index.js",
49
+ "types": "./out/tsc/workers-registry/openai-agent/src/index.d.ts"
50
+ },
47
51
  "./package.json": "./package.json"
48
52
  },
49
53
  "bin": {