@relayflows/sdk 2.0.25 → 2.0.27

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 (38) hide show
  1. package/dist/authored-flow-executor.d.ts +6 -1
  2. package/dist/authored-flow-executor.d.ts.map +1 -1
  3. package/dist/authored-flow-executor.js +12 -1
  4. package/dist/authored-flow-executor.js.map +1 -1
  5. package/dist/babysitter-catalog-export.d.ts +9 -0
  6. package/dist/babysitter-catalog-export.d.ts.map +1 -0
  7. package/dist/babysitter-catalog-export.js +36 -0
  8. package/dist/babysitter-catalog-export.js.map +1 -0
  9. package/dist/cloud-transcript-codex.d.ts +42 -0
  10. package/dist/cloud-transcript-codex.d.ts.map +1 -0
  11. package/dist/cloud-transcript-codex.js +411 -0
  12. package/dist/cloud-transcript-codex.js.map +1 -0
  13. package/dist/cloud-transcript-types.d.ts +161 -0
  14. package/dist/cloud-transcript-types.d.ts.map +1 -0
  15. package/dist/cloud-transcript-types.js +47 -0
  16. package/dist/cloud-transcript-types.js.map +1 -0
  17. package/dist/cloud-transcript.d.ts +2 -78
  18. package/dist/cloud-transcript.d.ts.map +1 -1
  19. package/dist/cloud-transcript.js +36 -38
  20. package/dist/cloud-transcript.js.map +1 -1
  21. package/dist/failure-kinds.d.ts +2 -2
  22. package/dist/failure-kinds.d.ts.map +1 -1
  23. package/dist/flow-extension-loader.d.ts +31 -0
  24. package/dist/flow-extension-loader.d.ts.map +1 -1
  25. package/dist/flow-extension-loader.js +68 -0
  26. package/dist/flow-extension-loader.js.map +1 -1
  27. package/dist/plugin-manifest.d.ts +1 -1
  28. package/dist/plugin-manifest.d.ts.map +1 -1
  29. package/dist/plugin-manifest.js +1 -0
  30. package/dist/plugin-manifest.js.map +1 -1
  31. package/package.json +9 -4
  32. package/src/authored-flow-executor.ts +25 -1
  33. package/src/babysitter-catalog-export.ts +44 -0
  34. package/src/cloud-transcript-codex.ts +429 -0
  35. package/src/cloud-transcript-types.ts +214 -0
  36. package/src/cloud-transcript.ts +44 -136
  37. package/src/flow-extension-loader.ts +87 -0
  38. package/src/plugin-manifest.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-manifest.js","sourceRoot":"","sources":["../src/plugin-manifest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin-manifest.js","sourceRoot":"","sources":["../src/plugin-manifest.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relayflows/sdk",
3
- "version": "2.0.25",
3
+ "version": "2.0.27",
4
4
  "description": "TypeScript-first authoring SDK for Relayflows. Compiles specs; speaks the journal protocol.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -47,9 +47,9 @@
47
47
  "license": "Apache-2.0",
48
48
  "dependencies": {
49
49
  "@modelcontextprotocol/sdk": "^1.30.0",
50
- "@relayfile/adapter-core": "0.5.26",
51
- "@relayfile/relay-helpers": "0.4.11",
52
- "@relayflows/surface": "2.0.25",
50
+ "@relayfile/adapter-core": "0.6.2",
51
+ "@relayfile/relay-helpers": "0.4.12",
52
+ "@relayflows/surface": "2.0.27",
53
53
  "@types/js-yaml": "^4.0.9",
54
54
  "ai-hist": "0.4.1",
55
55
  "ajv": "^8.17.1",
@@ -80,5 +80,10 @@
80
80
  "@agent-relay/sdk": {
81
81
  "optional": true
82
82
  }
83
+ },
84
+ "overrides": {
85
+ "@relayflows/surface": {
86
+ "@relayfile/relay-helpers": "$@relayfile/relay-helpers"
87
+ }
83
88
  }
84
89
  }
@@ -44,8 +44,14 @@ import {
44
44
  } from './authored-flow-operation.js';
45
45
  import { AuthoredFlowLifecycle } from './authored-flow-lifecycle.js';
46
46
  import { JournalClient } from './journal-client.js';
47
+ import { PluginError } from './plugin-manifest.js';
47
48
  import { createHookEvaluator } from './authored-hooks.js';
48
- import { probeFlowExtension, type LoadedFlowExtension } from './flow-extension-loader.js';
49
+ import {
50
+ extensionHandlerForHostedDispatch,
51
+ probeFlowExtension,
52
+ type HostedExtensionDispatch,
53
+ type LoadedFlowExtension,
54
+ } from './flow-extension-loader.js';
49
55
  import type {
50
56
  CompletionReason as ProtocolCompletionReason,
51
57
  RunCompletionReason as ProtocolRunCompletionReason,
@@ -152,6 +158,11 @@ export interface ExecuteAuthoredFlowOptions {
152
158
  readonly rootRunId?: string;
153
159
  /** Installed flow-extension plugins, in lock order, so `f.hook` can AND-compose them. */
154
160
  readonly extensions?: readonly LoadedFlowExtension[];
161
+ /**
162
+ * Server-authenticated integration delivery authority. Never derive this
163
+ * from authored input: direct `/workflows/run` callers control that JSON.
164
+ */
165
+ readonly extensionDispatch?: HostedExtensionDispatch;
155
166
  }
156
167
 
157
168
  export async function executeAuthoredFlow<Input = undefined>(
@@ -175,6 +186,7 @@ export async function executeAuthoredFlow<Input = undefined>(
175
186
  ...(options.onWait !== undefined ? { onWait: options.onWait } : {}),
176
187
  };
177
188
  const definition = getDefinition<Input>(handle);
189
+ const hostedHandler = extensionHandlerForHostedDispatch(options.extensionDispatch, options.extensions ?? []);
178
190
  const headerFields = Object.keys(definition.header).filter(key => key !== 'tools' && key !== 'budget' && key !== 'memory' && key !== 'version' && key !== 'hooks');
179
191
  if (definition.header.tools && Object.keys(definition.header.tools).some(key => !['mcp', ...helperProviders.map(p => p.namespace)].includes(key))) headerFields.push('tools');
180
192
  if (definition.header.tools?.relayfile !== undefined) headerFields.push('tools.relayfile');
@@ -195,6 +207,18 @@ export async function executeAuthoredFlow<Input = undefined>(
195
207
  for (const extension of options.extensions ?? []) {
196
208
  if (extension.manifest !== undefined) await probeFlowExtension(extension.manifest);
197
209
  }
210
+ if (hostedHandler !== undefined) {
211
+ // A schema-2 entry is ordinary authored JavaScript. Passing it the base
212
+ // context would not constrain direct Node access, f.run, helpers, MCP or
213
+ // agent harnesses to manifest.permissions; those declarations are still
214
+ // explicitly unenforced by gate 8 / #442. Refuse before either body runs.
215
+ // A later isolated runtime may replace this gate only when it can prove
216
+ // the manifest boundary, not merely proxy selected Ctx properties.
217
+ throw new PluginError(
218
+ 'plugin_unsupported',
219
+ `${hostedHandler.extension.name}: hosted extension handlers require enforced manifest permission isolation (gate 8 / #442).`,
220
+ );
221
+ }
198
222
 
199
223
  const budget = new AuthoredBudget(definition.header.budget);
200
224
  if (definition.header.memory?.agent === true) {
@@ -0,0 +1,44 @@
1
+ import { assertBaseCompatible } from './flow-extension-compat.js';
2
+ import { resolveExtensionSubmission, type FlowExtensionSubmission } from './flow-extension-submit.js';
3
+ import { canonicalPluginRef, parseCanonicalPluginRef } from './plugin-source.js';
4
+
5
+ export interface BabysitterCatalogPin {
6
+ readonly ref: string;
7
+ readonly digest: string;
8
+ readonly manifestSha256: string;
9
+ }
10
+
11
+ /** Export reviewed bytes, never execute the entry or infer runtime readiness. */
12
+ export async function exportBabysitterCatalogBundle(
13
+ pin: BabysitterCatalogPin,
14
+ options: Parameters<typeof resolveExtensionSubmission>[1] = {},
15
+ ): Promise<FlowExtensionSubmission> {
16
+ const source = parseCanonicalPluginRef(pin.ref);
17
+ if (canonicalPluginRef(source) !== pin.ref || source.owner !== 'AgentWorkforce' || source.repo !== 'flows') {
18
+ throw new Error('Babysitter catalog source must be a canonical AgentWorkforce/flows commit ref.');
19
+ }
20
+ if (![pin.digest, pin.manifestSha256].every(value => /^[0-9a-f]{64}$/.test(value))) {
21
+ throw new Error('Expected reviewed bundle and manifest SHA-256 digests.');
22
+ }
23
+ const bundle = await resolveExtensionSubmission(pin.ref, options);
24
+ if (bundle.ref !== pin.ref || bundle.digest !== pin.digest || bundle.manifestSha256 !== pin.manifestSha256) {
25
+ throw new Error('Babysitter artifact differs from the reviewed pin.');
26
+ }
27
+ const manifest = bundle.manifest;
28
+ if (manifest.name !== 'babysitter') throw new Error('Expected the babysitter extension.');
29
+ assertBaseCompatible(manifest, { name: 'software-factory' });
30
+ const p = manifest.permissions;
31
+ const exactly = (values: readonly string[], expected: string) => values.length === 1 && values[0] === expected;
32
+ if (!exactly(p.integrations, 'github') || !exactly(p.harnesses, 'codex') || p.mcp.length !== 0
33
+ || !exactly(p.writes, 'cloud:babysitter-turn')) {
34
+ throw new Error('Babysitter requires only github, codex, no MCP, and cloud:babysitter-turn.');
35
+ }
36
+ if (!manifest.extends.handlers || manifest.extends.hooks.length !== 0) {
37
+ throw new Error('Native Babysitter must contribute handlers only, without merge hooks.');
38
+ }
39
+ // The validator normalizes optional source fields. The wire manifest must
40
+ // remain the actual digest-bound JSON so Cloud can independently parse it.
41
+ const file = bundle.files.find(entry => entry.path === 'flows-plugin.json')!;
42
+ const rawManifest = JSON.parse(Buffer.from(file.content, file.encoding).toString('utf8'));
43
+ return { ...bundle, manifest: rawManifest };
44
+ }
@@ -0,0 +1,429 @@
1
+ // The Codex half of `flows logs --step`: `codex exec --json` JSONL.
2
+ //
3
+ // The vocabulary was captured, not guessed. Every frame shape read here came
4
+ // out of a real `codex exec --json` run of codex-cli 0.155.1 on 2026-09-21;
5
+ // the captures are the two fixtures under `tests/fixtures/`, and the event and
6
+ // item names are the ones the binary's own enums carry (`thread.started`,
7
+ // `turn.started`, `turn.completed`, `turn.failed`, `item.started`,
8
+ // `item.updated`, `item.completed`, `error`; items `agent_message`,
9
+ // `reasoning`, `command_execution`, `file_change`, `mcp_tool_call`,
10
+ // `web_search`, `todo_list`, `error`). `web_search` and `todo_list` keep the
11
+ // unknown-frame placeholder: naming an item type is not the same as knowing
12
+ // which of its fields carry what, and a placeholder is honest.
13
+ //
14
+ // The two rules `cloud-transcript.ts` states hold here too. Nothing is
15
+ // dropped: an item this module cannot read becomes the placeholder line rather
16
+ // than vanishing or being reported as a success. And every provider string is
17
+ // redacted whole before it is bounded, because a truncated credential still
18
+ // matches nothing and is still most of a credential.
19
+
20
+ import {
21
+ ERROR_MAX_CHARS, OUTPUT_MAX_CHARS, OUTPUT_MAX_LINES, PATH_MAX_CHARS, TARGET_MAX_CHARS,
22
+ isRecord, num, oneLine, safe, separator, str, thousands,
23
+ type TranscriptEntry, type TranscriptToolCodex,
24
+ } from './cloud-transcript-types.js';
25
+
26
+ /** Frame types that make a log a transcript this module can render. */
27
+ export const CODEX_FRAME_TYPES: ReadonlySet<string> = new Set([
28
+ 'thread.started', 'turn.started', 'turn.completed', 'turn.failed',
29
+ 'item.started', 'item.updated', 'item.completed', 'error',
30
+ ]);
31
+
32
+ /** Item types whose lifecycles are matched and whose fields are read. */
33
+ const SUPPORTED_ITEMS: ReadonlySet<string> = new Set([
34
+ 'agent_message', 'reasoning', 'command_execution', 'file_change', 'mcp_tool_call', 'error',
35
+ ]);
36
+
37
+ const ITEM_FRAMES: ReadonlySet<string> = new Set(['item.started', 'item.updated', 'item.completed']);
38
+
39
+ /** Calls are numbered; an `apply_patch` is file activity, not a call. */
40
+ const NUMBERED_ITEMS: ReadonlySet<string> = new Set(['command_execution', 'mcp_tool_call']);
41
+
42
+ interface Lifecycle {
43
+ snapshots: Array<Record<string, unknown>>;
44
+ complete: boolean;
45
+ /** The index of the last frame of this lifecycle: where the entry is emitted. */
46
+ at: number;
47
+ }
48
+
49
+ export interface CodexIndex {
50
+ /** Every frame index that belongs to a matched lifecycle. */
51
+ matched: Set<number>;
52
+ /** The lifecycle to emit at this frame index. */
53
+ emit: Map<number, Lifecycle>;
54
+ }
55
+
56
+ /**
57
+ * Match item lifecycles locally and in order, rather than by a global set of
58
+ * completed ids.
59
+ *
60
+ * `partition` changes at every attempt marker, thread and turn boundary, so a
61
+ * reused id never reaches across one: an attempt that was killed mid-command
62
+ * keeps its unfinished call even when the next attempt completes the same id.
63
+ * Within a partition a lifecycle is keyed by id *and* item type -- two items
64
+ * that merely share an id are two items -- and a start that arrives after that
65
+ * key already completed opens a new one.
66
+ */
67
+ export function indexCodexItems(frames: ReadonlyArray<{ frame: unknown }>): CodexIndex {
68
+ const matched = new Set<number>();
69
+ const emit = new Map<number, Lifecycle>();
70
+ const open = new Map<string, Lifecycle>();
71
+ let partition = 0;
72
+ for (let index = 0; index < frames.length; index += 1) {
73
+ const frame = frames[index]!.frame;
74
+ if (!isRecord(frame)) continue;
75
+ const type = str(frame['type']);
76
+ if (type === null) continue;
77
+ if (type.startsWith('relayflow.attempt') || type === 'thread.started' || type.startsWith('turn.')) {
78
+ partition += 1;
79
+ open.clear();
80
+ continue;
81
+ }
82
+ if (!ITEM_FRAMES.has(type)) continue;
83
+ const item = isRecord(frame['item']) ? frame['item'] : null;
84
+ if (item === null) continue;
85
+ const id = str(item['id']);
86
+ const itemType = str(item['type']);
87
+ // No id is not a correlation key, and an unsupported item is not one this
88
+ // module can merge. Either way the frame stands where it is.
89
+ if (id === null || itemType === null || !SUPPORTED_ITEMS.has(itemType)) continue;
90
+ const key = `${partition}\u0000${id}\u0000${itemType}`;
91
+ let lifecycle = open.get(key);
92
+ if (lifecycle === undefined || lifecycle.complete) {
93
+ lifecycle = { snapshots: [], complete: false, at: index };
94
+ open.set(key, lifecycle);
95
+ }
96
+ lifecycle.snapshots.push(item);
97
+ lifecycle.complete ||= type === 'item.completed';
98
+ emit.delete(lifecycle.at);
99
+ lifecycle.at = index;
100
+ emit.set(index, lifecycle);
101
+ matched.add(index);
102
+ }
103
+ return { matched, emit };
104
+ }
105
+
106
+ /** Where the numbering lives; the parse loop resets it at an attempt boundary. */
107
+ export interface CodexState { seq: number }
108
+
109
+ interface Context {
110
+ clean: (text: string) => string;
111
+ state: CodexState;
112
+ /** The whole source line, for a placeholder's size. */
113
+ line: string;
114
+ type: string;
115
+ }
116
+
117
+ function bounded(text: string, clean: (t: string) => string, limit = TARGET_MAX_CHARS): string {
118
+ return oneLine(clean(text), limit);
119
+ }
120
+
121
+ /**
122
+ * The textual content of an MCP result, as one string. `content` is a string
123
+ * or the MCP content-block array; a block that carries no text (an image, say)
124
+ * contributes nothing, exactly as `textChars` counts nothing for it.
125
+ */
126
+ function mcpResultText(result: Record<string, unknown>): string | null {
127
+ const content = result['content'];
128
+ if (typeof content === 'string') return content.length === 0 ? null : content;
129
+ const parts: string[] = [];
130
+ if (Array.isArray(content)) {
131
+ for (const block of content) {
132
+ if (!isRecord(block)) continue;
133
+ const text = str(block['text']);
134
+ if (text !== null && text.length > 0) parts.push(text);
135
+ }
136
+ }
137
+ if (parts.length > 0) return parts.join('\n');
138
+ // A result can be structured only; showing it beats reporting a size alone.
139
+ const structured = result['structured_content'];
140
+ return structured === undefined || structured === null ? null : JSON.stringify(structured);
141
+ }
142
+
143
+ /** A result's size, never its content. */
144
+ function textChars(content: unknown): number {
145
+ if (typeof content === 'string') return content.length;
146
+ if (!Array.isArray(content)) return 0;
147
+ let total = 0;
148
+ for (const block of content) if (isRecord(block)) total += str(block['text'])?.length ?? 0;
149
+ return total;
150
+ }
151
+
152
+ /**
153
+ * Redact the whole output, then cut it. Both bounds are applied and either one
154
+ * sets the flag, so the reader is told the excerpt is short of the size the
155
+ * call reported.
156
+ */
157
+ function excerpt(output: string, clean: (text: string) => string): { text: string; truncated: boolean } {
158
+ const cleaned = clean(output).replace(/\n+$/u, '');
159
+ const lines = cleaned.split('\n');
160
+ let truncated = lines.length > OUTPUT_MAX_LINES;
161
+ let text = lines.slice(0, OUTPUT_MAX_LINES).join('\n');
162
+ if (text.length > OUTPUT_MAX_CHARS) { text = text.slice(0, OUTPUT_MAX_CHARS); truncated = true; }
163
+ return { text, truncated };
164
+ }
165
+
166
+ function placeholder(context: Context, itemType: string | null): TranscriptEntry {
167
+ const label = itemType === null ? context.type : `${context.type}/${itemType}`;
168
+ return { kind: 'unknown', type: context.clean(label), chars: context.line.length };
169
+ }
170
+
171
+ function tool(name: string, target: string, resultChars: number | null, isError: boolean,
172
+ codex: TranscriptToolCodex): TranscriptEntry {
173
+ return { kind: 'tool', name, target, result_chars: resultChars, is_error: isError, nested: false, codex };
174
+ }
175
+
176
+ function commandEntry(item: Record<string, unknown>, complete: boolean, context: Context,
177
+ seq: number): TranscriptEntry | null {
178
+ const command = item['command'];
179
+ if (typeof command !== 'string') return null;
180
+ const output = typeof item['aggregated_output'] === 'string' ? item['aggregated_output'] : null;
181
+ const cut = output === null ? null : excerpt(output, context.clean);
182
+ const exitCode = num(item['exit_code']);
183
+ const status = str(item['status']);
184
+ return tool('command_execution', bounded(command, context.clean), output === null ? null : output.length,
185
+ status === 'failed' || (exitCode !== null && exitCode !== 0), {
186
+ seq, status: status === null ? null : bounded(status, context.clean, 40),
187
+ exit_code: exitCode, output_excerpt: cut === null ? null : cut.text,
188
+ output_truncated: cut?.truncated === true, complete, error: null,
189
+ });
190
+ }
191
+
192
+ /** `{"message": "..."}` is the shape both `turn.failed` and a failed call use. */
193
+ function errorText(value: unknown): string | null {
194
+ if (typeof value === 'string') return value;
195
+ return isRecord(value) ? str(value['message']) : null;
196
+ }
197
+
198
+ /**
199
+ * Redact every string an argument value carries, while each one is still the
200
+ * string the provider decoded.
201
+ *
202
+ * `redact` matches an environment value literally, and `JSON.stringify` escapes
203
+ * a quote, backslash, newline or tab inside one -- so a secret containing any
204
+ * of them no longer matches its own value once serialized, and reached the page
205
+ * escaped but complete. Keys are provider strings too: an argument object built
206
+ * out of an environment dump carries the value in the name.
207
+ */
208
+ function redactLeaves(value: unknown, clean: (text: string) => string): unknown {
209
+ if (typeof value === 'string') return clean(value);
210
+ if (Array.isArray(value)) return value.map((element) => redactLeaves(element, clean));
211
+ if (!isRecord(value)) return value;
212
+ const out: Record<string, unknown> = {};
213
+ for (const [key, element] of Object.entries(value)) out[clean(key)] = redactLeaves(element, clean);
214
+ return out;
215
+ }
216
+
217
+ function mcpEntry(item: Record<string, unknown>, complete: boolean, context: Context,
218
+ seq: number): TranscriptEntry | null {
219
+ const server = str(item['server']);
220
+ const name = str(item['tool']);
221
+ if (server === null && name === null) return null;
222
+ const args = item['arguments'];
223
+ // Redacted twice, bounded once, and in that order. The leaves go first,
224
+ // before serialization can escape a secret out of its own match; the
225
+ // serialized form is redacted again by the `bounded` call below, because
226
+ // `redact` recognises a credential field by its name and a leaf standing on
227
+ // its own has no name left to recognise. Only then is the line cut.
228
+ const rendered = args === undefined || args === null
229
+ ? '' : ` ${JSON.stringify(redactLeaves(args, context.clean))}`;
230
+ const result = isRecord(item['result']) ? item['result'] : null;
231
+ const structured = result === null ? null : result['structured_content'];
232
+ const failure = errorText(item['error']);
233
+ const status = str(item['status']);
234
+ // The result is evidence: bound and redact it exactly as command output is,
235
+ // instead of reporting a size the reader then has to fetch with `--raw`.
236
+ const text = result === null ? null : mcpResultText(result);
237
+ const cut = text === null ? null : excerpt(text, context.clean);
238
+ return tool('mcp_tool_call', bounded(`${server ?? '?'}/${name ?? '?'}${rendered}`, context.clean),
239
+ result === null ? null : textChars(result['content']) + (structured === undefined || structured === null
240
+ ? 0 : JSON.stringify(structured).length),
241
+ status === 'failed' || failure !== null, {
242
+ seq, status: status === null ? null : bounded(status, context.clean, 40),
243
+ exit_code: null, output_excerpt: cut === null ? null : cut.text,
244
+ output_truncated: cut?.truncated === true, complete,
245
+ error: failure === null ? null : bounded(failure, context.clean, ERROR_MAX_CHARS),
246
+ });
247
+ }
248
+
249
+ function fileChangeEntry(item: Record<string, unknown>, complete: boolean, context: Context): TranscriptEntry | null {
250
+ const raw = item['changes'];
251
+ if (!Array.isArray(raw)) return null;
252
+ const changes: Array<{ path: string | null; change: string | null }> = [];
253
+ let malformed = 0;
254
+ for (const change of raw) {
255
+ if (!isRecord(change)) { malformed += 1; continue; }
256
+ const path = str(change['path']);
257
+ const kind = str(change['kind']);
258
+ if (path === null && kind === null) { malformed += 1; continue; }
259
+ changes.push({
260
+ path: path === null ? null : bounded(path, context.clean, PATH_MAX_CHARS),
261
+ change: kind === null ? null : bounded(kind, context.clean, 40),
262
+ });
263
+ }
264
+ const status = str(item['status']);
265
+ return {
266
+ kind: 'file_change', changes, malformed,
267
+ status: status === null ? null : bounded(status, context.clean, 40), complete,
268
+ };
269
+ }
270
+
271
+ function itemEntries(item: Record<string, unknown>, complete: boolean, context: Context): TranscriptEntry[] {
272
+ const itemType = str(item['type']);
273
+ if (itemType === 'agent_message') {
274
+ const text = item['text'];
275
+ if (typeof text !== 'string') return [placeholder(context, itemType)];
276
+ return [{ kind: 'message', role: 'assistant', text: context.clean(text), nested: false,
277
+ ...(complete ? {} : { complete: false }) }];
278
+ }
279
+ if (itemType === 'reasoning') {
280
+ const text = item['text'];
281
+ // The count, never the text -- the one thing Claude's renderer refuses to
282
+ // print, and Codex is not the place that starts printing it.
283
+ if (typeof text !== 'string') return [placeholder(context, itemType)];
284
+ return [{ kind: 'thinking', chars: text.length, nested: false }];
285
+ }
286
+ if (itemType === 'error') {
287
+ const message = errorText(item['message']);
288
+ if (message === null) return [placeholder(context, itemType)];
289
+ return [{ kind: 'error', source: 'item', message: bounded(message, context.clean, ERROR_MAX_CHARS) }];
290
+ }
291
+ if (itemType === 'command_execution' || itemType === 'mcp_tool_call') {
292
+ // The number is only spent on a call this module could actually read, so a
293
+ // malformed item leaves no gap in the sequence.
294
+ const seq = context.state.seq + 1;
295
+ const entry = itemType === 'command_execution'
296
+ ? commandEntry(item, complete, context, seq) : mcpEntry(item, complete, context, seq);
297
+ if (entry === null) return [placeholder(context, itemType)];
298
+ context.state.seq = seq;
299
+ return [entry];
300
+ }
301
+ if (itemType === 'file_change') {
302
+ const entry = fileChangeEntry(item, complete, context);
303
+ return [entry ?? placeholder(context, itemType)];
304
+ }
305
+ return [placeholder(context, itemType)];
306
+ }
307
+
308
+ const NO_USAGE = {
309
+ tokens_in: null, tokens_out: null, cache_read: null, cache_creation: null, reasoning_out: null,
310
+ } as const;
311
+
312
+ /**
313
+ * One Codex frame's entries, or undefined when the frame belongs to a
314
+ * lifecycle that is reported at a later frame.
315
+ */
316
+ export function codexFrame(
317
+ frame: Record<string, unknown>, type: string, index: number, index_: CodexIndex,
318
+ state: CodexState, clean: (text: string) => string, line: string,
319
+ ): TranscriptEntry[] | undefined {
320
+ const context: Context = { clean, state, line, type };
321
+ if (type === 'thread.started') {
322
+ const id = str(frame['thread_id']);
323
+ return [{ kind: 'thread', thread_id: id === null ? null : bounded(id, clean) }];
324
+ }
325
+ if (type === 'turn.started') return [{ kind: 'turn', phase: 'started', ...NO_USAGE }];
326
+ if (type === 'turn.completed') {
327
+ const usage = isRecord(frame['usage']) ? frame['usage'] : null;
328
+ return [{
329
+ kind: 'turn', phase: 'completed',
330
+ tokens_in: usage === null ? null : num(usage['input_tokens']),
331
+ tokens_out: usage === null ? null : num(usage['output_tokens']),
332
+ cache_read: usage === null ? null : num(usage['cached_input_tokens']),
333
+ cache_creation: usage === null ? null : num(usage['cache_write_input_tokens']),
334
+ reasoning_out: usage === null ? null : num(usage['reasoning_output_tokens']),
335
+ }];
336
+ }
337
+ if (type === 'turn.failed' || type === 'error') {
338
+ const message = errorText(type === 'error' ? frame['message'] : frame['error']);
339
+ // A failure with no readable message is still a failure; it must not fall
340
+ // through to anything that reads like success.
341
+ const entry: TranscriptEntry = {
342
+ kind: 'error', source: type,
343
+ message: message === null ? '(no message)' : bounded(message, clean, ERROR_MAX_CHARS),
344
+ };
345
+ return type === 'turn.failed' ? [{ kind: 'turn', phase: 'failed', ...NO_USAGE }, entry] : [entry];
346
+ }
347
+ // Everything left in `CODEX_FRAME_TYPES` is an `item.*` frame.
348
+ const lifecycle = index_.emit.get(index);
349
+ if (lifecycle === undefined) {
350
+ // Part of a lifecycle reported later, or an item this module does not
351
+ // correlate -- no id, an unsupported type, a malformed frame.
352
+ if (index_.matched.has(index)) return [];
353
+ const item = isRecord(frame['item']) ? frame['item'] : null;
354
+ if (item === null) return [placeholder(context, null)];
355
+ return itemEntries(item, type === 'item.completed', context);
356
+ }
357
+ // The latest snapshot wins field by field, so a completion's null exit code
358
+ // is not overwritten by the zero-length output its start reported.
359
+ const merged = Object.assign({}, ...lifecycle.snapshots) as Record<string, unknown>;
360
+ return itemEntries(merged, lifecycle.complete, context);
361
+ }
362
+
363
+ function indented(text: string): string[] {
364
+ return text.split('\n').map((line) => ` ${safe(line)}`);
365
+ }
366
+
367
+ /** The Codex entry kinds. Undefined for everything the Claude renderer owns. */
368
+ export function renderCodexEntry(entry: TranscriptEntry): string[] | undefined {
369
+ switch (entry.kind) {
370
+ case 'thread':
371
+ return [`session codex${entry.thread_id === null ? '' : ` · thread ${safe(entry.thread_id)}`}`];
372
+ case 'turn': {
373
+ if (entry.phase === 'started') return [separator('turn')];
374
+ if (entry.phase === 'failed') return [separator('turn failed')];
375
+ const facts = [
376
+ entry.tokens_in === null && entry.tokens_out === null ? null
377
+ : `${entry.tokens_in === null ? '?' : thousands(entry.tokens_in)} in`
378
+ + ` / ${entry.tokens_out === null ? '?' : thousands(entry.tokens_out)} out`,
379
+ entry.cache_read === null ? null : `${thousands(entry.cache_read)} cache read`,
380
+ entry.cache_creation === null ? null : `${thousands(entry.cache_creation)} cache write`,
381
+ entry.reasoning_out === null ? null : `${thousands(entry.reasoning_out)} reasoning out`,
382
+ ].filter((fact): fact is string => fact !== null);
383
+ return [separator(`turn complete${facts.length === 0 ? '' : ` · ${facts.join(' · ')}`}`)];
384
+ }
385
+ case 'error':
386
+ return [entry.source === 'error' ? 'error:' : `error (${safe(entry.source)}):`, ...entry.message.split('\n').map((line) => ` ${safe(line)}`)];
387
+ case 'file_change': {
388
+ const total = entry.changes.length + entry.malformed;
389
+ const state = [
390
+ entry.status === null ? null : safe(entry.status),
391
+ entry.complete ? null : 'no completion frame',
392
+ ].filter((fact): fact is string => fact !== null);
393
+ const lines = [` files ${total} change${total === 1 ? '' : 's'}`
394
+ + `${state.length === 0 ? '' : ` · ${state.join(' · ')}`}`];
395
+ for (const change of entry.changes) {
396
+ lines.push(` ${safe(change.change ?? '?')} ${safe(change.path ?? '(no path)')}`);
397
+ }
398
+ if (entry.malformed > 0) {
399
+ lines.push(` ${entry.malformed} change${entry.malformed === 1 ? '' : 's'}`
400
+ + ' not rendered here — see --raw');
401
+ }
402
+ return lines;
403
+ }
404
+ case 'tool': {
405
+ if (entry.codex === undefined) return undefined;
406
+ const codex = entry.codex;
407
+ // The size stays on the line whatever happened: a reviewer reading a
408
+ // failed step needs the exit code, the status and the size together.
409
+ const facts = [
410
+ entry.result_chars === null ? 'no result' : `${thousands(entry.result_chars)} chars`,
411
+ codex.exit_code === null ? null : `exit ${codex.exit_code}`,
412
+ codex.status === null ? null : safe(codex.status),
413
+ codex.complete ? null : 'no completion frame',
414
+ ].filter((fact): fact is string => fact !== null);
415
+ const lines = [` tool ${codex.seq} ${safe(entry.name)} ${safe(entry.target ?? '')} → ${facts.join(' · ')}`];
416
+ if (codex.error !== null) lines.push(...indented(`error ${codex.error}`));
417
+ if (codex.output_excerpt !== null && codex.output_excerpt.length > 0) {
418
+ lines.push(...indented(codex.output_excerpt));
419
+ }
420
+ if (codex.output_truncated) {
421
+ lines.push(` … excerpt cut at ${thousands(OUTPUT_MAX_LINES)} lines`
422
+ + ` / ${thousands(OUTPUT_MAX_CHARS)} chars — see --raw`);
423
+ }
424
+ return lines;
425
+ }
426
+ default:
427
+ return undefined;
428
+ }
429
+ }