axexec 1.0.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.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +196 -0
  3. package/bin/axexec +17 -0
  4. package/dist/agents/claude-code/adapter.d.ts +6 -0
  5. package/dist/agents/claude-code/adapter.js +71 -0
  6. package/dist/agents/claude-code/parse-event.d.ts +16 -0
  7. package/dist/agents/claude-code/parse-event.js +185 -0
  8. package/dist/agents/claude-code/types.d.ts +180 -0
  9. package/dist/agents/claude-code/types.js +112 -0
  10. package/dist/agents/codex/adapter.d.ts +6 -0
  11. package/dist/agents/codex/adapter.js +62 -0
  12. package/dist/agents/codex/item-parsers.d.ts +17 -0
  13. package/dist/agents/codex/item-parsers.js +126 -0
  14. package/dist/agents/codex/parse-event.d.ts +38 -0
  15. package/dist/agents/codex/parse-event.js +141 -0
  16. package/dist/agents/codex/types.d.ts +407 -0
  17. package/dist/agents/codex/types.js +188 -0
  18. package/dist/agents/copilot/adapter.d.ts +11 -0
  19. package/dist/agents/copilot/adapter.js +81 -0
  20. package/dist/agents/copilot/parse-event.d.ts +20 -0
  21. package/dist/agents/copilot/parse-event.js +137 -0
  22. package/dist/agents/copilot/stream-session.d.ts +10 -0
  23. package/dist/agents/copilot/stream-session.js +120 -0
  24. package/dist/agents/copilot/tail-file.d.ts +11 -0
  25. package/dist/agents/copilot/tail-file.js +52 -0
  26. package/dist/agents/copilot/transform-event.d.ts +19 -0
  27. package/dist/agents/copilot/transform-event.js +100 -0
  28. package/dist/agents/copilot/types.d.ts +320 -0
  29. package/dist/agents/copilot/types.js +220 -0
  30. package/dist/agents/copilot/watch-session.d.ts +26 -0
  31. package/dist/agents/copilot/watch-session.js +186 -0
  32. package/dist/agents/gemini/adapter.d.ts +6 -0
  33. package/dist/agents/gemini/adapter.js +78 -0
  34. package/dist/agents/gemini/parse-event.d.ts +18 -0
  35. package/dist/agents/gemini/parse-event.js +144 -0
  36. package/dist/agents/gemini/types.d.ts +162 -0
  37. package/dist/agents/gemini/types.js +103 -0
  38. package/dist/agents/opencode/adapter.d.ts +16 -0
  39. package/dist/agents/opencode/adapter.js +142 -0
  40. package/dist/agents/opencode/cleanup-session.d.ts +17 -0
  41. package/dist/agents/opencode/cleanup-session.js +41 -0
  42. package/dist/agents/opencode/create-session-start-event.d.ts +18 -0
  43. package/dist/agents/opencode/create-session-start-event.js +24 -0
  44. package/dist/agents/opencode/detect-empty-session.d.ts +25 -0
  45. package/dist/agents/opencode/detect-empty-session.js +42 -0
  46. package/dist/agents/opencode/map-error-to-event.d.ts +10 -0
  47. package/dist/agents/opencode/map-error-to-event.js +55 -0
  48. package/dist/agents/opencode/parse-message-part.d.ts +24 -0
  49. package/dist/agents/opencode/parse-message-part.js +112 -0
  50. package/dist/agents/opencode/parse-sse-event.d.ts +23 -0
  51. package/dist/agents/opencode/parse-sse-event.js +125 -0
  52. package/dist/agents/opencode/process-sse-events.d.ts +24 -0
  53. package/dist/agents/opencode/process-sse-events.js +66 -0
  54. package/dist/agents/opencode/server-types.d.ts +509 -0
  55. package/dist/agents/opencode/server-types.js +293 -0
  56. package/dist/agents/opencode/session-api.d.ts +56 -0
  57. package/dist/agents/opencode/session-api.js +151 -0
  58. package/dist/agents/opencode/spawn-server.d.ts +29 -0
  59. package/dist/agents/opencode/spawn-server.js +95 -0
  60. package/dist/agents/opencode/sse-client.d.ts +33 -0
  61. package/dist/agents/opencode/sse-client.js +145 -0
  62. package/dist/agents/registry.d.ts +15 -0
  63. package/dist/agents/registry.js +24 -0
  64. package/dist/cli.d.ts +15 -0
  65. package/dist/cli.js +119 -0
  66. package/dist/credentials/get-credential-environment.d.ts +13 -0
  67. package/dist/credentials/get-credential-environment.js +46 -0
  68. package/dist/credentials/install-credentials.d.ts +27 -0
  69. package/dist/credentials/install-credentials.js +102 -0
  70. package/dist/credentials/save-json-file.d.ts +11 -0
  71. package/dist/credentials/save-json-file.js +21 -0
  72. package/dist/credentials/types.d.ts +24 -0
  73. package/dist/credentials/types.js +4 -0
  74. package/dist/credentials/write-agent-credentials.d.ts +36 -0
  75. package/dist/credentials/write-agent-credentials.js +91 -0
  76. package/dist/determine-session-success.d.ts +15 -0
  77. package/dist/determine-session-success.js +25 -0
  78. package/dist/format-event-tsv.d.ts +23 -0
  79. package/dist/format-event-tsv.js +136 -0
  80. package/dist/parse-credentials.d.ts +13 -0
  81. package/dist/parse-credentials.js +63 -0
  82. package/dist/parse-iso-timestamp.d.ts +21 -0
  83. package/dist/parse-iso-timestamp.js +37 -0
  84. package/dist/resolve-binary.d.ts +29 -0
  85. package/dist/resolve-binary.js +46 -0
  86. package/dist/resolve-output-mode.d.ts +39 -0
  87. package/dist/resolve-output-mode.js +39 -0
  88. package/dist/run-agent.d.ts +32 -0
  89. package/dist/run-agent.js +146 -0
  90. package/dist/stream-agent.d.ts +20 -0
  91. package/dist/stream-agent.js +207 -0
  92. package/dist/types/adapter.d.ts +101 -0
  93. package/dist/types/adapter.js +14 -0
  94. package/dist/types/events.d.ts +82 -0
  95. package/dist/types/events.js +13 -0
  96. package/dist/types/options.d.ts +41 -0
  97. package/dist/types/options.js +4 -0
  98. package/dist/validate-agent.d.ts +20 -0
  99. package/dist/validate-agent.js +50 -0
  100. package/dist/write-event.d.ts +23 -0
  101. package/dist/write-event.js +43 -0
  102. package/package.json +79 -0
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Raw event types from Claude Code's JSONL stream.
3
+ *
4
+ * These types represent the raw output from:
5
+ * `claude -p "..." --output-format stream-json --verbose`
6
+ *
7
+ * The parser transforms these into normalized {@link AxexecEvent} types.
8
+ */
9
+ import { z } from "zod";
10
+ /** System event - emitted at startup with session info */
11
+ declare const ClaudeSystemEvent: z.ZodObject<{
12
+ type: z.ZodLiteral<"system">;
13
+ subtype: z.ZodOptional<z.ZodString>;
14
+ timestamp: z.ZodOptional<z.ZodString>;
15
+ session_id: z.ZodOptional<z.ZodString>;
16
+ model: z.ZodOptional<z.ZodString>;
17
+ tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
18
+ }, z.core.$strip>;
19
+ type ClaudeSystemEvent = z.infer<typeof ClaudeSystemEvent>;
20
+ /** Assistant event - model responses and tool calls */
21
+ declare const ClaudeAssistantEvent: z.ZodObject<{
22
+ type: z.ZodLiteral<"assistant">;
23
+ subtype: z.ZodOptional<z.ZodEnum<{
24
+ content: "content";
25
+ tool_use: "tool_use";
26
+ thinking: "thinking";
27
+ }>>;
28
+ timestamp: z.ZodOptional<z.ZodString>;
29
+ message: z.ZodOptional<z.ZodObject<{
30
+ id: z.ZodOptional<z.ZodString>;
31
+ role: z.ZodOptional<z.ZodString>;
32
+ content: z.ZodOptional<z.ZodArray<z.ZodObject<{
33
+ type: z.ZodString;
34
+ text: z.ZodOptional<z.ZodString>;
35
+ id: z.ZodOptional<z.ZodString>;
36
+ name: z.ZodOptional<z.ZodString>;
37
+ input: z.ZodOptional<z.ZodUnknown>;
38
+ }, z.core.$strip>>>;
39
+ model: z.ZodOptional<z.ZodString>;
40
+ usage: z.ZodOptional<z.ZodObject<{
41
+ input_tokens: z.ZodOptional<z.ZodNumber>;
42
+ output_tokens: z.ZodOptional<z.ZodNumber>;
43
+ }, z.core.$strip>>;
44
+ }, z.core.$strip>>;
45
+ content_block: z.ZodOptional<z.ZodObject<{
46
+ type: z.ZodString;
47
+ text: z.ZodOptional<z.ZodString>;
48
+ id: z.ZodOptional<z.ZodString>;
49
+ name: z.ZodOptional<z.ZodString>;
50
+ input: z.ZodOptional<z.ZodUnknown>;
51
+ }, z.core.$strip>>;
52
+ }, z.core.$strip>;
53
+ type ClaudeAssistantEvent = z.infer<typeof ClaudeAssistantEvent>;
54
+ /** User event - typically tool results */
55
+ declare const ClaudeUserEvent: z.ZodObject<{
56
+ type: z.ZodLiteral<"user">;
57
+ timestamp: z.ZodOptional<z.ZodString>;
58
+ message: z.ZodOptional<z.ZodObject<{
59
+ role: z.ZodOptional<z.ZodString>;
60
+ content: z.ZodOptional<z.ZodArray<z.ZodObject<{
61
+ type: z.ZodEnum<{
62
+ text: "text";
63
+ tool_result: "tool_result";
64
+ }>;
65
+ tool_use_id: z.ZodOptional<z.ZodString>;
66
+ content: z.ZodOptional<z.ZodUnknown>;
67
+ text: z.ZodOptional<z.ZodString>;
68
+ }, z.core.$strip>>>;
69
+ }, z.core.$strip>>;
70
+ tool_use_result: z.ZodOptional<z.ZodObject<{
71
+ type: z.ZodOptional<z.ZodString>;
72
+ status: z.ZodOptional<z.ZodString>;
73
+ }, z.core.$loose>>;
74
+ }, z.core.$strip>;
75
+ type ClaudeUserEvent = z.infer<typeof ClaudeUserEvent>;
76
+ /** Result event - emitted when task completes */
77
+ declare const ClaudeResultEvent: z.ZodObject<{
78
+ type: z.ZodLiteral<"result">;
79
+ subtype: z.ZodEnum<{
80
+ success: "success";
81
+ error: "error";
82
+ cancelled: "cancelled";
83
+ }>;
84
+ timestamp: z.ZodOptional<z.ZodString>;
85
+ message: z.ZodOptional<z.ZodString>;
86
+ error: z.ZodOptional<z.ZodString>;
87
+ duration_ms: z.ZodOptional<z.ZodNumber>;
88
+ total_cost_usd: z.ZodOptional<z.ZodNumber>;
89
+ usage: z.ZodOptional<z.ZodObject<{
90
+ input_tokens: z.ZodOptional<z.ZodNumber>;
91
+ output_tokens: z.ZodOptional<z.ZodNumber>;
92
+ }, z.core.$strip>>;
93
+ }, z.core.$strip>;
94
+ type ClaudeResultEvent = z.infer<typeof ClaudeResultEvent>;
95
+ /** Union of all Claude Code event types */
96
+ declare const ClaudeEvent: z.ZodDiscriminatedUnion<[z.ZodObject<{
97
+ type: z.ZodLiteral<"system">;
98
+ subtype: z.ZodOptional<z.ZodString>;
99
+ timestamp: z.ZodOptional<z.ZodString>;
100
+ session_id: z.ZodOptional<z.ZodString>;
101
+ model: z.ZodOptional<z.ZodString>;
102
+ tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
103
+ }, z.core.$strip>, z.ZodObject<{
104
+ type: z.ZodLiteral<"assistant">;
105
+ subtype: z.ZodOptional<z.ZodEnum<{
106
+ content: "content";
107
+ tool_use: "tool_use";
108
+ thinking: "thinking";
109
+ }>>;
110
+ timestamp: z.ZodOptional<z.ZodString>;
111
+ message: z.ZodOptional<z.ZodObject<{
112
+ id: z.ZodOptional<z.ZodString>;
113
+ role: z.ZodOptional<z.ZodString>;
114
+ content: z.ZodOptional<z.ZodArray<z.ZodObject<{
115
+ type: z.ZodString;
116
+ text: z.ZodOptional<z.ZodString>;
117
+ id: z.ZodOptional<z.ZodString>;
118
+ name: z.ZodOptional<z.ZodString>;
119
+ input: z.ZodOptional<z.ZodUnknown>;
120
+ }, z.core.$strip>>>;
121
+ model: z.ZodOptional<z.ZodString>;
122
+ usage: z.ZodOptional<z.ZodObject<{
123
+ input_tokens: z.ZodOptional<z.ZodNumber>;
124
+ output_tokens: z.ZodOptional<z.ZodNumber>;
125
+ }, z.core.$strip>>;
126
+ }, z.core.$strip>>;
127
+ content_block: z.ZodOptional<z.ZodObject<{
128
+ type: z.ZodString;
129
+ text: z.ZodOptional<z.ZodString>;
130
+ id: z.ZodOptional<z.ZodString>;
131
+ name: z.ZodOptional<z.ZodString>;
132
+ input: z.ZodOptional<z.ZodUnknown>;
133
+ }, z.core.$strip>>;
134
+ }, z.core.$strip>, z.ZodObject<{
135
+ type: z.ZodLiteral<"user">;
136
+ timestamp: z.ZodOptional<z.ZodString>;
137
+ message: z.ZodOptional<z.ZodObject<{
138
+ role: z.ZodOptional<z.ZodString>;
139
+ content: z.ZodOptional<z.ZodArray<z.ZodObject<{
140
+ type: z.ZodEnum<{
141
+ text: "text";
142
+ tool_result: "tool_result";
143
+ }>;
144
+ tool_use_id: z.ZodOptional<z.ZodString>;
145
+ content: z.ZodOptional<z.ZodUnknown>;
146
+ text: z.ZodOptional<z.ZodString>;
147
+ }, z.core.$strip>>>;
148
+ }, z.core.$strip>>;
149
+ tool_use_result: z.ZodOptional<z.ZodObject<{
150
+ type: z.ZodOptional<z.ZodString>;
151
+ status: z.ZodOptional<z.ZodString>;
152
+ }, z.core.$loose>>;
153
+ }, z.core.$strip>, z.ZodObject<{
154
+ type: z.ZodLiteral<"result">;
155
+ subtype: z.ZodEnum<{
156
+ success: "success";
157
+ error: "error";
158
+ cancelled: "cancelled";
159
+ }>;
160
+ timestamp: z.ZodOptional<z.ZodString>;
161
+ message: z.ZodOptional<z.ZodString>;
162
+ error: z.ZodOptional<z.ZodString>;
163
+ duration_ms: z.ZodOptional<z.ZodNumber>;
164
+ total_cost_usd: z.ZodOptional<z.ZodNumber>;
165
+ usage: z.ZodOptional<z.ZodObject<{
166
+ input_tokens: z.ZodOptional<z.ZodNumber>;
167
+ output_tokens: z.ZodOptional<z.ZodNumber>;
168
+ }, z.core.$strip>>;
169
+ }, z.core.$strip>], "type">;
170
+ type ClaudeEvent = z.infer<typeof ClaudeEvent>;
171
+ /** Type guard for system events */
172
+ declare function isSystemEvent(event: ClaudeEvent): event is ClaudeSystemEvent;
173
+ /** Type guard for assistant events */
174
+ declare function isAssistantEvent(event: ClaudeEvent): event is ClaudeAssistantEvent;
175
+ /** Type guard for user events */
176
+ declare function isUserEvent(event: ClaudeEvent): event is ClaudeUserEvent;
177
+ /** Type guard for result events */
178
+ declare function isResultEvent(event: ClaudeEvent): event is ClaudeResultEvent;
179
+ export { ClaudeEvent, isAssistantEvent, isResultEvent, isSystemEvent, isUserEvent, };
180
+ export type { ClaudeAssistantEvent, ClaudeResultEvent, ClaudeSystemEvent, ClaudeUserEvent, };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Raw event types from Claude Code's JSONL stream.
3
+ *
4
+ * These types represent the raw output from:
5
+ * `claude -p "..." --output-format stream-json --verbose`
6
+ *
7
+ * The parser transforms these into normalized {@link AxexecEvent} types.
8
+ */
9
+ import { z } from "zod";
10
+ /** Content block schema - used in assistant events */
11
+ const ClaudeContentBlock = z.object({
12
+ type: z.string(),
13
+ text: z.string().optional(),
14
+ id: z.string().optional(),
15
+ name: z.string().optional(),
16
+ input: z.unknown().optional(),
17
+ });
18
+ /** System event - emitted at startup with session info */
19
+ const ClaudeSystemEvent = z.object({
20
+ type: z.literal("system"),
21
+ subtype: z.string().optional(),
22
+ timestamp: z.string().optional(),
23
+ session_id: z.string().optional(),
24
+ model: z.string().optional(),
25
+ tools: z.array(z.string()).optional(),
26
+ });
27
+ /** Assistant event - model responses and tool calls */
28
+ const ClaudeAssistantEvent = z.object({
29
+ type: z.literal("assistant"),
30
+ subtype: z.enum(["content", "tool_use", "thinking"]).optional(),
31
+ timestamp: z.string().optional(),
32
+ message: z
33
+ .object({
34
+ id: z.string().optional(),
35
+ role: z.string().optional(),
36
+ content: z.array(ClaudeContentBlock).optional(),
37
+ model: z.string().optional(),
38
+ usage: z
39
+ .object({
40
+ input_tokens: z.number().optional(),
41
+ output_tokens: z.number().optional(),
42
+ })
43
+ .optional(),
44
+ })
45
+ .optional(),
46
+ content_block: ClaudeContentBlock.optional(),
47
+ });
48
+ /** Content block in user messages (tool results) */
49
+ const ClaudeUserContentBlock = z.object({
50
+ type: z.enum(["tool_result", "text"]),
51
+ tool_use_id: z.string().optional(),
52
+ content: z.unknown().optional(),
53
+ text: z.string().optional(),
54
+ });
55
+ /** User event - typically tool results */
56
+ const ClaudeUserEvent = z.object({
57
+ type: z.literal("user"),
58
+ timestamp: z.string().optional(),
59
+ message: z
60
+ .object({
61
+ role: z.string().optional(),
62
+ content: z.array(ClaudeUserContentBlock).optional(),
63
+ })
64
+ .optional(),
65
+ // Tool use result metadata (when present)
66
+ tool_use_result: z
67
+ .looseObject({
68
+ type: z.string().optional(),
69
+ status: z.string().optional(),
70
+ })
71
+ .optional(),
72
+ });
73
+ /** Result event - emitted when task completes */
74
+ const ClaudeResultEvent = z.object({
75
+ type: z.literal("result"),
76
+ subtype: z.enum(["success", "error", "cancelled"]),
77
+ timestamp: z.string().optional(),
78
+ message: z.string().optional(),
79
+ error: z.string().optional(),
80
+ duration_ms: z.number().optional(),
81
+ total_cost_usd: z.number().optional(),
82
+ usage: z
83
+ .object({
84
+ input_tokens: z.number().optional(),
85
+ output_tokens: z.number().optional(),
86
+ })
87
+ .optional(),
88
+ });
89
+ /** Union of all Claude Code event types */
90
+ const ClaudeEvent = z.discriminatedUnion("type", [
91
+ ClaudeSystemEvent,
92
+ ClaudeAssistantEvent,
93
+ ClaudeUserEvent,
94
+ ClaudeResultEvent,
95
+ ]);
96
+ /** Type guard for system events */
97
+ function isSystemEvent(event) {
98
+ return event.type === "system";
99
+ }
100
+ /** Type guard for assistant events */
101
+ function isAssistantEvent(event) {
102
+ return event.type === "assistant";
103
+ }
104
+ /** Type guard for user events */
105
+ function isUserEvent(event) {
106
+ return event.type === "user";
107
+ }
108
+ /** Type guard for result events */
109
+ function isResultEvent(event) {
110
+ return event.type === "result";
111
+ }
112
+ export { ClaudeEvent, isAssistantEvent, isResultEvent, isSystemEvent, isUserEvent, };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Codex adapter implementation.
3
+ *
4
+ * Implements the {@link AgentAdapter} interface for Codex CLI.
5
+ */
6
+ export {};
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Codex adapter implementation.
3
+ *
4
+ * Implements the {@link AgentAdapter} interface for Codex CLI.
5
+ */
6
+ import { determineSessionSuccess } from "../../determine-session-success.js";
7
+ import { resolveBinary } from "../../resolve-binary.js";
8
+ import { registerAdapter } from "../registry.js";
9
+ import { createCodexParser } from "./parse-event.js";
10
+ const CODEX_INFO = {
11
+ id: "codex",
12
+ name: "Codex",
13
+ package: "@openai/codex",
14
+ };
15
+ /**
16
+ * Prepares the command to spawn Codex CLI.
17
+ */
18
+ function prepareCommand(options) {
19
+ // Build CLI arguments.
20
+ // Enable network access in sandbox: Codex blocks network by default in
21
+ // workspace-write mode, but other agents (Gemini, OpenCode) allow it by
22
+ // default. For unified behavior, enable it. This keeps file-level sandbox
23
+ // protections while allowing gh, curl, etc.
24
+ const cliArguments = [
25
+ "exec",
26
+ "--json",
27
+ "--full-auto",
28
+ "-c",
29
+ "sandbox_workspace_write.network_access=true",
30
+ // Add model flag if specified (o4-mini, gpt-5.1-codex, gpt-5.2, etc.)
31
+ ...(options.model ? ["--model", options.model] : []),
32
+ // Use "--" to prevent prompt from being misinterpreted as a flag
33
+ "--",
34
+ options.prompt,
35
+ ];
36
+ const environment = {};
37
+ // Pass through authentication environment variable.
38
+ // If not set, Codex CLI will handle auth errors on its own.
39
+ if (process.env["OPENAI_API_KEY"]) {
40
+ environment["OPENAI_API_KEY"] = process.env["OPENAI_API_KEY"];
41
+ }
42
+ const bin = resolveBinary({
43
+ name: "codex",
44
+ command: "codex",
45
+ environmentVariable: "AXEXEC_CODEX_PATH",
46
+ installHint: "npm install -g @openai/codex",
47
+ });
48
+ return {
49
+ bin,
50
+ args: cliArguments,
51
+ env: environment,
52
+ };
53
+ }
54
+ /** Codex adapter */
55
+ const codexAdapter = {
56
+ info: () => CODEX_INFO,
57
+ prepareCommand,
58
+ createParser: (options) => createCodexParser(options?.model),
59
+ isSuccess: determineSessionSuccess,
60
+ };
61
+ // Self-register on import
62
+ registerAdapter(codexAdapter);
@@ -0,0 +1,17 @@
1
+ import type { AxexecEvent } from "../../types/events.js";
2
+ import type { CodexItemCompletedEvent, CodexItemStartedEvent } from "./types.js";
3
+ /**
4
+ * Parses item.started events.
5
+ *
6
+ * Only handles tool calls (command_execution, mcp_tool_call, file_change).
7
+ * Codex emits reasoning and agent_message only as item.completed, not item.started.
8
+ */
9
+ export declare function parseItemStartedEvent(event: CodexItemStartedEvent, toolContext: Map<string, {
10
+ tool: string;
11
+ input: unknown;
12
+ }>, timestamp: number): AxexecEvent | undefined;
13
+ /** Parses item.completed events */
14
+ export declare function parseItemCompletedEvent(event: CodexItemCompletedEvent, toolContext: Map<string, {
15
+ tool: string;
16
+ input: unknown;
17
+ }>, timestamp: number): AxexecEvent | undefined;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Parses item.started events.
3
+ *
4
+ * Only handles tool calls (command_execution, mcp_tool_call, file_change).
5
+ * Codex emits reasoning and agent_message only as item.completed, not item.started.
6
+ */
7
+ export function parseItemStartedEvent(event, toolContext, timestamp) {
8
+ const item = event.item;
9
+ if (item.type === "command_execution") {
10
+ const tool = "shell";
11
+ const input = { command: item.command };
12
+ toolContext.set(item.id, { tool, input });
13
+ return {
14
+ type: "tool.call",
15
+ callId: item.id,
16
+ tool,
17
+ input,
18
+ timestamp,
19
+ };
20
+ }
21
+ if (item.type === "mcp_tool_call") {
22
+ const tool = `${item.server}:${item.tool}`;
23
+ const input = item.arguments;
24
+ toolContext.set(item.id, { tool, input });
25
+ return {
26
+ type: "tool.call",
27
+ callId: item.id,
28
+ tool,
29
+ input,
30
+ timestamp,
31
+ };
32
+ }
33
+ if (item.type === "file_change") {
34
+ const tool = "file_change";
35
+ const input = item.changes;
36
+ toolContext.set(item.id, { tool, input });
37
+ return {
38
+ type: "tool.call",
39
+ callId: item.id,
40
+ tool,
41
+ input,
42
+ timestamp,
43
+ };
44
+ }
45
+ // Ignore other item types like web_search, todo_list, error (handled elsewhere or ignored)
46
+ return undefined;
47
+ }
48
+ /** Parses item.completed events */
49
+ export function parseItemCompletedEvent(event, toolContext, timestamp) {
50
+ const item = event.item;
51
+ // Handle reasoning and agent_message items as progress events.
52
+ // Codex emits these as item.completed, not item.started.
53
+ if (item.type === "reasoning") {
54
+ return {
55
+ type: "agent.reasoning",
56
+ content: item.text,
57
+ timestamp,
58
+ };
59
+ }
60
+ if (item.type === "agent_message") {
61
+ return {
62
+ type: "agent.message",
63
+ content: item.text,
64
+ timestamp,
65
+ };
66
+ }
67
+ // Only handle tool completions below
68
+ if (item.type !== "command_execution" &&
69
+ item.type !== "mcp_tool_call" &&
70
+ item.type !== "file_change") {
71
+ return undefined;
72
+ }
73
+ const context = toolContext.get(item.id);
74
+ // If an item.completed event arrives without a corresponding item.started event,
75
+ // we won't have context, so deriveToolName reconstructs the tool name from the item type.
76
+ const toolName = context?.tool ?? deriveToolName(item.type, item);
77
+ // Clean up map
78
+ toolContext.delete(item.id);
79
+ let output;
80
+ switch (item.type) {
81
+ case "command_execution": {
82
+ output = item.aggregated_output;
83
+ break;
84
+ }
85
+ case "mcp_tool_call": {
86
+ output = item.result ?? item.error;
87
+ break;
88
+ }
89
+ case "file_change": {
90
+ // File change doesn't usually have an output other than success/fail status
91
+ // We can use the changes as output or just "File changes applied"
92
+ output = item.changes;
93
+ break;
94
+ }
95
+ }
96
+ return {
97
+ type: "tool.result",
98
+ callId: item.id,
99
+ tool: toolName,
100
+ output,
101
+ success: item.status === "completed",
102
+ timestamp,
103
+ };
104
+ }
105
+ /** Helper to derive tool name if context is missing */
106
+ function deriveToolName(type, item) {
107
+ switch (type) {
108
+ case "command_execution": {
109
+ return "shell";
110
+ }
111
+ case "mcp_tool_call": {
112
+ // Validate mcp_tool_call has expected shape before accessing properties
113
+ if (typeof item === "object" &&
114
+ item !== null &&
115
+ "server" in item &&
116
+ "tool" in item) {
117
+ const mcp = item;
118
+ return `${mcp.server}:${mcp.tool}`;
119
+ }
120
+ return "unknown_mcp";
121
+ }
122
+ case "file_change": {
123
+ return "file_change";
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Event parser for Codex CLI.
3
+ *
4
+ * Transforms raw Codex CLI JSONL events into normalized {@link AxexecEvent} types.
5
+ * Uses stateful correlation to resolve tool names in tool.result events.
6
+ *
7
+ * ## Turn Completion Semantics
8
+ *
9
+ * In `codex exec` mode (the mode used by this adapter), each process invocation
10
+ * handles exactly **one turn** before exiting. The event sequence is:
11
+ *
12
+ * 1. `thread.started` - session initialized
13
+ * 2. `turn.started` - single turn begins
14
+ * 3. `item.*` events - tool calls, messages, reasoning
15
+ * 4. `turn.completed` or `turn.failed` - turn ends
16
+ * 5. Process exits
17
+ *
18
+ * This means a single `codex exec` invocation will emit exactly one
19
+ * `turn.completed` (or `turn.failed`) event. The "resume" functionality
20
+ * (`codex exec resume <id>`) spawns a **new process** for each resumed turn.
21
+ *
22
+ * Therefore, mapping `turn.completed` to `task.complete` is correct; there is
23
+ * no risk of multiple `task.complete` events per session.
24
+ *
25
+ * @see https://github.com/openai/codex/blob/main/codex-rs/exec/src/lib.rs
26
+ * Search for `TaskComplete` handling which triggers `InitiateShutdown`.
27
+ */
28
+ import type { EventParser } from "../../types/adapter.js";
29
+ /**
30
+ * Creates a parser for Codex CLI events.
31
+ *
32
+ * Maintains a mapping from item ID to tool info to support
33
+ * correlating start/complete events for tools.
34
+ *
35
+ * @param requestedModel - Model passed via --model flag (if any)
36
+ */
37
+ declare function createCodexParser(requestedModel?: string): EventParser;
38
+ export { createCodexParser };
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Event parser for Codex CLI.
3
+ *
4
+ * Transforms raw Codex CLI JSONL events into normalized {@link AxexecEvent} types.
5
+ * Uses stateful correlation to resolve tool names in tool.result events.
6
+ *
7
+ * ## Turn Completion Semantics
8
+ *
9
+ * In `codex exec` mode (the mode used by this adapter), each process invocation
10
+ * handles exactly **one turn** before exiting. The event sequence is:
11
+ *
12
+ * 1. `thread.started` - session initialized
13
+ * 2. `turn.started` - single turn begins
14
+ * 3. `item.*` events - tool calls, messages, reasoning
15
+ * 4. `turn.completed` or `turn.failed` - turn ends
16
+ * 5. Process exits
17
+ *
18
+ * This means a single `codex exec` invocation will emit exactly one
19
+ * `turn.completed` (or `turn.failed`) event. The "resume" functionality
20
+ * (`codex exec resume <id>`) spawns a **new process** for each resumed turn.
21
+ *
22
+ * Therefore, mapping `turn.completed` to `task.complete` is correct; there is
23
+ * no risk of multiple `task.complete` events per session.
24
+ *
25
+ * @see https://github.com/openai/codex/blob/main/codex-rs/exec/src/lib.rs
26
+ * Search for `TaskComplete` handling which triggers `InitiateShutdown`.
27
+ */
28
+ import { createTimestampParser } from "../../parse-iso-timestamp.js";
29
+ import { CodexEvent, isErrorEvent, isItemCompletedEvent, isItemStartedEvent, isThreadStartedEvent, isTurnCompletedEvent, isTurnFailedEvent, } from "./types.js";
30
+ import { parseItemCompletedEvent, parseItemStartedEvent, } from "./item-parsers.js";
31
+ /**
32
+ * Creates a parser for Codex CLI events.
33
+ *
34
+ * Maintains a mapping from item ID to tool info to support
35
+ * correlating start/complete events for tools.
36
+ *
37
+ * @param requestedModel - Model passed via --model flag (if any)
38
+ */
39
+ function createCodexParser(requestedModel) {
40
+ // Map from item ID to tool info for correlation
41
+ // This is needed because item.completed events might need context from item.started
42
+ // (specifically tool name, although AxexecEvent.tool.result needs it)
43
+ const toolContext = new Map();
44
+ // Stateful timestamp parser - falls back to previous timestamp when missing
45
+ const parseTimestamp = createTimestampParser();
46
+ // Track session start time to calculate duration (Codex doesn't provide it)
47
+ let sessionStartMs;
48
+ return (line) => {
49
+ const trimmed = line.trim();
50
+ if (trimmed === "") {
51
+ return undefined;
52
+ }
53
+ let json;
54
+ try {
55
+ json = JSON.parse(trimmed);
56
+ }
57
+ catch {
58
+ return undefined;
59
+ }
60
+ // Extract timestamp from RolloutLine envelope before schema validation.
61
+ // Codex wraps events in RolloutLine which adds a timestamp field at the top level.
62
+ const rawTimestamp = typeof json === "object" &&
63
+ json !== null &&
64
+ "timestamp" in json &&
65
+ typeof json["timestamp"] === "string"
66
+ ? json["timestamp"]
67
+ : undefined;
68
+ const timestamp = parseTimestamp(rawTimestamp);
69
+ const result = CodexEvent.safeParse(json);
70
+ if (!result.success) {
71
+ return undefined;
72
+ }
73
+ const raw = result.data;
74
+ if (isThreadStartedEvent(raw)) {
75
+ sessionStartMs = Date.now();
76
+ return parseThreadStartedEvent(raw, timestamp, requestedModel);
77
+ }
78
+ if (isItemStartedEvent(raw)) {
79
+ return parseItemStartedEvent(raw, toolContext, timestamp);
80
+ }
81
+ if (isItemCompletedEvent(raw)) {
82
+ return parseItemCompletedEvent(raw, toolContext, timestamp);
83
+ }
84
+ if (isTurnCompletedEvent(raw)) {
85
+ const durationMs = sessionStartMs === undefined ? 0 : Date.now() - sessionStartMs;
86
+ return parseTurnCompletedEvent(raw, durationMs, timestamp);
87
+ }
88
+ if (isTurnFailedEvent(raw)) {
89
+ return parseTurnFailedEvent(raw, timestamp);
90
+ }
91
+ if (isErrorEvent(raw)) {
92
+ return parseErrorEvent(raw, timestamp);
93
+ }
94
+ return undefined;
95
+ };
96
+ }
97
+ /** Parses a thread.started event (session start) */
98
+ function parseThreadStartedEvent(event, timestamp, requestedModel) {
99
+ // Codex JSONL output doesn't include model info - it's stripped during
100
+ // conversion from SessionConfiguredEvent to ThreadStartedEvent.
101
+ // Use the model passed via CLI if available, otherwise report "unknown".
102
+ return {
103
+ type: "session.start",
104
+ sessionId: event.thread_id,
105
+ agent: "codex",
106
+ model: requestedModel ?? "unknown",
107
+ provider: "openai",
108
+ timestamp,
109
+ };
110
+ }
111
+ /** Parses turn.completed event (session complete) */
112
+ function parseTurnCompletedEvent(event, durationMs, timestamp) {
113
+ return {
114
+ type: "session.complete",
115
+ stats: {
116
+ durationMs,
117
+ inputTokens: event.usage.input_tokens,
118
+ outputTokens: event.usage.output_tokens,
119
+ },
120
+ timestamp,
121
+ };
122
+ }
123
+ /** Parses turn.failed event (session error) */
124
+ function parseTurnFailedEvent(event, timestamp) {
125
+ return {
126
+ type: "session.error",
127
+ code: "AGENT_ERROR",
128
+ message: event.error.message,
129
+ timestamp,
130
+ };
131
+ }
132
+ /** Parses error event (session error) */
133
+ function parseErrorEvent(event, timestamp) {
134
+ return {
135
+ type: "session.error",
136
+ code: "AGENT_ERROR",
137
+ message: event.message,
138
+ timestamp,
139
+ };
140
+ }
141
+ export { createCodexParser };