@agentex/agent 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/claude/execute.js +8 -8
- package/dist/providers/claude/execute.js.map +1 -1
- package/dist/providers/claude/index.d.ts.map +1 -1
- package/dist/providers/claude/index.js +1 -44
- package/dist/providers/claude/index.js.map +1 -1
- package/dist/providers/claude/parse.d.ts +30 -6
- package/dist/providers/claude/parse.d.ts.map +1 -1
- package/dist/providers/claude/parse.js +217 -84
- package/dist/providers/claude/parse.js.map +1 -1
- package/dist/providers/codex/execute.d.ts.map +1 -1
- package/dist/providers/codex/execute.js +38 -26
- package/dist/providers/codex/execute.js.map +1 -1
- package/dist/providers/codex/index.d.ts.map +1 -1
- package/dist/providers/codex/index.js +1 -44
- package/dist/providers/codex/index.js.map +1 -1
- package/dist/providers/codex/parse.d.ts +18 -6
- package/dist/providers/codex/parse.d.ts.map +1 -1
- package/dist/providers/codex/parse.js +333 -35
- package/dist/providers/codex/parse.js.map +1 -1
- package/dist/providers/codex/session.js +16 -2
- package/dist/providers/codex/session.js.map +1 -1
- package/dist/providers/cursor/index.d.ts.map +1 -1
- package/dist/providers/cursor/index.js +1 -10
- package/dist/providers/cursor/index.js.map +1 -1
- package/dist/providers/cursor/parse.d.ts.map +1 -1
- package/dist/providers/cursor/parse.js +33 -12
- package/dist/providers/cursor/parse.js.map +1 -1
- package/dist/providers/gemini/index.d.ts.map +1 -1
- package/dist/providers/gemini/index.js +1 -44
- package/dist/providers/gemini/index.js.map +1 -1
- package/dist/providers/gemini/parse.d.ts.map +1 -1
- package/dist/providers/gemini/parse.js +38 -10
- package/dist/providers/gemini/parse.js.map +1 -1
- package/dist/providers/openclaw/execute.d.ts.map +1 -1
- package/dist/providers/openclaw/execute.js +12 -1
- package/dist/providers/openclaw/execute.js.map +1 -1
- package/dist/providers/opencode/index.d.ts.map +1 -1
- package/dist/providers/opencode/index.js +1 -44
- package/dist/providers/opencode/index.js.map +1 -1
- package/dist/providers/opencode/parse.d.ts.map +1 -1
- package/dist/providers/opencode/parse.js +40 -11
- package/dist/providers/opencode/parse.js.map +1 -1
- package/dist/providers/pi/index.d.ts.map +1 -1
- package/dist/providers/pi/index.js +1 -46
- package/dist/providers/pi/index.js.map +1 -1
- package/dist/providers/pi/parse.d.ts.map +1 -1
- package/dist/providers/pi/parse.js +28 -9
- package/dist/providers/pi/parse.js.map +1 -1
- package/dist/types.d.ts +167 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,54 +1,12 @@
|
|
|
1
1
|
import { executeCodexProvider } from "./execute.js";
|
|
2
2
|
import { createCodexSession } from "./session.js";
|
|
3
3
|
import { codexSessionCodec } from "./codec.js";
|
|
4
|
-
import { findBinary } from "../../utils/binary.js";
|
|
5
|
-
import { buildEnv, ensurePathInEnv } from "../../utils/env.js";
|
|
6
4
|
import { resolveAuthForProvider } from "../../utils/auth.js";
|
|
7
|
-
import { ModelCache } from "../../utils/model-cache.js";
|
|
8
|
-
import { runChildProcess } from "../../utils/process.js";
|
|
9
|
-
const FALLBACK_MODELS = [
|
|
10
|
-
{ id: "o3", name: "o3", provider: "openai" },
|
|
11
|
-
{ id: "o4-mini", name: "o4-mini", provider: "openai" },
|
|
12
|
-
{ id: "codex-mini-latest", name: "Codex Mini", provider: "openai" },
|
|
13
|
-
];
|
|
14
|
-
const cache = new ModelCache();
|
|
15
|
-
async function fetchModels() {
|
|
16
|
-
try {
|
|
17
|
-
const resolved = await findBinary("codex");
|
|
18
|
-
const env = buildEnv();
|
|
19
|
-
ensurePathInEnv(env);
|
|
20
|
-
const proc = await runChildProcess({
|
|
21
|
-
runId: "list-models",
|
|
22
|
-
command: resolved.bin,
|
|
23
|
-
args: [...resolved.prefixArgs, "models"],
|
|
24
|
-
cwd: process.cwd(),
|
|
25
|
-
env,
|
|
26
|
-
timeoutSec: 10,
|
|
27
|
-
});
|
|
28
|
-
if ((proc.exitCode ?? 1) === 0 && proc.stdout.trim()) {
|
|
29
|
-
return proc.stdout
|
|
30
|
-
.trim()
|
|
31
|
-
.split(/\r?\n/)
|
|
32
|
-
.filter((line) => line.trim())
|
|
33
|
-
.map((line) => {
|
|
34
|
-
const id = line.trim();
|
|
35
|
-
return { id, name: id, provider: "openai" };
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
// Fallback
|
|
41
|
-
}
|
|
42
|
-
return FALLBACK_MODELS;
|
|
43
|
-
}
|
|
44
|
-
async function listModels(options) {
|
|
45
|
-
return cache.get(options?.cacheTtlMs ?? 0, fetchModels);
|
|
46
|
-
}
|
|
47
5
|
export const codexProvider = {
|
|
48
6
|
type: "codex",
|
|
49
7
|
capabilities: {
|
|
50
8
|
sessions: true,
|
|
51
|
-
modelDiscovery:
|
|
9
|
+
modelDiscovery: false,
|
|
52
10
|
quotaProbing: false,
|
|
53
11
|
mcp: false,
|
|
54
12
|
skills: true,
|
|
@@ -59,6 +17,5 @@ export const codexProvider = {
|
|
|
59
17
|
createSession: (ctx) => createCodexSession(ctx),
|
|
60
18
|
resolveAuth: (ctx) => resolveAuthForProvider("codex", ctx),
|
|
61
19
|
sessionCodec: codexSessionCodec,
|
|
62
|
-
listModels,
|
|
63
20
|
};
|
|
64
21
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/codex/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/codex/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C,IAAI,EAAE,OAAO;IACb,YAAY,EAAE;QACZ,QAAQ,EAAE,IAAI;QACd,cAAc,EAAE,KAAK;QACrB,YAAY,EAAE,KAAK;QACnB,GAAG,EAAE,KAAK;QACV,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,IAAI;QAClB,SAAS,EAAE,IAAI;KAChB;IACD,OAAO,EAAE,oBAAoB;IAC7B,aAAa,EAAE,CAAC,GAAmB,EAAyB,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC;IACtF,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,sBAAsB,CAAC,OAAO,EAAE,GAAG,CAAC;IAC1D,YAAY,EAAE,iBAAiB;CAChC,CAAC"}
|
|
@@ -1,19 +1,31 @@
|
|
|
1
|
-
import type { StreamEvent } from "../../types.js";
|
|
1
|
+
import type { StreamEvent, TokenUsage } from "../../types.js";
|
|
2
2
|
export interface CodexParsedResult {
|
|
3
3
|
sessionId: string | null;
|
|
4
|
+
/**
|
|
5
|
+
* Codex doesn't emit `model` in its NDJSON output — always null from
|
|
6
|
+
* stdout. Executors should fall back to the requested model.
|
|
7
|
+
*/
|
|
4
8
|
model: string | null;
|
|
5
|
-
usage:
|
|
6
|
-
inputTokens: number;
|
|
7
|
-
outputTokens: number;
|
|
8
|
-
} | null;
|
|
9
|
+
usage: TokenUsage | null;
|
|
9
10
|
costUsd: number | null;
|
|
10
11
|
summary: string | null;
|
|
11
12
|
isError: boolean;
|
|
12
13
|
errorCode: string | null;
|
|
13
14
|
errorMessage: string | null;
|
|
15
|
+
/** Final `turn.completed` / `turn.failed` / `error` event verbatim. */
|
|
16
|
+
finalEvent: Record<string, unknown> | null;
|
|
14
17
|
}
|
|
15
18
|
export declare function parseCodexJsonl(stdout: string): CodexParsedResult;
|
|
16
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Parse a single Codex line into a StreamEvent.
|
|
21
|
+
*
|
|
22
|
+
* @param line Raw JSON text.
|
|
23
|
+
* @param sessionId Caller-tracked thread id (NDJSON emits it once on
|
|
24
|
+
* `thread.started` and the executor threads it through).
|
|
25
|
+
* v2 parses it from `params.threadId` directly and
|
|
26
|
+
* ignores this arg.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseCodexStreamLine(line: string, sessionId?: string | null): StreamEvent | null;
|
|
17
29
|
export declare function stripCodexRolloutNoise(text: string): string;
|
|
18
30
|
export declare function isCodexAuthRequired(stdout: string, stderr: string): boolean;
|
|
19
31
|
export declare function isCodexUnknownSessionError(stdout: string, stderr: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../src/providers/codex/parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../src/providers/codex/parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,WAAW,EACX,UAAU,EACX,MAAM,gBAAgB,CAAC;AAIxB,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AAkED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CA4FjE;AAUD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAM,GAAG,IAAW,GAC9B,WAAW,GAAG,IAAI,CAWpB;AAyXD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS3D;AAID,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAE3E;AAID,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAElF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const PROVIDER_TYPE = "codex";
|
|
1
2
|
function parseJson(line) {
|
|
2
3
|
try {
|
|
3
4
|
const parsed = JSON.parse(line);
|
|
@@ -13,25 +14,53 @@ function parseJson(line) {
|
|
|
13
14
|
function asString(value, fallback) {
|
|
14
15
|
return typeof value === "string" ? value : fallback;
|
|
15
16
|
}
|
|
17
|
+
function asNullableString(value) {
|
|
18
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
19
|
+
}
|
|
16
20
|
function asNumber(value, fallback) {
|
|
17
21
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
18
22
|
}
|
|
23
|
+
function asNullableNumber(value) {
|
|
24
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
25
|
+
}
|
|
19
26
|
function parseObject(value) {
|
|
20
27
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
21
28
|
return value;
|
|
22
29
|
}
|
|
23
30
|
return {};
|
|
24
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Build base fields. For Codex, `eventId` is always null (neither wire
|
|
34
|
+
* format emits a per-line UUID). `sessionId` and `turnId` come from the
|
|
35
|
+
* caller — the v2 parser extracts them from the event's `params`; the
|
|
36
|
+
* NDJSON parser tracks sessionId across lines and leaves turnId null.
|
|
37
|
+
*/
|
|
38
|
+
function baseFields(event, sessionId, messageId, turnId) {
|
|
39
|
+
return {
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
providerType: PROVIDER_TYPE,
|
|
42
|
+
sessionId,
|
|
43
|
+
messageId,
|
|
44
|
+
eventId: null,
|
|
45
|
+
turnId,
|
|
46
|
+
parentToolCallId: null,
|
|
47
|
+
raw: event,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Run-level parser used by executeCodexProvider to summarize a full stdout.
|
|
52
|
+
// Operates on NDJSON only (executor uses `codex exec --json`).
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
25
54
|
export function parseCodexJsonl(stdout) {
|
|
26
55
|
let sessionId = null;
|
|
27
|
-
let model = null;
|
|
28
56
|
let totalInputTokens = 0;
|
|
29
57
|
let totalOutputTokens = 0;
|
|
58
|
+
let totalCachedInputTokens = 0;
|
|
30
59
|
let hasUsage = false;
|
|
31
60
|
let summary = null;
|
|
32
61
|
let isError = false;
|
|
33
|
-
let errorCode = null;
|
|
34
62
|
let errorMessage = null;
|
|
63
|
+
let finalEvent = null;
|
|
35
64
|
for (const rawLine of stdout.split(/\r?\n/)) {
|
|
36
65
|
const line = rawLine.trim();
|
|
37
66
|
if (!line)
|
|
@@ -41,19 +70,17 @@ export function parseCodexJsonl(stdout) {
|
|
|
41
70
|
continue;
|
|
42
71
|
const type = asString(event["type"], "");
|
|
43
72
|
if (type === "thread.started") {
|
|
44
|
-
sessionId =
|
|
73
|
+
sessionId = asNullableString(event["thread_id"]) ?? sessionId;
|
|
45
74
|
continue;
|
|
46
75
|
}
|
|
47
76
|
if (type === "item.completed") {
|
|
48
77
|
const item = parseObject(event["item"]);
|
|
49
78
|
if (asString(item["type"], "") === "agent_message") {
|
|
50
|
-
// Direct text field (Codex 0.30+)
|
|
51
79
|
const directText = asString(item["text"], "");
|
|
52
80
|
if (directText) {
|
|
53
81
|
summary = directText;
|
|
54
82
|
}
|
|
55
83
|
else {
|
|
56
|
-
// Fallback: content array with output_text blocks
|
|
57
84
|
const content = Array.isArray(item["content"]) ? item["content"] : [];
|
|
58
85
|
for (const entry of content) {
|
|
59
86
|
if (typeof entry !== "object" || entry === null || Array.isArray(entry))
|
|
@@ -73,134 +100,405 @@ export function parseCodexJsonl(stdout) {
|
|
|
73
100
|
const usage = parseObject(event["usage"]);
|
|
74
101
|
const inputTokens = asNumber(usage["input_tokens"], 0);
|
|
75
102
|
const outputTokens = asNumber(usage["output_tokens"], 0);
|
|
76
|
-
|
|
103
|
+
const cachedInputTokens = asNumber(usage["cached_input_tokens"], 0);
|
|
104
|
+
if (inputTokens > 0 || outputTokens > 0 || cachedInputTokens > 0) {
|
|
77
105
|
totalInputTokens += inputTokens;
|
|
78
106
|
totalOutputTokens += outputTokens;
|
|
107
|
+
totalCachedInputTokens += cachedInputTokens;
|
|
79
108
|
hasUsage = true;
|
|
80
109
|
}
|
|
81
|
-
|
|
110
|
+
finalEvent = event;
|
|
82
111
|
continue;
|
|
83
112
|
}
|
|
84
113
|
if (type === "turn.failed") {
|
|
85
114
|
isError = true;
|
|
86
|
-
errorMessage =
|
|
115
|
+
errorMessage = asNullableString(event["message"]) ?? asNullableString(event["error"]);
|
|
116
|
+
finalEvent = event;
|
|
87
117
|
continue;
|
|
88
118
|
}
|
|
89
119
|
if (type === "error") {
|
|
90
120
|
isError = true;
|
|
91
|
-
errorMessage =
|
|
121
|
+
errorMessage = asNullableString(event["message"]);
|
|
122
|
+
finalEvent = event;
|
|
92
123
|
continue;
|
|
93
124
|
}
|
|
94
125
|
}
|
|
126
|
+
const usage = hasUsage ? {
|
|
127
|
+
inputTokens: totalInputTokens,
|
|
128
|
+
outputTokens: totalOutputTokens,
|
|
129
|
+
...(totalCachedInputTokens > 0 ? { cachedInputTokens: totalCachedInputTokens } : {}),
|
|
130
|
+
} : null;
|
|
95
131
|
return {
|
|
96
132
|
sessionId,
|
|
97
|
-
model,
|
|
98
|
-
usage
|
|
99
|
-
costUsd: null, // Codex
|
|
133
|
+
model: null,
|
|
134
|
+
usage,
|
|
135
|
+
costUsd: null, // Codex doesn't report cost
|
|
100
136
|
summary,
|
|
101
137
|
isError,
|
|
102
|
-
errorCode,
|
|
138
|
+
errorCode: null,
|
|
103
139
|
errorMessage,
|
|
140
|
+
finalEvent,
|
|
104
141
|
};
|
|
105
142
|
}
|
|
106
|
-
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Stream-line parser. Auto-detects wire format:
|
|
145
|
+
// - `codex exec --json` NDJSON: `{"type":"...","...": ...}`
|
|
146
|
+
// - `codex --json` v2 JSON-RPC: `{"jsonrpc":"2.0","method":"...","params":{...}}`
|
|
147
|
+
// Both shapes produce the same StreamEvent variants so downstream
|
|
148
|
+
// consumers don't branch on format.
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
/**
|
|
151
|
+
* Parse a single Codex line into a StreamEvent.
|
|
152
|
+
*
|
|
153
|
+
* @param line Raw JSON text.
|
|
154
|
+
* @param sessionId Caller-tracked thread id (NDJSON emits it once on
|
|
155
|
+
* `thread.started` and the executor threads it through).
|
|
156
|
+
* v2 parses it from `params.threadId` directly and
|
|
157
|
+
* ignores this arg.
|
|
158
|
+
*/
|
|
159
|
+
export function parseCodexStreamLine(line, sessionId = null) {
|
|
107
160
|
const event = parseJson(line);
|
|
108
161
|
if (!event)
|
|
109
162
|
return null;
|
|
163
|
+
// v2 JSON-RPC notification: has `method` + `params`.
|
|
164
|
+
if (typeof event["method"] === "string") {
|
|
165
|
+
return parseV2Notification(event);
|
|
166
|
+
}
|
|
167
|
+
// NDJSON legacy format: has `type`.
|
|
168
|
+
return parseNdjsonEvent(event, sessionId);
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// v2 JSON-RPC notifications (codex --json app-server mode)
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
function parseV2Notification(event) {
|
|
174
|
+
const method = asString(event["method"], "");
|
|
175
|
+
const params = parseObject(event["params"]);
|
|
176
|
+
// Extract thread + turn scope. Most notifications carry threadId at the
|
|
177
|
+
// top of params; thread/started nests it under thread.id.
|
|
178
|
+
const thread = parseObject(params["thread"]);
|
|
179
|
+
const threadId = asNullableString(params["threadId"]) ??
|
|
180
|
+
asNullableString(thread["id"]);
|
|
181
|
+
// turn/started + turn/completed nest the turn object; everything else
|
|
182
|
+
// puts turnId at the top of params.
|
|
183
|
+
const turn = parseObject(params["turn"]);
|
|
184
|
+
const turnId = asNullableString(params["turnId"]) ??
|
|
185
|
+
asNullableString(turn["id"]);
|
|
186
|
+
const makeBase = (messageId) => baseFields(event, threadId, messageId, turnId);
|
|
187
|
+
// ---- Thread lifecycle ----
|
|
188
|
+
if (method === "thread/started") {
|
|
189
|
+
return {
|
|
190
|
+
type: "system",
|
|
191
|
+
subtype: "init",
|
|
192
|
+
model: null,
|
|
193
|
+
cwd: asNullableString(thread["cwd"]),
|
|
194
|
+
tools: null,
|
|
195
|
+
permissionMode: null,
|
|
196
|
+
...makeBase(null),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// ---- Turn lifecycle ----
|
|
200
|
+
if (method === "turn/started") {
|
|
201
|
+
// Lifecycle marker; items will follow. Skip to reduce noise.
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
if (method === "turn/completed") {
|
|
205
|
+
return {
|
|
206
|
+
type: "result",
|
|
207
|
+
text: "",
|
|
208
|
+
costUsd: null,
|
|
209
|
+
isError: false,
|
|
210
|
+
stopReason: null,
|
|
211
|
+
terminalReason: asNullableString(turn["status"]),
|
|
212
|
+
numTurns: null,
|
|
213
|
+
durationMs: asNullableNumber(turn["durationMs"]),
|
|
214
|
+
...makeBase(null),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (method === "turn/failed") {
|
|
218
|
+
return {
|
|
219
|
+
type: "result",
|
|
220
|
+
text: asString(params["message"], ""),
|
|
221
|
+
costUsd: null,
|
|
222
|
+
isError: true,
|
|
223
|
+
stopReason: null,
|
|
224
|
+
terminalReason: asNullableString(turn["status"]),
|
|
225
|
+
numTurns: null,
|
|
226
|
+
durationMs: asNullableNumber(turn["durationMs"]),
|
|
227
|
+
...makeBase(null),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
// ---- Item lifecycle ----
|
|
231
|
+
if (method === "item/started" || method === "item/completed") {
|
|
232
|
+
const item = parseObject(params["item"]);
|
|
233
|
+
const itemType = asString(item["type"], "");
|
|
234
|
+
const itemId = asNullableString(item["id"]) ??
|
|
235
|
+
asNullableString(item["call_id"]);
|
|
236
|
+
const base = makeBase(itemId);
|
|
237
|
+
// Tool starts — emit tool_call on item/started only.
|
|
238
|
+
if (method === "item/started") {
|
|
239
|
+
if (itemType === "command_execution") {
|
|
240
|
+
return {
|
|
241
|
+
type: "tool_call",
|
|
242
|
+
toolCallId: itemId,
|
|
243
|
+
name: "command_execution",
|
|
244
|
+
input: asString(item["command"], ""),
|
|
245
|
+
...base,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (itemType === "function_call") {
|
|
249
|
+
return {
|
|
250
|
+
type: "tool_call",
|
|
251
|
+
toolCallId: itemId,
|
|
252
|
+
name: asString(item["name"], "function_call"),
|
|
253
|
+
input: item["arguments"] ?? item["input"] ?? "",
|
|
254
|
+
...base,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
// reasoning, agentMessage, userMessage — wait for item/completed.
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
// item/completed — emit the terminal event for each item type.
|
|
261
|
+
if (itemType === "command_execution") {
|
|
262
|
+
const exitCode = asNullableNumber(item["exit_code"]);
|
|
263
|
+
return {
|
|
264
|
+
type: "tool_result",
|
|
265
|
+
toolCallId: itemId,
|
|
266
|
+
content: asString(item["aggregated_output"], ""),
|
|
267
|
+
isError: exitCode !== null && exitCode !== 0,
|
|
268
|
+
exitCode,
|
|
269
|
+
...base,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (itemType === "function_call") {
|
|
273
|
+
const output = item["output"] ?? item["result"] ?? "";
|
|
274
|
+
return {
|
|
275
|
+
type: "tool_result",
|
|
276
|
+
toolCallId: itemId,
|
|
277
|
+
content: typeof output === "string" ? output : JSON.stringify(output),
|
|
278
|
+
isError: item["status"] === "failed",
|
|
279
|
+
exitCode: null,
|
|
280
|
+
...base,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
if (itemType === "agentMessage") {
|
|
284
|
+
const directText = asString(item["text"], "");
|
|
285
|
+
if (directText || directText === "") {
|
|
286
|
+
return {
|
|
287
|
+
type: "assistant",
|
|
288
|
+
text: directText,
|
|
289
|
+
...base,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (itemType === "reasoning") {
|
|
294
|
+
// Reasoning content may be empty / encrypted out-of-band — consumers
|
|
295
|
+
// that need the raw payload read it from `raw`.
|
|
296
|
+
const text = asString(item["text"], "") ||
|
|
297
|
+
extractReasoningText(item);
|
|
298
|
+
return {
|
|
299
|
+
type: "thinking",
|
|
300
|
+
text,
|
|
301
|
+
...base,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (itemType === "userMessage") {
|
|
305
|
+
// Consumer persists user input on the write path before calling send().
|
|
306
|
+
// We don't re-emit it here to keep a single source of truth.
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
// Unknown item type — surface as unknown.
|
|
310
|
+
return {
|
|
311
|
+
type: "unknown",
|
|
312
|
+
subtype: `item/completed:${itemType}`,
|
|
313
|
+
...base,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
// ---- Streaming deltas: block-level only for v1; skip token deltas. ----
|
|
317
|
+
if (method === "item/agentMessage/delta" || method === "item/reasoning/delta") {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
// ---- Rate limits ----
|
|
321
|
+
if (method === "account/rateLimits/updated") {
|
|
322
|
+
const rateLimits = parseObject(params["rateLimits"]);
|
|
323
|
+
const primary = parseObject(rateLimits["primary"]);
|
|
324
|
+
const usedPercent = asNullableNumber(primary["usedPercent"]);
|
|
325
|
+
return {
|
|
326
|
+
type: "rate_limit",
|
|
327
|
+
status: usedPercent !== null && usedPercent >= 100 ? "rejected" : "allowed",
|
|
328
|
+
limitType: asNullableString(rateLimits["limitId"]),
|
|
329
|
+
resetAt: null,
|
|
330
|
+
overageStatus: null,
|
|
331
|
+
isUsingOverage: null,
|
|
332
|
+
...makeBase(null),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
// ---- Pure telemetry / status ----
|
|
336
|
+
if (method === "thread/tokenUsage/updated" ||
|
|
337
|
+
method === "thread/status/changed" ||
|
|
338
|
+
method === "mcpServer/startupStatus/updated") {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
// ---- Forward-compat: unknown method ----
|
|
342
|
+
return {
|
|
343
|
+
type: "unknown",
|
|
344
|
+
subtype: method,
|
|
345
|
+
...makeBase(null),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function extractReasoningText(item) {
|
|
349
|
+
// Codex reasoning items carry summary and content arrays. Concatenate
|
|
350
|
+
// any visible text fragments for display; fall back to "" if all empty
|
|
351
|
+
// or encrypted.
|
|
352
|
+
const parts = [];
|
|
353
|
+
for (const key of ["summary", "content"]) {
|
|
354
|
+
const arr = Array.isArray(item[key]) ? item[key] : [];
|
|
355
|
+
for (const entry of arr) {
|
|
356
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry))
|
|
357
|
+
continue;
|
|
358
|
+
const block = entry;
|
|
359
|
+
const text = asString(block["text"], "");
|
|
360
|
+
if (text)
|
|
361
|
+
parts.push(text);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return parts.join("\n").trim();
|
|
365
|
+
}
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
// NDJSON format (codex exec --json)
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
function parseNdjsonEvent(event, sessionId) {
|
|
110
370
|
const type = asString(event["type"], "");
|
|
111
|
-
const timestamp = new Date().toISOString();
|
|
112
371
|
if (type === "thread.started") {
|
|
372
|
+
const threadId = asNullableString(event["thread_id"]);
|
|
113
373
|
return {
|
|
114
374
|
type: "system",
|
|
115
375
|
subtype: "init",
|
|
116
|
-
sessionId: asString(event["thread_id"], "") || null,
|
|
117
376
|
model: null,
|
|
118
|
-
|
|
377
|
+
cwd: null,
|
|
378
|
+
tools: null,
|
|
379
|
+
permissionMode: null,
|
|
380
|
+
...baseFields(event, threadId, null, null),
|
|
119
381
|
};
|
|
120
382
|
}
|
|
121
383
|
if (type === "item.started") {
|
|
122
384
|
const item = parseObject(event["item"]);
|
|
123
385
|
const itemType = asString(item["type"], "");
|
|
386
|
+
const itemId = asNullableString(item["id"]) ?? asNullableString(item["call_id"]);
|
|
387
|
+
const base = baseFields(event, sessionId, itemId, null);
|
|
124
388
|
if (itemType === "command_execution") {
|
|
125
389
|
return {
|
|
126
390
|
type: "tool_call",
|
|
127
|
-
|
|
391
|
+
toolCallId: itemId,
|
|
128
392
|
name: "command_execution",
|
|
129
393
|
input: asString(item["command"], ""),
|
|
130
|
-
|
|
394
|
+
...base,
|
|
131
395
|
};
|
|
132
396
|
}
|
|
133
397
|
if (itemType === "function_call") {
|
|
134
398
|
return {
|
|
135
399
|
type: "tool_call",
|
|
136
|
-
|
|
400
|
+
toolCallId: itemId,
|
|
137
401
|
name: asString(item["name"], "function_call"),
|
|
138
402
|
input: item["arguments"] ?? item["input"] ?? "",
|
|
139
|
-
|
|
403
|
+
...base,
|
|
140
404
|
};
|
|
141
405
|
}
|
|
142
406
|
}
|
|
143
407
|
if (type === "item.completed") {
|
|
144
408
|
const item = parseObject(event["item"]);
|
|
145
409
|
const itemType = asString(item["type"], "");
|
|
410
|
+
const itemId = asNullableString(item["id"]) ?? asNullableString(item["call_id"]);
|
|
411
|
+
const base = baseFields(event, sessionId, itemId, null);
|
|
146
412
|
if (itemType === "command_execution") {
|
|
147
|
-
const exitCode =
|
|
413
|
+
const exitCode = asNullableNumber(item["exit_code"]);
|
|
148
414
|
return {
|
|
149
415
|
type: "tool_result",
|
|
150
|
-
toolCallId:
|
|
416
|
+
toolCallId: itemId,
|
|
151
417
|
content: asString(item["aggregated_output"], ""),
|
|
152
418
|
isError: exitCode !== null && exitCode !== 0,
|
|
153
|
-
|
|
419
|
+
exitCode,
|
|
420
|
+
...base,
|
|
154
421
|
};
|
|
155
422
|
}
|
|
156
423
|
if (itemType === "function_call") {
|
|
157
424
|
const output = item["output"] ?? item["result"] ?? "";
|
|
158
425
|
return {
|
|
159
426
|
type: "tool_result",
|
|
160
|
-
toolCallId:
|
|
427
|
+
toolCallId: itemId,
|
|
161
428
|
content: typeof output === "string" ? output : JSON.stringify(output),
|
|
162
429
|
isError: item["status"] === "failed",
|
|
163
|
-
|
|
430
|
+
exitCode: null,
|
|
431
|
+
...base,
|
|
164
432
|
};
|
|
165
433
|
}
|
|
166
434
|
if (itemType === "agent_message") {
|
|
167
|
-
// Direct text field (Codex 0.30+)
|
|
168
435
|
const directText = asString(item["text"], "");
|
|
169
436
|
if (directText) {
|
|
170
|
-
return {
|
|
437
|
+
return {
|
|
438
|
+
type: "assistant",
|
|
439
|
+
text: directText,
|
|
440
|
+
...base,
|
|
441
|
+
};
|
|
171
442
|
}
|
|
172
|
-
// Fallback: content array with output_text blocks
|
|
173
443
|
const content = Array.isArray(item["content"]) ? item["content"] : [];
|
|
174
444
|
for (const entry of content) {
|
|
175
445
|
if (typeof entry !== "object" || entry === null || Array.isArray(entry))
|
|
176
446
|
continue;
|
|
177
447
|
const block = entry;
|
|
178
448
|
if (asString(block["type"], "") === "output_text") {
|
|
179
|
-
return {
|
|
449
|
+
return {
|
|
450
|
+
type: "assistant",
|
|
451
|
+
text: asString(block["text"], ""),
|
|
452
|
+
...base,
|
|
453
|
+
};
|
|
180
454
|
}
|
|
181
455
|
}
|
|
182
456
|
}
|
|
457
|
+
if (itemType === "reasoning") {
|
|
458
|
+
const text = asString(item["text"], "") || extractReasoningText(item);
|
|
459
|
+
return {
|
|
460
|
+
type: "thinking",
|
|
461
|
+
text,
|
|
462
|
+
...base,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
183
465
|
}
|
|
184
466
|
if (type === "turn.completed") {
|
|
185
467
|
return {
|
|
186
468
|
type: "result",
|
|
187
469
|
text: "",
|
|
188
|
-
|
|
470
|
+
costUsd: null,
|
|
189
471
|
isError: false,
|
|
190
|
-
|
|
472
|
+
stopReason: null,
|
|
473
|
+
terminalReason: null,
|
|
474
|
+
numTurns: null,
|
|
475
|
+
durationMs: null,
|
|
476
|
+
...baseFields(event, sessionId, null, null),
|
|
191
477
|
};
|
|
192
478
|
}
|
|
193
479
|
if (type === "error" || type === "turn.failed") {
|
|
194
480
|
return {
|
|
195
481
|
type: "result",
|
|
196
482
|
text: asString(event["message"], ""),
|
|
197
|
-
|
|
483
|
+
costUsd: null,
|
|
198
484
|
isError: true,
|
|
199
|
-
|
|
485
|
+
stopReason: null,
|
|
486
|
+
terminalReason: null,
|
|
487
|
+
numTurns: null,
|
|
488
|
+
durationMs: null,
|
|
489
|
+
...baseFields(event, sessionId, null, null),
|
|
200
490
|
};
|
|
201
491
|
}
|
|
202
|
-
|
|
492
|
+
// Forward-compat: surface unknown event types.
|
|
493
|
+
return {
|
|
494
|
+
type: "unknown",
|
|
495
|
+
subtype: type,
|
|
496
|
+
...baseFields(event, sessionId, null, null),
|
|
497
|
+
};
|
|
203
498
|
}
|
|
499
|
+
// ---------------------------------------------------------------------------
|
|
500
|
+
// Error detection utilities (unchanged)
|
|
501
|
+
// ---------------------------------------------------------------------------
|
|
204
502
|
const CODEX_ROLLOUT_NOISE_RE = /^\d{4}-\d{2}-\d{2}T\S+\s+ERROR\s+codex_core::rollout::list:/i;
|
|
205
503
|
export function stripCodexRolloutNoise(text) {
|
|
206
504
|
return text
|