@relayflows/sdk 2.0.32 → 2.0.33
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/dist/cloud-mirror-step.d.ts +12 -0
- package/dist/cloud-mirror-step.d.ts.map +1 -1
- package/dist/cloud-mirror-step.js +20 -1
- package/dist/cloud-mirror-step.js.map +1 -1
- package/dist/cloud-transcript-codex.d.ts.map +1 -1
- package/dist/cloud-transcript-codex.js +90 -4
- package/dist/cloud-transcript-codex.js.map +1 -1
- package/dist/cloud-transcript-types.d.ts +19 -1
- package/dist/cloud-transcript-types.d.ts.map +1 -1
- package/dist/cloud-transcript-types.js.map +1 -1
- package/dist/cloud-transcript.d.ts +1 -1
- package/dist/cloud-transcript.d.ts.map +1 -1
- package/dist/cloud-transcript.js +10 -0
- package/dist/cloud-transcript.js.map +1 -1
- package/dist/helper-reference.d.ts.map +1 -1
- package/dist/helper-reference.js +134 -6
- package/dist/helper-reference.js.map +1 -1
- package/dist/hosted-extension-sandbox.js +1 -1
- package/package.json +2 -2
- package/src/cloud-mirror-step.ts +33 -1
- package/src/cloud-transcript-codex.ts +96 -4
- package/src/cloud-transcript-types.ts +18 -1
- package/src/cloud-transcript.ts +9 -1
- package/src/helper-reference.ts +131 -6
- package/src/hosted-extension-sandbox.ts +1 -1
package/dist/helper-reference.js
CHANGED
|
@@ -27,7 +27,8 @@ export function helperNamespacesUsed(body, root) {
|
|
|
27
27
|
if (program === null)
|
|
28
28
|
return textFallback(body, root);
|
|
29
29
|
const used = new Set();
|
|
30
|
-
|
|
30
|
+
const scope = { rootFunctionFound: false };
|
|
31
|
+
walkReferences(program, root, false, scope, (node) => {
|
|
31
32
|
if (node.type !== 'MemberExpression')
|
|
32
33
|
return;
|
|
33
34
|
const object = node.object;
|
|
@@ -106,9 +107,46 @@ function textFallback(body, root) {
|
|
|
106
107
|
}
|
|
107
108
|
return used;
|
|
108
109
|
}
|
|
109
|
-
/**
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Depth-first over references that still resolve to the outer flow context.
|
|
112
|
+
*
|
|
113
|
+
* A nested callback/method parameter or lexical destructuring can reuse the
|
|
114
|
+
* context's spelling while binding an unrelated value. Counting its members
|
|
115
|
+
* would refuse the flow for a mount it never touches. Acorn gives us binding
|
|
116
|
+
* shapes, so scopes are tracked as syntax instead of guessed from text.
|
|
117
|
+
*/
|
|
118
|
+
function walkReferences(node, root, shadowed, state, visit) {
|
|
119
|
+
if (isFunction(node)) {
|
|
120
|
+
const parameters = Array.isArray(node.params) ? node.params.filter(isNode) : [];
|
|
121
|
+
const bindsParameter = parameters.some(parameter => patternBinds(parameter, root));
|
|
122
|
+
let bodyHidden = shadowed;
|
|
123
|
+
const isRootFunction = bindsParameter && !state.rootFunctionFound;
|
|
124
|
+
if (isRootFunction)
|
|
125
|
+
state.rootFunctionFound = true;
|
|
126
|
+
else if (bindsParameter || (isNode(node.id) && patternBinds(node.id, root)) || functionVarBinds(node, root)) {
|
|
127
|
+
bodyHidden = true;
|
|
128
|
+
}
|
|
129
|
+
// Parameter initializers run before the function body and are outside a
|
|
130
|
+
// body-level `var` scope. Keep them visible unless the parameter list
|
|
131
|
+
// itself binds the root name (in which case all parameter references are
|
|
132
|
+
// local to that parameter environment).
|
|
133
|
+
const parameterHidden = shadowed || (bindsParameter && !isRootFunction);
|
|
134
|
+
for (const parameter of parameters)
|
|
135
|
+
walkReferences(parameter, root, parameterHidden, state, visit);
|
|
136
|
+
const body = isNode(node.body) ? node.body : undefined;
|
|
137
|
+
if (body !== undefined)
|
|
138
|
+
walkReferences(body, root, bodyHidden, state, visit);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
let hidden = shadowed;
|
|
142
|
+
if (node.type === 'BlockStatement' && blockBinds(node, root)) {
|
|
143
|
+
hidden = true;
|
|
144
|
+
}
|
|
145
|
+
else if (node.type === 'CatchClause' && isNode(node.param) && patternBinds(node.param, root)) {
|
|
146
|
+
hidden = true;
|
|
147
|
+
}
|
|
148
|
+
if (!hidden)
|
|
149
|
+
visit(node);
|
|
112
150
|
for (const key of Object.keys(node)) {
|
|
113
151
|
if (key === 'type' || key === 'start' || key === 'end' || key === 'loc')
|
|
114
152
|
continue;
|
|
@@ -116,13 +154,103 @@ function walk(node, visit) {
|
|
|
116
154
|
if (Array.isArray(child)) {
|
|
117
155
|
for (const entry of child)
|
|
118
156
|
if (isNode(entry))
|
|
119
|
-
|
|
157
|
+
walkReferences(entry, root, hidden, state, visit);
|
|
120
158
|
}
|
|
121
159
|
else if (isNode(child)) {
|
|
122
|
-
|
|
160
|
+
walkReferences(child, root, hidden, state, visit);
|
|
123
161
|
}
|
|
124
162
|
}
|
|
125
163
|
}
|
|
164
|
+
function isFunction(node) {
|
|
165
|
+
return node.type === 'ArrowFunctionExpression'
|
|
166
|
+
|| node.type === 'FunctionExpression'
|
|
167
|
+
|| node.type === 'FunctionDeclaration';
|
|
168
|
+
}
|
|
169
|
+
/** Lexical declarations bind across their complete block, including TDZ. */
|
|
170
|
+
function blockBinds(node, root) {
|
|
171
|
+
const statements = Array.isArray(node.body) ? node.body.filter(isNode) : [];
|
|
172
|
+
return statements.some((statement) => {
|
|
173
|
+
if (statement.type === 'VariableDeclaration' && statement.kind !== 'var') {
|
|
174
|
+
const declarations = Array.isArray(statement.declarations)
|
|
175
|
+
? statement.declarations.filter(isNode) : [];
|
|
176
|
+
return declarations.some(declaration => isNode(declaration.id) && patternBinds(declaration.id, root));
|
|
177
|
+
}
|
|
178
|
+
return (statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration')
|
|
179
|
+
&& isNode(statement.id) && patternBinds(statement.id, root);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/** Identifier occurrences that introduce bindings, including nested patterns. */
|
|
183
|
+
function patternBinds(pattern, root) {
|
|
184
|
+
switch (pattern.type) {
|
|
185
|
+
case 'Identifier':
|
|
186
|
+
return pattern.name === root;
|
|
187
|
+
case 'AssignmentPattern':
|
|
188
|
+
return isNode(pattern.left) && patternBinds(pattern.left, root);
|
|
189
|
+
case 'RestElement':
|
|
190
|
+
return isNode(pattern.argument) && patternBinds(pattern.argument, root);
|
|
191
|
+
case 'ArrayPattern': {
|
|
192
|
+
const elements = Array.isArray(pattern.elements) ? pattern.elements : [];
|
|
193
|
+
return elements.some(element => isNode(element) && patternBinds(element, root));
|
|
194
|
+
}
|
|
195
|
+
case 'ObjectPattern': {
|
|
196
|
+
const properties = Array.isArray(pattern.properties) ? pattern.properties : [];
|
|
197
|
+
return properties.some(property => {
|
|
198
|
+
if (!isNode(property))
|
|
199
|
+
return false;
|
|
200
|
+
if (property.type === 'RestElement') {
|
|
201
|
+
return isNode(property.argument) && patternBinds(property.argument, root);
|
|
202
|
+
}
|
|
203
|
+
// Object keys are labels, not bindings. Only the value side introduces
|
|
204
|
+
// the local (including a default-value AssignmentPattern's left side).
|
|
205
|
+
return property.type === 'Property'
|
|
206
|
+
&& isNode(property.value)
|
|
207
|
+
&& patternBinds(property.value, root);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
default:
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/** `var` is function-scoped, so a nested function's declaration hides the
|
|
215
|
+
* outer flow context for its entire body, including code before the
|
|
216
|
+
* declaration. Nested functions have their own var scopes and are skipped. */
|
|
217
|
+
function functionVarBinds(node, root) {
|
|
218
|
+
const body = isNode(node.body) ? node.body : undefined;
|
|
219
|
+
if (body === undefined)
|
|
220
|
+
return false;
|
|
221
|
+
let found = false;
|
|
222
|
+
const scan = (current) => {
|
|
223
|
+
if (found)
|
|
224
|
+
return;
|
|
225
|
+
if (current !== body && isFunction(current))
|
|
226
|
+
return;
|
|
227
|
+
if (current.type === 'VariableDeclaration' && current.kind === 'var') {
|
|
228
|
+
const declarations = Array.isArray(current.declarations)
|
|
229
|
+
? current.declarations.filter(isNode) : [];
|
|
230
|
+
if (declarations.some(declaration => isNode(declaration.id) && patternBinds(declaration.id, root))) {
|
|
231
|
+
found = true;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
for (const key of Object.keys(current)) {
|
|
236
|
+
if (key === 'type' || key === 'start' || key === 'end' || key === 'loc')
|
|
237
|
+
continue;
|
|
238
|
+
const child = current[key];
|
|
239
|
+
if (Array.isArray(child)) {
|
|
240
|
+
for (const entry of child)
|
|
241
|
+
if (isNode(entry))
|
|
242
|
+
scan(entry);
|
|
243
|
+
}
|
|
244
|
+
else if (isNode(child)) {
|
|
245
|
+
scan(child);
|
|
246
|
+
}
|
|
247
|
+
if (found)
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
scan(body);
|
|
252
|
+
return found;
|
|
253
|
+
}
|
|
126
254
|
function isNode(value) {
|
|
127
255
|
return typeof value === 'object' && value !== null && typeof value.type === 'string';
|
|
128
256
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper-reference.js","sourceRoot":"","sources":["../src/helper-reference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,IAAY;IAC7D,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,
|
|
1
|
+
{"version":3,"file":"helper-reference.js","sourceRoot":"","sources":["../src/helper-reference.ts"],"names":[],"mappings":"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"}
|
|
@@ -69,7 +69,7 @@ const ADDRESS_SPACE_BYTES = 16 * 1024 * 1024 * 1024;
|
|
|
69
69
|
const DATA_BYTES = 3 * 1024 * 1024 * 1024;
|
|
70
70
|
const SURFACE_RUNTIME_SHA256 = OBJECT_FREEZE({
|
|
71
71
|
'flow.js': '4aaeacc55de3074f4d121ce7253c3be50a93757e6540ba8159889a9450d1c05c',
|
|
72
|
-
'helpers/providers.js': '
|
|
72
|
+
'helpers/providers.js': '4eb06d0d85ca0a3434bb2dbba7407e3d95eef2dfbedaeb0e2de0c0c0ea812457',
|
|
73
73
|
'provider-trigger.js': 'e2664c65397f93fb486eb6f1e756c7cec3f88b3851d79c23567cad986f80f1ff',
|
|
74
74
|
'schedule.js': '8fe72f176a75ec0b5f26e12db7a597575c259a2e2cbb59690f9dc20a5e63940b',
|
|
75
75
|
'triggers.js': '4a3515b571a318f6c7a5661f9310bc9af43e3faf20ea39903a9b363c51258e4c',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relayflows/sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.33",
|
|
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",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
50
50
|
"@relayfile/adapter-core": "0.6.2",
|
|
51
51
|
"@relayfile/relay-helpers": "0.4.12",
|
|
52
|
-
"@relayflows/surface": "2.0.
|
|
52
|
+
"@relayflows/surface": "2.0.33",
|
|
53
53
|
"@types/js-yaml": "^4.0.9",
|
|
54
54
|
"acorn": "^8.18.0",
|
|
55
55
|
"ai-hist": "0.4.1",
|
package/src/cloud-mirror-step.ts
CHANGED
|
@@ -114,6 +114,18 @@ export interface FinalStep {
|
|
|
114
114
|
model?: string;
|
|
115
115
|
tokensInput?: number;
|
|
116
116
|
tokensOutput?: number;
|
|
117
|
+
/**
|
|
118
|
+
* True when tokens were counted but nobody priced them.
|
|
119
|
+
*
|
|
120
|
+
* The kernel already records this (`run-state.ts`'s `Spend`), and
|
|
121
|
+
* `flows status` already prints `(unmetered)` from it. Without it on the
|
|
122
|
+
* wire, Cloud cannot tell a step that genuinely cost nothing — a
|
|
123
|
+
* deterministic `echo` — from one that spent real money against an unpriced
|
|
124
|
+
* model: both arrive as an absent `costUsd` and render blank. Codex is the
|
|
125
|
+
* everyday case, because it selects its own model and never reports it, so
|
|
126
|
+
* `MODEL_PRICING` has nothing to match (`model-pricing.ts` says so outright).
|
|
127
|
+
*/
|
|
128
|
+
costUnmetered?: true;
|
|
117
129
|
costUsd?: number;
|
|
118
130
|
error?: string;
|
|
119
131
|
detail?: Record<string, unknown>;
|
|
@@ -314,6 +326,7 @@ interface AttemptRecord {
|
|
|
314
326
|
tokensIn?: number;
|
|
315
327
|
tokensOut?: number;
|
|
316
328
|
costUsd?: number;
|
|
329
|
+
costUnmetered?: boolean;
|
|
317
330
|
}
|
|
318
331
|
|
|
319
332
|
/**
|
|
@@ -346,6 +359,7 @@ function attemptsByStep(events: readonly JournalEvent[]): Map<string, AttemptRec
|
|
|
346
359
|
...(typeof result?.['total_cost_usd'] === 'number' && Number.isFinite(result['total_cost_usd'])
|
|
347
360
|
&& result['total_cost_usd'] >= 0
|
|
348
361
|
? { costUsd: result['total_cost_usd'] as number } : {}),
|
|
362
|
+
...(budget['dollars_unmetered'] === true ? { costUnmetered: true } : {}),
|
|
349
363
|
});
|
|
350
364
|
byStep.set(event.step_id, entries);
|
|
351
365
|
}
|
|
@@ -374,6 +388,23 @@ function finalStep(
|
|
|
374
388
|
// Cloud totals these rows for the run's spend — so a retried agent's earlier
|
|
375
389
|
// charges simply vanished from the run.
|
|
376
390
|
const spentUsd = attempts.reduce((total, entry) => total + (entry.costUsd ?? 0), 0);
|
|
391
|
+
const priced = attempts.some(entry => entry.costUsd !== undefined);
|
|
392
|
+
/**
|
|
393
|
+
* Any unpriced attempt makes the step's cost unknown, even when another
|
|
394
|
+
* attempt reported one.
|
|
395
|
+
*
|
|
396
|
+
* The first cut suppressed the flag whenever `spentUsd > 0`, which published
|
|
397
|
+
* a partial subtotal as though it were the whole cost: a step that spent
|
|
398
|
+
* $0.02 on attempt 1 and an unpriced amount on attempt 2 reported exactly
|
|
399
|
+
* $0.02. The two facts are not exclusive — `costUsd` is then a *lower bound*
|
|
400
|
+
* and the flag says so. `run-state.ts`'s `addSpend` keeps them together for
|
|
401
|
+
* the same reason.
|
|
402
|
+
*
|
|
403
|
+
* Presence, not a positive sum: an attempt reporting `total_cost_usd: 0` did
|
|
404
|
+
* produce a price, and testing `spentUsd === 0` would have called that step
|
|
405
|
+
* unpriced.
|
|
406
|
+
*/
|
|
407
|
+
const unmetered = attempts.some(entry => entry.costUnmetered === true);
|
|
377
408
|
const detail = stepDetail(attempts, env);
|
|
378
409
|
const verification = step.last_attempt?.verification ?? null;
|
|
379
410
|
// What the step said about itself. A gate's verdict detail is the nearest
|
|
@@ -407,7 +438,8 @@ function finalStep(
|
|
|
407
438
|
...(model === undefined ? {} : { model }),
|
|
408
439
|
...(tokensIn > 0 ? { tokensInput: Math.min(tokensIn, MAX_INT32) } : {}),
|
|
409
440
|
...(tokensOut > 0 ? { tokensOutput: Math.min(tokensOut, MAX_INT32) } : {}),
|
|
410
|
-
...(
|
|
441
|
+
...(priced ? { costUsd: spentUsd } : {}),
|
|
442
|
+
...(unmetered ? { costUnmetered: true as const } : {}),
|
|
411
443
|
...(error === undefined ? {} : { error }),
|
|
412
444
|
...(detail.detail === undefined ? {} : { detail: detail.detail }),
|
|
413
445
|
...(detail.truncated === undefined ? {} : { detailTruncated: detail.truncated }),
|
|
@@ -32,12 +32,22 @@ export const CODEX_FRAME_TYPES: ReadonlySet<string> = new Set([
|
|
|
32
32
|
/** Item types whose lifecycles are matched and whose fields are read. */
|
|
33
33
|
const SUPPORTED_ITEMS: ReadonlySet<string> = new Set([
|
|
34
34
|
'agent_message', 'reasoning', 'command_execution', 'file_change', 'mcp_tool_call', 'error',
|
|
35
|
+
// The rest of Codex's `ThreadItemDetails` union
|
|
36
|
+
// (codex-rs/exec/src/exec_events.rs). Without these three, a real agent turn
|
|
37
|
+
// reported them as bare `unknown` placeholders naming a type and a size —
|
|
38
|
+
// honest, but useless: `todo_list` in particular updates continuously
|
|
39
|
+
// through a turn, so it was the most frequent thing a reader could not read.
|
|
40
|
+
'web_search', 'todo_list', 'collab_tool_call',
|
|
35
41
|
]);
|
|
36
42
|
|
|
37
43
|
const ITEM_FRAMES: ReadonlySet<string> = new Set(['item.started', 'item.updated', 'item.completed']);
|
|
38
44
|
|
|
39
45
|
/** Calls are numbered; an `apply_patch` is file activity, not a call. */
|
|
40
|
-
const NUMBERED_ITEMS: ReadonlySet<string> = new Set([
|
|
46
|
+
const NUMBERED_ITEMS: ReadonlySet<string> = new Set([
|
|
47
|
+
'command_execution', 'mcp_tool_call', 'web_search', 'collab_tool_call',
|
|
48
|
+
]);
|
|
49
|
+
/** Todo entries listed per snapshot; a longer plan is counted, not printed. */
|
|
50
|
+
const TODO_MAX_ITEMS = 12;
|
|
41
51
|
|
|
42
52
|
interface Lifecycle {
|
|
43
53
|
snapshots: Array<Record<string, unknown>>;
|
|
@@ -246,6 +256,65 @@ function mcpEntry(item: Record<string, unknown>, complete: boolean, context: Con
|
|
|
246
256
|
});
|
|
247
257
|
}
|
|
248
258
|
|
|
259
|
+
/**
|
|
260
|
+
* `web_search`: a query and what was done with it (`WebSearchItem` — `query`,
|
|
261
|
+
* `action`, optional structured `results`). Rendered as a call rather than
|
|
262
|
+
* prose, so a reader sees the search in sequence beside the commands.
|
|
263
|
+
*/
|
|
264
|
+
function webSearchEntry(item: Record<string, unknown>, complete: boolean, context: Context,
|
|
265
|
+
seq: number): TranscriptEntry | null {
|
|
266
|
+
const query = str(item['query']);
|
|
267
|
+
if (query === null) return null;
|
|
268
|
+
const action = isRecord(item['action']) ? str(item['action']['type']) : str(item['action']);
|
|
269
|
+
const results = item['results'];
|
|
270
|
+
const count = Array.isArray(results) ? results.length : null;
|
|
271
|
+
return tool('web_search', bounded(query, context.clean),
|
|
272
|
+
count === null ? null : JSON.stringify(results).length, false, {
|
|
273
|
+
seq, status: action === null ? null : bounded(action, context.clean, 40),
|
|
274
|
+
exit_code: null,
|
|
275
|
+
output_excerpt: count === null ? null : `${count} result${count === 1 ? '' : 's'}`,
|
|
276
|
+
output_truncated: false, complete, error: null,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* `collab_tool_call`: Codex driving other Codex threads (`CollabToolCallItem` —
|
|
282
|
+
* `tool`, `sender_thread_id`, `receiver_thread_ids`, `agents_states`).
|
|
283
|
+
*
|
|
284
|
+
* The per-agent states are reduced to a count per status rather than listed:
|
|
285
|
+
* `agents_states` is a map keyed by agent id, and ids are not something a
|
|
286
|
+
* transcript reader can act on.
|
|
287
|
+
*/
|
|
288
|
+
function collabEntry(item: Record<string, unknown>, complete: boolean, context: Context,
|
|
289
|
+
seq: number): TranscriptEntry | null {
|
|
290
|
+
const name = str(item['tool']);
|
|
291
|
+
if (name === null) return null;
|
|
292
|
+
const receivers = Array.isArray(item['receiver_thread_ids']) ? item['receiver_thread_ids'].length : 0;
|
|
293
|
+
const states = isRecord(item['agents_states']) ? item['agents_states'] : null;
|
|
294
|
+
const byStatus = new Map<string, number>();
|
|
295
|
+
if (states !== null) {
|
|
296
|
+
for (const state of Object.values(states)) {
|
|
297
|
+
const status = isRecord(state) ? str(state['status']) : null;
|
|
298
|
+
if (status !== null) byStatus.set(status, (byStatus.get(status) ?? 0) + 1);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Redacted like every neighbouring field. A status is provider text, not a
|
|
302
|
+
// closed vocabulary this module controls, and it was the one string here that
|
|
303
|
+
// reached the page without passing the redactor.
|
|
304
|
+
const summary = bounded(
|
|
305
|
+
[...byStatus.entries()].sort().map(([status, n]) => `${n} ${status}`).join(', '),
|
|
306
|
+
context.clean,
|
|
307
|
+
);
|
|
308
|
+
const status = str(item['status']);
|
|
309
|
+
return tool('collab_tool_call',
|
|
310
|
+
bounded(`${name}${receivers > 0 ? ` → ${receivers} thread${receivers === 1 ? '' : 's'}` : ''}`, context.clean),
|
|
311
|
+
null, status === 'failed', {
|
|
312
|
+
seq, status: status === null ? null : bounded(status, context.clean, 40),
|
|
313
|
+
exit_code: null, output_excerpt: summary.length === 0 ? null : summary,
|
|
314
|
+
output_truncated: false, complete, error: null,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
249
318
|
function fileChangeEntry(item: Record<string, unknown>, complete: boolean, context: Context): TranscriptEntry | null {
|
|
250
319
|
const raw = item['changes'];
|
|
251
320
|
if (!Array.isArray(raw)) return null;
|
|
@@ -288,12 +357,35 @@ function itemEntries(item: Record<string, unknown>, complete: boolean, context:
|
|
|
288
357
|
if (message === null) return [placeholder(context, itemType)];
|
|
289
358
|
return [{ kind: 'error', source: 'item', message: bounded(message, context.clean, ERROR_MAX_CHARS) }];
|
|
290
359
|
}
|
|
291
|
-
if (itemType === '
|
|
360
|
+
if (itemType === 'todo_list') {
|
|
361
|
+
// A plan, not a call: it carries no status and earns no sequence number.
|
|
362
|
+
// `items[]` is `{text, completed}` (TodoListItem/TodoItem).
|
|
363
|
+
const items = Array.isArray(item['items']) ? item['items'] : null;
|
|
364
|
+
if (items === null) return [placeholder(context, itemType)];
|
|
365
|
+
const listed = items.slice(0, TODO_MAX_ITEMS).flatMap((entry) => {
|
|
366
|
+
const todo = isRecord(entry) ? entry : undefined;
|
|
367
|
+
const text = str(todo?.['text']);
|
|
368
|
+
return text === null ? [] : [{ text: context.clean(text), done: todo!['completed'] === true }];
|
|
369
|
+
});
|
|
370
|
+
// Counted across the whole plan; only the *display* is capped. Counting the
|
|
371
|
+
// prefix rendered a 13-item plan whose last item was done as `0/13`, which
|
|
372
|
+
// is exactly backwards from "the rest are counted, not printed".
|
|
373
|
+
const done = items.filter(entry => isRecord(entry) && entry['completed'] === true).length;
|
|
374
|
+
return [{
|
|
375
|
+
kind: 'todo', total: items.length, done,
|
|
376
|
+
items: listed, ...(items.length > listed.length ? { omitted: items.length - listed.length } : {}),
|
|
377
|
+
...(complete ? {} : { complete: false }),
|
|
378
|
+
}];
|
|
379
|
+
}
|
|
380
|
+
if (itemType === 'command_execution' || itemType === 'mcp_tool_call'
|
|
381
|
+
|| itemType === 'web_search' || itemType === 'collab_tool_call') {
|
|
292
382
|
// The number is only spent on a call this module could actually read, so a
|
|
293
383
|
// malformed item leaves no gap in the sequence.
|
|
294
384
|
const seq = context.state.seq + 1;
|
|
295
|
-
const entry = itemType === 'command_execution'
|
|
296
|
-
|
|
385
|
+
const entry = itemType === 'command_execution' ? commandEntry(item, complete, context, seq)
|
|
386
|
+
: itemType === 'mcp_tool_call' ? mcpEntry(item, complete, context, seq)
|
|
387
|
+
: itemType === 'web_search' ? webSearchEntry(item, complete, context, seq)
|
|
388
|
+
: collabEntry(item, complete, context, seq);
|
|
297
389
|
if (entry === null) return [placeholder(context, itemType)];
|
|
298
390
|
context.state.seq = seq;
|
|
299
391
|
return [entry];
|
|
@@ -138,6 +138,22 @@ export interface TranscriptResult {
|
|
|
138
138
|
cache_creation: number | null;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Codex's running to-do list (`TodoListItem`). A plan, not a call: it carries
|
|
143
|
+
* no status and takes no sequence number, and it updates repeatedly through a
|
|
144
|
+
* turn — which is why leaving it unparsed was the most visible gap.
|
|
145
|
+
*/
|
|
146
|
+
export interface TranscriptTodo {
|
|
147
|
+
kind: 'todo';
|
|
148
|
+
total: number;
|
|
149
|
+
done: number;
|
|
150
|
+
items: Array<{ text: string; done: boolean }>;
|
|
151
|
+
/** Entries past the per-snapshot cap: counted, never printed. */
|
|
152
|
+
omitted?: number;
|
|
153
|
+
/** False when read off a snapshot that never completed. */
|
|
154
|
+
complete?: boolean;
|
|
155
|
+
}
|
|
156
|
+
|
|
141
157
|
export interface TranscriptUnknown {
|
|
142
158
|
kind: 'unknown';
|
|
143
159
|
type: string;
|
|
@@ -152,7 +168,8 @@ export interface TranscriptUnparsed {
|
|
|
152
168
|
export type TranscriptEntry =
|
|
153
169
|
| TranscriptAttempt | TranscriptAttemptOmitted | TranscriptInit | TranscriptMessage
|
|
154
170
|
| TranscriptThinking | TranscriptTool | TranscriptFileChange | TranscriptThread
|
|
155
|
-
| TranscriptTurn | TranscriptError | TranscriptResult |
|
|
171
|
+
| TranscriptTurn | TranscriptError | TranscriptResult | TranscriptTodo
|
|
172
|
+
| TranscriptUnknown | TranscriptUnparsed;
|
|
156
173
|
|
|
157
174
|
export interface ParsedTranscript {
|
|
158
175
|
entries: TranscriptEntry[];
|
package/src/cloud-transcript.ts
CHANGED
|
@@ -34,7 +34,7 @@ import { redact } from './redact.js';
|
|
|
34
34
|
export type {
|
|
35
35
|
ParsedTranscript, TranscriptEntry, TranscriptAttempt, TranscriptAttemptOmitted, TranscriptError,
|
|
36
36
|
TranscriptFileChange, TranscriptInit, TranscriptMessage, TranscriptResult, TranscriptThinking,
|
|
37
|
-
TranscriptThread, TranscriptTool, TranscriptToolCodex, TranscriptTurn, TranscriptUnknown,
|
|
37
|
+
TranscriptThread, TranscriptTodo, TranscriptTool, TranscriptToolCodex, TranscriptTurn, TranscriptUnknown,
|
|
38
38
|
TranscriptUnparsed,
|
|
39
39
|
} from './cloud-transcript-types.js';
|
|
40
40
|
|
|
@@ -297,6 +297,14 @@ export function renderAgentTranscript(parsed: ParsedTranscript): string[] {
|
|
|
297
297
|
case 'thinking':
|
|
298
298
|
lines.push(` thinking ${thousands(entry.chars)} chars (not shown)`);
|
|
299
299
|
break;
|
|
300
|
+
case 'todo': {
|
|
301
|
+
// The plan the agent is working to, as it stood at this snapshot.
|
|
302
|
+
const progress = `${entry.done}/${entry.total}`;
|
|
303
|
+
lines.push(` plan ${progress}${entry.complete === false ? ' (in progress)' : ''}`);
|
|
304
|
+
for (const todo of entry.items) lines.push(` ${todo.done ? '✓' : '·'} ${safe(todo.text)}`);
|
|
305
|
+
if (entry.omitted !== undefined) lines.push(` …${entry.omitted} more`);
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
300
308
|
case 'tool': {
|
|
301
309
|
const size = entry.is_error ? 'ERROR'
|
|
302
310
|
: entry.result_chars === null ? 'no result' : `${thousands(entry.result_chars)} chars`;
|
package/src/helper-reference.ts
CHANGED
|
@@ -27,7 +27,8 @@ export function helperNamespacesUsed(body: string, root: string): ReadonlySet<st
|
|
|
27
27
|
const program = parseFlowBody(body);
|
|
28
28
|
if (program === null) return textFallback(body, root);
|
|
29
29
|
const used = new Set<string>();
|
|
30
|
-
|
|
30
|
+
const scope = { rootFunctionFound: false };
|
|
31
|
+
walkReferences(program, root, false, scope, (node) => {
|
|
31
32
|
if (node.type !== 'MemberExpression') return;
|
|
32
33
|
const object = node.object as AstNode | undefined;
|
|
33
34
|
if (object?.type !== 'Identifier' || object.name !== root) return;
|
|
@@ -111,20 +112,144 @@ type AstNode = {
|
|
|
111
112
|
[key: string]: unknown;
|
|
112
113
|
};
|
|
113
114
|
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Depth-first over references that still resolve to the outer flow context.
|
|
117
|
+
*
|
|
118
|
+
* A nested callback/method parameter or lexical destructuring can reuse the
|
|
119
|
+
* context's spelling while binding an unrelated value. Counting its members
|
|
120
|
+
* would refuse the flow for a mount it never touches. Acorn gives us binding
|
|
121
|
+
* shapes, so scopes are tracked as syntax instead of guessed from text.
|
|
122
|
+
*/
|
|
123
|
+
function walkReferences(
|
|
124
|
+
node: AstNode,
|
|
125
|
+
root: string,
|
|
126
|
+
shadowed: boolean,
|
|
127
|
+
state: { rootFunctionFound: boolean },
|
|
128
|
+
visit: (node: AstNode) => void,
|
|
129
|
+
): void {
|
|
130
|
+
if (isFunction(node)) {
|
|
131
|
+
const parameters = Array.isArray(node.params) ? node.params.filter(isNode) : [];
|
|
132
|
+
const bindsParameter = parameters.some(parameter => patternBinds(parameter, root));
|
|
133
|
+
let bodyHidden = shadowed;
|
|
134
|
+
const isRootFunction = bindsParameter && !state.rootFunctionFound;
|
|
135
|
+
if (isRootFunction) state.rootFunctionFound = true;
|
|
136
|
+
else if (bindsParameter || (isNode(node.id) && patternBinds(node.id, root)) || functionVarBinds(node, root)) {
|
|
137
|
+
bodyHidden = true;
|
|
138
|
+
}
|
|
139
|
+
// Parameter initializers run before the function body and are outside a
|
|
140
|
+
// body-level `var` scope. Keep them visible unless the parameter list
|
|
141
|
+
// itself binds the root name (in which case all parameter references are
|
|
142
|
+
// local to that parameter environment).
|
|
143
|
+
const parameterHidden = shadowed || (bindsParameter && !isRootFunction);
|
|
144
|
+
for (const parameter of parameters) walkReferences(parameter, root, parameterHidden, state, visit);
|
|
145
|
+
const body = isNode(node.body) ? node.body : undefined;
|
|
146
|
+
if (body !== undefined) walkReferences(body, root, bodyHidden, state, visit);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let hidden = shadowed;
|
|
150
|
+
if (node.type === 'BlockStatement' && blockBinds(node, root)) {
|
|
151
|
+
hidden = true;
|
|
152
|
+
} else if (node.type === 'CatchClause' && isNode(node.param) && patternBinds(node.param, root)) {
|
|
153
|
+
hidden = true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!hidden) visit(node);
|
|
117
157
|
for (const key of Object.keys(node)) {
|
|
118
158
|
if (key === 'type' || key === 'start' || key === 'end' || key === 'loc') continue;
|
|
119
159
|
const child = node[key];
|
|
120
160
|
if (Array.isArray(child)) {
|
|
121
|
-
for (const entry of child) if (isNode(entry))
|
|
161
|
+
for (const entry of child) if (isNode(entry)) walkReferences(entry, root, hidden, state, visit);
|
|
122
162
|
} else if (isNode(child)) {
|
|
123
|
-
|
|
163
|
+
walkReferences(child, root, hidden, state, visit);
|
|
124
164
|
}
|
|
125
165
|
}
|
|
126
166
|
}
|
|
127
167
|
|
|
168
|
+
function isFunction(node: AstNode): boolean {
|
|
169
|
+
return node.type === 'ArrowFunctionExpression'
|
|
170
|
+
|| node.type === 'FunctionExpression'
|
|
171
|
+
|| node.type === 'FunctionDeclaration';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Lexical declarations bind across their complete block, including TDZ. */
|
|
175
|
+
function blockBinds(node: AstNode, root: string): boolean {
|
|
176
|
+
const statements = Array.isArray(node.body) ? node.body.filter(isNode) : [];
|
|
177
|
+
return statements.some((statement) => {
|
|
178
|
+
if (statement.type === 'VariableDeclaration' && statement.kind !== 'var') {
|
|
179
|
+
const declarations = Array.isArray(statement.declarations)
|
|
180
|
+
? statement.declarations.filter(isNode) : [];
|
|
181
|
+
return declarations.some(declaration => isNode(declaration.id) && patternBinds(declaration.id, root));
|
|
182
|
+
}
|
|
183
|
+
return (statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration')
|
|
184
|
+
&& isNode(statement.id) && patternBinds(statement.id, root);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Identifier occurrences that introduce bindings, including nested patterns. */
|
|
189
|
+
function patternBinds(pattern: AstNode, root: string): boolean {
|
|
190
|
+
switch (pattern.type) {
|
|
191
|
+
case 'Identifier':
|
|
192
|
+
return pattern.name === root;
|
|
193
|
+
case 'AssignmentPattern':
|
|
194
|
+
return isNode(pattern.left) && patternBinds(pattern.left, root);
|
|
195
|
+
case 'RestElement':
|
|
196
|
+
return isNode(pattern.argument) && patternBinds(pattern.argument, root);
|
|
197
|
+
case 'ArrayPattern': {
|
|
198
|
+
const elements = Array.isArray(pattern.elements) ? pattern.elements : [];
|
|
199
|
+
return elements.some(element => isNode(element) && patternBinds(element, root));
|
|
200
|
+
}
|
|
201
|
+
case 'ObjectPattern': {
|
|
202
|
+
const properties = Array.isArray(pattern.properties) ? pattern.properties : [];
|
|
203
|
+
return properties.some(property => {
|
|
204
|
+
if (!isNode(property)) return false;
|
|
205
|
+
if (property.type === 'RestElement') {
|
|
206
|
+
return isNode(property.argument) && patternBinds(property.argument, root);
|
|
207
|
+
}
|
|
208
|
+
// Object keys are labels, not bindings. Only the value side introduces
|
|
209
|
+
// the local (including a default-value AssignmentPattern's left side).
|
|
210
|
+
return property.type === 'Property'
|
|
211
|
+
&& isNode(property.value)
|
|
212
|
+
&& patternBinds(property.value, root);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
default:
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** `var` is function-scoped, so a nested function's declaration hides the
|
|
221
|
+
* outer flow context for its entire body, including code before the
|
|
222
|
+
* declaration. Nested functions have their own var scopes and are skipped. */
|
|
223
|
+
function functionVarBinds(node: AstNode, root: string): boolean {
|
|
224
|
+
const body = isNode(node.body) ? node.body : undefined;
|
|
225
|
+
if (body === undefined) return false;
|
|
226
|
+
let found = false;
|
|
227
|
+
const scan = (current: AstNode): void => {
|
|
228
|
+
if (found) return;
|
|
229
|
+
if (current !== body && isFunction(current)) return;
|
|
230
|
+
if (current.type === 'VariableDeclaration' && current.kind === 'var') {
|
|
231
|
+
const declarations = Array.isArray(current.declarations)
|
|
232
|
+
? current.declarations.filter(isNode) : [];
|
|
233
|
+
if (declarations.some(declaration => isNode(declaration.id) && patternBinds(declaration.id, root))) {
|
|
234
|
+
found = true;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
for (const key of Object.keys(current)) {
|
|
239
|
+
if (key === 'type' || key === 'start' || key === 'end' || key === 'loc') continue;
|
|
240
|
+
const child = current[key];
|
|
241
|
+
if (Array.isArray(child)) {
|
|
242
|
+
for (const entry of child) if (isNode(entry)) scan(entry);
|
|
243
|
+
} else if (isNode(child)) {
|
|
244
|
+
scan(child);
|
|
245
|
+
}
|
|
246
|
+
if (found) return;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
scan(body);
|
|
250
|
+
return found;
|
|
251
|
+
}
|
|
252
|
+
|
|
128
253
|
function isNode(value: unknown): value is AstNode {
|
|
129
254
|
return typeof value === 'object' && value !== null && typeof (value as { type?: unknown }).type === 'string';
|
|
130
255
|
}
|
|
@@ -109,7 +109,7 @@ const ADDRESS_SPACE_BYTES = 16 * 1024 * 1024 * 1024;
|
|
|
109
109
|
const DATA_BYTES = 3 * 1024 * 1024 * 1024;
|
|
110
110
|
const SURFACE_RUNTIME_SHA256 = OBJECT_FREEZE({
|
|
111
111
|
'flow.js': '4aaeacc55de3074f4d121ce7253c3be50a93757e6540ba8159889a9450d1c05c',
|
|
112
|
-
'helpers/providers.js': '
|
|
112
|
+
'helpers/providers.js': '4eb06d0d85ca0a3434bb2dbba7407e3d95eef2dfbedaeb0e2de0c0c0ea812457',
|
|
113
113
|
'provider-trigger.js': 'e2664c65397f93fb486eb6f1e756c7cec3f88b3851d79c23567cad986f80f1ff',
|
|
114
114
|
'schedule.js': '8fe72f176a75ec0b5f26e12db7a597575c259a2e2cbb59690f9dc20a5e63940b',
|
|
115
115
|
'triggers.js': '4a3515b571a318f6c7a5661f9310bc9af43e3faf20ea39903a9b363c51258e4c',
|