@myagentroam/node 0.10.10 → 0.10.11
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/capabilities.js +1 -0
- package/dist/connector.js +5 -1
- package/dist/runner/codex/conversation-parser.js +89 -0
- package/dist/runner/codex/managed-run-controller.js +63 -20
- package/dist/service/model-usage-service.js +5 -0
- package/dist/service/runner-interaction-service.js +11 -5
- package/dist/service/session-lifecycle-service.js +5 -0
- package/package.json +3 -3
package/dist/capabilities.js
CHANGED
|
@@ -12,6 +12,7 @@ const runtimeCredentialCapability = () => ({
|
|
|
12
12
|
serviceTiers: ['auto', 'default', 'flex', 'priority', 'fast', 'ultrafast'],
|
|
13
13
|
namespacedTools: true,
|
|
14
14
|
responsesOptionalParameters: true,
|
|
15
|
+
responsesHttpContinuation: true,
|
|
15
16
|
httpVersions: ['1.1', '2']
|
|
16
17
|
});
|
|
17
18
|
const workspaceDirectTransferCapability = () => ({
|
package/dist/connector.js
CHANGED
|
@@ -698,6 +698,7 @@ export class NodeConnector {
|
|
|
698
698
|
runtime: this.runtime,
|
|
699
699
|
state: this.runState,
|
|
700
700
|
codex: () => this.codexClient,
|
|
701
|
+
settleCodexUsage: (runId, turnId) => this.codexRunController.settleUsage(runId, turnId),
|
|
701
702
|
claude: () => this.claudeClient,
|
|
702
703
|
runners: this.runners,
|
|
703
704
|
emitRun: (runId, eventType, payload, status) => this.runEventBridge.emitRun(runId, eventType, payload, status),
|
|
@@ -975,7 +976,10 @@ export class NodeConnector {
|
|
|
975
976
|
}
|
|
976
977
|
},
|
|
977
978
|
migrateQueue: (previousId, nextId) => this.continuousRunCoordinator.migrateSession(previousId, nextId),
|
|
978
|
-
migrateRunnerState: (previousId, nextId) =>
|
|
979
|
+
migrateRunnerState: (previousId, nextId) => {
|
|
980
|
+
this.modelUsage.promoteSession(previousId, nextId);
|
|
981
|
+
this.runEventBridge.migrateRunnerSessionIdentity(previousId, nextId);
|
|
982
|
+
},
|
|
979
983
|
emitSession: (session) => this.emitSessionWorkbenchEvent({ session }),
|
|
980
984
|
emitRun: (run) => this.emitWorkbenchEvent('run', { run }),
|
|
981
985
|
emitQueue: (sessionId) => this.continuousRunCoordinator.publish(sessionId),
|
|
@@ -1,11 +1,96 @@
|
|
|
1
|
+
import { open, stat } from 'node:fs/promises';
|
|
2
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
3
|
+
import { nodeLog } from '../../operational.js';
|
|
1
4
|
import { RunnerUsageTracker, usageToken } from '../../service/runner-usage-tracker.js';
|
|
2
5
|
export class CodexUsageNormalizer {
|
|
3
6
|
requestedModel;
|
|
4
7
|
tracker;
|
|
8
|
+
rollout;
|
|
9
|
+
pendingRead = Promise.resolve();
|
|
10
|
+
decoder = new StringDecoder('utf8');
|
|
11
|
+
partialLine = '';
|
|
12
|
+
oversizedLine = false;
|
|
5
13
|
constructor(identity, publish, requestedModel) {
|
|
6
14
|
this.requestedModel = requestedModel;
|
|
7
15
|
this.tracker = new RunnerUsageTracker(identity, publish);
|
|
8
16
|
}
|
|
17
|
+
async attachRollout(path, threadId) {
|
|
18
|
+
await this.pendingRead;
|
|
19
|
+
this.decoder = new StringDecoder('utf8');
|
|
20
|
+
this.partialLine = '';
|
|
21
|
+
this.oversizedLine = false;
|
|
22
|
+
try {
|
|
23
|
+
// Anchor before dispatch. Inherited history and earlier requests never become new usage.
|
|
24
|
+
this.rollout = { path, threadId, offset: (await stat(path)).size };
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
this.reportReadFailure();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
get hasRollout() {
|
|
31
|
+
return this.rollout !== undefined;
|
|
32
|
+
}
|
|
33
|
+
flushRollout(turnId, compact) {
|
|
34
|
+
this.pendingRead = this.pendingRead
|
|
35
|
+
.then(async () => {
|
|
36
|
+
const rollout = this.rollout;
|
|
37
|
+
if (!rollout)
|
|
38
|
+
return;
|
|
39
|
+
const file = await open(rollout.path, 'r');
|
|
40
|
+
try {
|
|
41
|
+
const end = (await file.stat()).size;
|
|
42
|
+
if (end < rollout.offset || end - rollout.offset > 16 * 1024 * 1024)
|
|
43
|
+
throw new Error('USAGE_NATIVE_READ_LIMIT');
|
|
44
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
45
|
+
while (rollout.offset < end) {
|
|
46
|
+
const { bytesRead } = await file.read(buffer, 0, Math.min(buffer.length, end - rollout.offset), rollout.offset);
|
|
47
|
+
if (!bytesRead)
|
|
48
|
+
break;
|
|
49
|
+
rollout.offset += bytesRead;
|
|
50
|
+
this.consumeRollout(this.decoder.write(buffer.subarray(0, bytesRead)), turnId, compact);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await file.close();
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
.catch(() => this.reportReadFailure());
|
|
58
|
+
return this.pendingRead;
|
|
59
|
+
}
|
|
60
|
+
reportReadFailure() {
|
|
61
|
+
this.rollout = undefined;
|
|
62
|
+
nodeLog('model.usage.unavailable', { runner: 'codex', code: 'USAGE_NATIVE_READ_FAILED' });
|
|
63
|
+
}
|
|
64
|
+
consumeRollout(text, turnId, compact) {
|
|
65
|
+
for (const part of text.split(/(?<=\n)/u)) {
|
|
66
|
+
if (!this.oversizedLine)
|
|
67
|
+
this.partialLine += part;
|
|
68
|
+
if (this.partialLine.length > 128 * 1024) {
|
|
69
|
+
this.partialLine = '';
|
|
70
|
+
this.oversizedLine = true;
|
|
71
|
+
}
|
|
72
|
+
if (!part.endsWith('\n'))
|
|
73
|
+
continue;
|
|
74
|
+
if (!this.oversizedLine) {
|
|
75
|
+
try {
|
|
76
|
+
const value = JSON.parse(this.partialLine);
|
|
77
|
+
if (isPlainRecord(value) && value.type === 'token_usage_record') {
|
|
78
|
+
const receipt = isPlainRecord(value.payload) ? value.payload : undefined;
|
|
79
|
+
if (receipt?.thread_id === this.rollout?.threadId && receipt?.turn_id === turnId)
|
|
80
|
+
this.observe('rawResponse/completed', {
|
|
81
|
+
responseId: receipt.response_id,
|
|
82
|
+
usage: receipt.usage
|
|
83
|
+
}, compact);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Non-receipt and malformed native lines cannot turn into estimated usage.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
this.partialLine = '';
|
|
91
|
+
this.oversizedLine = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
9
94
|
observe(method, params, compact) {
|
|
10
95
|
// TokenCount.last can replay context/compaction snapshots without a request identity.
|
|
11
96
|
// Only raw response completions are independently additive request facts.
|
|
@@ -15,10 +100,13 @@ export class CodexUsageNormalizer {
|
|
|
15
100
|
const inputTokens = usageToken(usage.inputTokens ?? usage.input_tokens);
|
|
16
101
|
const outputTokens = usageToken(usage.outputTokens ?? usage.output_tokens);
|
|
17
102
|
const cacheReadTokens = usageToken(usage.cachedInputTokens ??
|
|
103
|
+
usage.cached_input_tokens ??
|
|
18
104
|
(isPlainRecord(usage.input_tokens_details)
|
|
19
105
|
? usage.input_tokens_details.cached_tokens
|
|
20
106
|
: undefined));
|
|
107
|
+
const cacheWriteTokens = usageToken(usage.cacheWriteInputTokens ?? usage.cache_write_input_tokens);
|
|
21
108
|
const reasoningTokens = usageToken(usage.reasoningOutputTokens ??
|
|
109
|
+
usage.reasoning_output_tokens ??
|
|
22
110
|
(isPlainRecord(usage.output_tokens_details)
|
|
23
111
|
? usage.output_tokens_details.reasoning_tokens
|
|
24
112
|
: undefined));
|
|
@@ -31,6 +119,7 @@ export class CodexUsageNormalizer {
|
|
|
31
119
|
...(inputTokens === undefined ? {} : { inputTokens }),
|
|
32
120
|
...(outputTokens === undefined ? {} : { outputTokens }),
|
|
33
121
|
...(cacheReadTokens === undefined ? {} : { cacheReadTokens }),
|
|
122
|
+
...(cacheWriteTokens === undefined ? {} : { cacheWriteTokens }),
|
|
34
123
|
...(reasoningTokens === undefined ? {} : { reasoningTokens })
|
|
35
124
|
};
|
|
36
125
|
this.tracker.snapshot(params.responseId, values, true, 'partial');
|
|
@@ -29,6 +29,7 @@ export class CodexManagedRunController {
|
|
|
29
29
|
runner;
|
|
30
30
|
host;
|
|
31
31
|
usage = new Map();
|
|
32
|
+
finishing = new Set();
|
|
32
33
|
constructor(runner, host) {
|
|
33
34
|
this.runner = runner;
|
|
34
35
|
this.host = host;
|
|
@@ -103,7 +104,8 @@ export class CodexManagedRunController {
|
|
|
103
104
|
});
|
|
104
105
|
this.host.emit(runId, 'run.started', { command: 'compact' }, 'STARTING');
|
|
105
106
|
try {
|
|
106
|
-
await this.runner.resumeThread(threadId, session.cwd, undefined, runId);
|
|
107
|
+
const resumed = await this.runner.resumeThread(threadId, session.cwd, undefined, runId);
|
|
108
|
+
await this.attachUsageRollout(runId, resumed);
|
|
107
109
|
await this.runner.request('thread/compact/start', { threadId }, runId);
|
|
108
110
|
return {
|
|
109
111
|
command: 'compact',
|
|
@@ -140,12 +142,9 @@ export class CodexManagedRunController {
|
|
|
140
142
|
if (run === undefined)
|
|
141
143
|
return;
|
|
142
144
|
const [runId, activeRun] = run;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this.usage.set(runId, usage);
|
|
147
|
-
}
|
|
148
|
-
usage.observe(notification.method, params, activeRun.generationCompacting === true);
|
|
145
|
+
const usage = this.usageFor(runId);
|
|
146
|
+
const compactUsage = activeRun.refreshTitle === false || activeRun.generationCompacting === true;
|
|
147
|
+
usage.observe(notification.method, params, compactUsage);
|
|
149
148
|
const receiptAt = activeRun.generationTiming?.timestamp();
|
|
150
149
|
if (notification.method === 'turn/started' && turnId !== undefined) {
|
|
151
150
|
const active = this.runner.execution.runs.get(runId);
|
|
@@ -161,6 +160,8 @@ export class CodexManagedRunController {
|
|
|
161
160
|
return;
|
|
162
161
|
}
|
|
163
162
|
if (notification.method === 'thread/tokenUsage/updated') {
|
|
163
|
+
if (turnId !== undefined)
|
|
164
|
+
void usage.flushRollout(turnId, compactUsage);
|
|
164
165
|
this.completeStandardUsage(runId, params, activeRun, receiptAt);
|
|
165
166
|
return;
|
|
166
167
|
}
|
|
@@ -325,6 +326,8 @@ export class CodexManagedRunController {
|
|
|
325
326
|
return;
|
|
326
327
|
}
|
|
327
328
|
if (notification.method === 'turn/completed') {
|
|
329
|
+
if (this.finishing.has(runId))
|
|
330
|
+
return;
|
|
328
331
|
this.endPendingGenerations(activeRun, receiptAt);
|
|
329
332
|
const status = turn?.status;
|
|
330
333
|
const terminal = status === 'completed' ? 'SUCCEEDED' : status === 'interrupted' ? 'INTERRUPTED' : 'FAILED';
|
|
@@ -333,21 +336,60 @@ export class CodexManagedRunController {
|
|
|
333
336
|
const failure = terminal === 'FAILED'
|
|
334
337
|
? runnerErrorProjection(turn?.error ?? turn, 'CODEX_RUN_FAILED')
|
|
335
338
|
: { status: status ?? 'unknown' };
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
?
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
339
|
+
const finish = () => {
|
|
340
|
+
if (!this.runner.execution.runs.has(runId)) {
|
|
341
|
+
this.finishing.delete(runId);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
this.host.emit(runId, terminal === 'FAILED' ? 'run.failed' : 'run.completed', failure, terminal);
|
|
345
|
+
const active = this.runner.execution.runs.get(runId);
|
|
346
|
+
const titleRefresh = active === undefined ||
|
|
347
|
+
active.refreshTitle === false ||
|
|
348
|
+
this.host.session(active.sessionId)?.titleSource !== 'AUTO'
|
|
349
|
+
? undefined
|
|
350
|
+
: this.host.syncTitle(active.sessionId, active.threadId, active.cwd);
|
|
351
|
+
this.usage.delete(runId);
|
|
352
|
+
this.runner.execution.runs.delete(runId);
|
|
353
|
+
if (titleRefresh === undefined)
|
|
354
|
+
this.runner.releaseEnvironment(runId);
|
|
355
|
+
else
|
|
356
|
+
void titleRefresh.finally(() => this.runner.releaseEnvironment(runId));
|
|
357
|
+
this.host.removeActive(runId);
|
|
358
|
+
this.finishing.delete(runId);
|
|
359
|
+
};
|
|
360
|
+
if (usage.hasRollout && turnId !== undefined) {
|
|
361
|
+
this.finishing.add(runId);
|
|
362
|
+
void this.settleUsage(runId, turnId).then(finish);
|
|
363
|
+
}
|
|
347
364
|
else
|
|
348
|
-
|
|
349
|
-
|
|
365
|
+
finish();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async settleUsage(runId, turnId) {
|
|
369
|
+
const usage = this.usage.get(runId);
|
|
370
|
+
const active = this.runner.execution.runs.get(runId);
|
|
371
|
+
if (usage) {
|
|
372
|
+
await usage.flushRollout(turnId, active?.refreshTitle === false || active?.generationCompacting === true);
|
|
373
|
+
if (this.usage.get(runId) === usage)
|
|
374
|
+
this.usage.delete(runId);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
usageFor(runId) {
|
|
378
|
+
let usage = this.usage.get(runId);
|
|
379
|
+
if (!usage) {
|
|
380
|
+
const active = this.runner.execution.runs.get(runId);
|
|
381
|
+
usage = new CodexUsageNormalizer(runId, (value) => this.host.emit(runId, 'run.usage', {
|
|
382
|
+
...value,
|
|
383
|
+
sessionId: this.runner.execution.runs.get(runId)?.threadId
|
|
384
|
+
}), active ? (this.host.session(active.sessionId)?.model ?? undefined) : undefined);
|
|
385
|
+
this.usage.set(runId, usage);
|
|
350
386
|
}
|
|
387
|
+
return usage;
|
|
388
|
+
}
|
|
389
|
+
async attachUsageRollout(runId, result) {
|
|
390
|
+
const thread = isPlainRecord(result) && isPlainRecord(result.thread) ? result.thread : undefined;
|
|
391
|
+
if (typeof thread?.path === 'string' && typeof thread.id === 'string')
|
|
392
|
+
await this.usageFor(runId).attachRollout(thread.path, thread.id);
|
|
351
393
|
}
|
|
352
394
|
observeResponse(runId, itemId, incremental = false) {
|
|
353
395
|
const latencyMs = this.runner.execution.runs
|
|
@@ -629,6 +671,7 @@ export class CodexManagedRunController {
|
|
|
629
671
|
generationResponseIds: new Map(),
|
|
630
672
|
completedGenerationResponseIds: new Set()
|
|
631
673
|
});
|
|
674
|
+
await this.attachUsageRollout(runId, threadResult);
|
|
632
675
|
turnResult = await this.runner.startTurn(threadId, input, cwd, turnConfiguration, materializedAttachments, runId);
|
|
633
676
|
break;
|
|
634
677
|
}
|
|
@@ -36,6 +36,11 @@ export class NodeModelUsageService {
|
|
|
36
36
|
release(runId) {
|
|
37
37
|
this.bindings.delete(runId);
|
|
38
38
|
}
|
|
39
|
+
promoteSession(previousId, nextId) {
|
|
40
|
+
for (const [runId, binding] of this.bindings)
|
|
41
|
+
if (binding.sessionId === previousId)
|
|
42
|
+
this.bindings.set(runId, { ...binding, sessionId: nextId });
|
|
43
|
+
}
|
|
39
44
|
clear() {
|
|
40
45
|
this.bindings.clear();
|
|
41
46
|
}
|
|
@@ -147,16 +147,22 @@ export class RunnerInteractionService {
|
|
|
147
147
|
}
|
|
148
148
|
async cancelCodexTurn(runId, threadId, turnId, cwd, terminalStatus) {
|
|
149
149
|
const codex = this.options.codex();
|
|
150
|
+
let failed = false;
|
|
150
151
|
try {
|
|
151
152
|
await codex.interruptTurn(threadId, turnId, runId);
|
|
152
|
-
const run = this.options.runtime.getRun(runId);
|
|
153
|
-
if (run !== undefined && !isTerminalRunStatus(run.status))
|
|
154
|
-
this.options.emitRun(runId, 'run.completed', { status: terminalStatus }, terminalStatus);
|
|
155
153
|
}
|
|
156
154
|
catch {
|
|
155
|
+
failed = true;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
await this.options.settleCodexUsage(runId, turnId);
|
|
157
159
|
const run = this.options.runtime.getRun(runId);
|
|
158
|
-
if (run !== undefined && !isTerminalRunStatus(run.status))
|
|
159
|
-
|
|
160
|
+
if (run !== undefined && !isTerminalRunStatus(run.status)) {
|
|
161
|
+
if (failed)
|
|
162
|
+
this.options.emitRun(runId, 'run.failed', { code: 'CODEX_INTERRUPT_FAILED' }, 'FAILED');
|
|
163
|
+
else
|
|
164
|
+
this.options.emitRun(runId, 'run.completed', { status: terminalStatus }, terminalStatus);
|
|
165
|
+
}
|
|
160
166
|
}
|
|
161
167
|
finally {
|
|
162
168
|
codex.execution.runs.delete(runId);
|
|
@@ -198,6 +198,10 @@ export class SessionLifecycleService {
|
|
|
198
198
|
sessionId: bootstrapId,
|
|
199
199
|
commandId: value.commandId,
|
|
200
200
|
input: value.input,
|
|
201
|
+
...(input.__requestUserId === undefined
|
|
202
|
+
? {}
|
|
203
|
+
: { __requestUserId: input.__requestUserId }),
|
|
204
|
+
...(input.__usageGrant === undefined ? {} : { __usageGrant: input.__usageGrant }),
|
|
201
205
|
...(input.secretEnvironment === undefined
|
|
202
206
|
? {}
|
|
203
207
|
: { secretEnvironment: input.secretEnvironment })
|
|
@@ -211,6 +215,7 @@ export class SessionLifecycleService {
|
|
|
211
215
|
clientMessageId: input.clientMessageId,
|
|
212
216
|
content: input.content,
|
|
213
217
|
...(input.__requestUserId === undefined ? {} : { __requestUserId: input.__requestUserId }),
|
|
218
|
+
...(input.__usageGrant === undefined ? {} : { __usageGrant: input.__usageGrant }),
|
|
214
219
|
...(input.secretEnvironment === undefined
|
|
215
220
|
? {}
|
|
216
221
|
: { secretEnvironment: input.secretEnvironment }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.11",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"playwright": "1.62.1",
|
|
38
38
|
"ws": "^8.21.3",
|
|
39
39
|
"zod": "4.4.3",
|
|
40
|
-
"@myagentroam/agent": "0.10.
|
|
40
|
+
"@myagentroam/agent": "0.10.11",
|
|
41
41
|
"@myagentroam/code-navigation": "0.10.1",
|
|
42
|
-
"@myagentroam/protocol": "0.10.
|
|
42
|
+
"@myagentroam/protocol": "0.10.11",
|
|
43
43
|
"@myagentroam/marctl": "0.10.2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|