@myagentroam/node 0.9.173 → 0.9.175
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/codex-app-server.js +1 -0
- package/dist/runner/claude/managed-run-controller.js +89 -15
- package/dist/runner/codex/managed-run-controller.js +149 -3
- package/dist/runner/mar-agent/compact-extensions.js +2 -1
- package/dist/runner/mar-agent/compact-prompt.js +56 -0
- package/dist/runner/mar-agent/managed-run-controller.js +1 -1
- package/dist/runner/mar-agent/runtime-model-factory.js +0 -1
- package/dist/runner/opencode/managed-run-controller.js +128 -14
- package/dist/service/generation-timing-tracker.js +81 -0
- package/dist/service/hosted-conversation-node-service.js +0 -1
- package/package.json +4 -4
package/dist/codex-app-server.js
CHANGED
|
@@ -8,6 +8,7 @@ import { parseSecretEnvironment } from '../../util/secret-environment.js';
|
|
|
8
8
|
import { safeErrorCode } from '../../util/safe-error.js';
|
|
9
9
|
import { runnerErrorProjection } from '../../util/runner-error.js';
|
|
10
10
|
import { ResponseLatencyTracker } from '../../service/response-latency-tracker.js';
|
|
11
|
+
import { GenerationTimingTracker } from '../../service/generation-timing-tracker.js';
|
|
11
12
|
export class ClaudeManagedRunController {
|
|
12
13
|
runner;
|
|
13
14
|
host;
|
|
@@ -164,12 +165,27 @@ export class ClaudeManagedRunController {
|
|
|
164
165
|
try {
|
|
165
166
|
const gate = new ClaudeChannelGate(sessionId);
|
|
166
167
|
const responseLatency = new ResponseLatencyTracker();
|
|
168
|
+
const generationTiming = new GenerationTimingTracker();
|
|
169
|
+
const finalOutputTokens = new Map();
|
|
170
|
+
const pendingOutputTokens = new Map();
|
|
171
|
+
const completedMessages = new Set();
|
|
167
172
|
let responseMessageId;
|
|
168
173
|
const observeResponse = (id, incremental = false) => {
|
|
169
174
|
const latencyMs = responseLatency.response(id, incremental);
|
|
170
175
|
if (latencyMs !== undefined)
|
|
171
176
|
this.host.emit(runId, 'run.performance', { latencyMs });
|
|
172
177
|
};
|
|
178
|
+
const settleGeneration = (id, observedAt) => {
|
|
179
|
+
if (id === undefined)
|
|
180
|
+
return;
|
|
181
|
+
generationTiming.end(id, observedAt);
|
|
182
|
+
const outputTokens = finalOutputTokens.get(id);
|
|
183
|
+
if (outputTokens === undefined)
|
|
184
|
+
return;
|
|
185
|
+
const result = generationTiming.settle(id, outputTokens, observedAt);
|
|
186
|
+
if (result !== undefined)
|
|
187
|
+
this.host.emit(runId, 'run.performance', { tokensPerSecond: result.tokensPerSecond });
|
|
188
|
+
};
|
|
173
189
|
handle = this.runner.startChannel({
|
|
174
190
|
prompt: payload.input,
|
|
175
191
|
cwd,
|
|
@@ -200,9 +216,14 @@ export class ClaudeManagedRunController {
|
|
|
200
216
|
}))
|
|
201
217
|
}),
|
|
202
218
|
onPermission: (toolName, toolInput, options) => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
219
|
+
if (options.agentID === undefined) {
|
|
220
|
+
const receiptAt = generationTiming.timestamp();
|
|
221
|
+
if (responseMessageId !== undefined)
|
|
222
|
+
generationTiming.pause(responseMessageId, receiptAt);
|
|
223
|
+
const latencyMs = responseLatency.toolStarted(options.toolUseID);
|
|
224
|
+
if (latencyMs !== undefined)
|
|
225
|
+
this.host.emit(runId, 'run.performance', { latencyMs });
|
|
226
|
+
}
|
|
206
227
|
return toolName === 'AskUserQuestion'
|
|
207
228
|
? (toolKinds.set(options.toolUseID, 'user_input_request'),
|
|
208
229
|
this.host.requestUserInput(runId, toolInput, options.toolUseID))
|
|
@@ -212,6 +233,7 @@ export class ClaudeManagedRunController {
|
|
|
212
233
|
onMessage: (message) => {
|
|
213
234
|
if (terminal)
|
|
214
235
|
return;
|
|
236
|
+
const receiptAt = generationTiming.timestamp();
|
|
215
237
|
const record = message;
|
|
216
238
|
const nativeSessionId = typeof record.session_id === 'string' ? record.session_id : undefined;
|
|
217
239
|
const current = this.runner.execution.runs.get(runId);
|
|
@@ -236,7 +258,8 @@ export class ClaudeManagedRunController {
|
|
|
236
258
|
});
|
|
237
259
|
}
|
|
238
260
|
}
|
|
239
|
-
|
|
261
|
+
const mainTask = record.parent_tool_use_id === undefined || record.parent_tool_use_id === null;
|
|
262
|
+
if (record.type === 'stream_event' && mainTask) {
|
|
240
263
|
// Observe semantic timing without forwarding raw thinking or input fragments.
|
|
241
264
|
const event = record.event;
|
|
242
265
|
const block = event?.content_block;
|
|
@@ -245,14 +268,49 @@ export class ClaudeManagedRunController {
|
|
|
245
268
|
const message = event.message;
|
|
246
269
|
responseMessageId = typeof message?.id === 'string' ? message.id : undefined;
|
|
247
270
|
}
|
|
271
|
+
if (event?.type === 'message_delta') {
|
|
272
|
+
const usage = event.usage;
|
|
273
|
+
if (responseMessageId !== undefined) {
|
|
274
|
+
if (typeof usage?.output_tokens === 'number')
|
|
275
|
+
pendingOutputTokens.set(responseMessageId, usage.output_tokens);
|
|
276
|
+
if (typeof delta?.stop_reason === 'string' && delta.stop_reason.length > 0) {
|
|
277
|
+
const tokens = pendingOutputTokens.get(responseMessageId);
|
|
278
|
+
if (tokens !== undefined)
|
|
279
|
+
finalOutputTokens.set(responseMessageId, tokens);
|
|
280
|
+
completedMessages.add(responseMessageId);
|
|
281
|
+
settleGeneration(responseMessageId, receiptAt);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (event?.type === 'message_stop') {
|
|
286
|
+
if (responseMessageId !== undefined) {
|
|
287
|
+
completedMessages.add(responseMessageId);
|
|
288
|
+
if (!finalOutputTokens.has(responseMessageId)) {
|
|
289
|
+
const tokens = pendingOutputTokens.get(responseMessageId);
|
|
290
|
+
if (tokens !== undefined)
|
|
291
|
+
finalOutputTokens.set(responseMessageId, tokens);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
settleGeneration(responseMessageId, receiptAt);
|
|
295
|
+
}
|
|
248
296
|
if (event?.type === 'content_block_start' &&
|
|
249
|
-
block?.type === 'tool_use'
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
297
|
+
(block?.type === 'tool_use' ||
|
|
298
|
+
block?.type === 'thinking' ||
|
|
299
|
+
block?.type === 'redacted_thinking' ||
|
|
300
|
+
block?.type === 'encrypted_thinking' ||
|
|
301
|
+
block?.type === 'text')) {
|
|
302
|
+
if (responseMessageId !== undefined)
|
|
303
|
+
generationTiming.start(responseMessageId, receiptAt);
|
|
304
|
+
if (block?.type !== 'text' ||
|
|
305
|
+
(typeof block.text === 'string' && block.text.length > 0)) {
|
|
306
|
+
const latencyMs = block?.type === 'tool_use' && typeof block.id === 'string'
|
|
307
|
+
? responseLatency.toolStarted(block.id)
|
|
308
|
+
: responseLatency.response(responseMessageId);
|
|
309
|
+
if (latencyMs !== undefined)
|
|
310
|
+
this.host.emit(runId, 'run.performance', { latencyMs });
|
|
311
|
+
if (block?.type === 'tool_use')
|
|
312
|
+
responseLatency.response(responseMessageId);
|
|
313
|
+
}
|
|
256
314
|
}
|
|
257
315
|
if ((event?.type === 'content_block_start' && block?.type === 'thinking') ||
|
|
258
316
|
(event?.type === 'content_block_delta' &&
|
|
@@ -264,12 +322,27 @@ export class ClaudeManagedRunController {
|
|
|
264
322
|
delta.thinking.length > 0) ||
|
|
265
323
|
(delta?.type === 'input_json_delta' &&
|
|
266
324
|
typeof delta.partial_json === 'string' &&
|
|
267
|
-
delta.partial_json.length > 0))))
|
|
325
|
+
delta.partial_json.length > 0)))) {
|
|
326
|
+
if (responseMessageId !== undefined)
|
|
327
|
+
generationTiming.start(responseMessageId, receiptAt);
|
|
268
328
|
observeResponse(responseMessageId, event?.type === 'content_block_delta');
|
|
329
|
+
}
|
|
269
330
|
}
|
|
270
331
|
if (record.type === 'assistant') {
|
|
332
|
+
const snapshotMessage = record.message;
|
|
333
|
+
const snapshotId = typeof snapshotMessage?.id === 'string' ? snapshotMessage.id : undefined;
|
|
334
|
+
const snapshotUsage = snapshotMessage?.usage;
|
|
335
|
+
if (mainTask &&
|
|
336
|
+
snapshotId !== undefined &&
|
|
337
|
+
typeof snapshotUsage?.output_tokens === 'number' &&
|
|
338
|
+
(completedMessages.has(snapshotId) ||
|
|
339
|
+
(typeof snapshotMessage?.stop_reason === 'string' &&
|
|
340
|
+
snapshotMessage.stop_reason.length > 0))) {
|
|
341
|
+
finalOutputTokens.set(snapshotId, snapshotUsage.output_tokens);
|
|
342
|
+
settleGeneration(snapshotId, receiptAt);
|
|
343
|
+
}
|
|
271
344
|
const text = extractClaudeText(record);
|
|
272
|
-
if (text.length > 0 || extractClaudeThinkingSummaries(record).length > 0)
|
|
345
|
+
if (mainTask && (text.length > 0 || extractClaudeThinkingSummaries(record).length > 0))
|
|
273
346
|
observeResponse(typeof record.message?.id === 'string'
|
|
274
347
|
? record.message.id
|
|
275
348
|
: undefined);
|
|
@@ -292,7 +365,7 @@ export class ClaudeManagedRunController {
|
|
|
292
365
|
});
|
|
293
366
|
}
|
|
294
367
|
for (const tool of extractClaudeToolUses(record)) {
|
|
295
|
-
const latencyMs = responseLatency.toolStarted(tool.id);
|
|
368
|
+
const latencyMs = mainTask ? responseLatency.toolStarted(tool.id) : undefined;
|
|
296
369
|
if (latencyMs !== undefined)
|
|
297
370
|
this.host.emit(runId, 'run.performance', { latencyMs });
|
|
298
371
|
if (tool.name === 'AskUserQuestion') {
|
|
@@ -358,7 +431,8 @@ export class ClaudeManagedRunController {
|
|
|
358
431
|
}
|
|
359
432
|
if (record.type === 'user') {
|
|
360
433
|
for (const result of extractClaudeToolResults(record)) {
|
|
361
|
-
|
|
434
|
+
if (mainTask)
|
|
435
|
+
responseLatency.toolCompleted(result.toolUseId);
|
|
362
436
|
const kind = toolKinds.get(result.toolUseId) ?? 'tool_call';
|
|
363
437
|
if (kind === 'user_input_request')
|
|
364
438
|
continue;
|
|
@@ -4,6 +4,7 @@ import { parseSecretEnvironment } from '../../util/secret-environment.js';
|
|
|
4
4
|
import { safeErrorCode } from '../../util/safe-error.js';
|
|
5
5
|
import { runnerErrorProjection } from '../../util/runner-error.js';
|
|
6
6
|
import { ResponseLatencyTracker } from '../../service/response-latency-tracker.js';
|
|
7
|
+
import { GenerationTimingTracker } from '../../service/generation-timing-tracker.js';
|
|
7
8
|
import { boundedConversationItemId, codexImageAttachments, codexLiveConversationItem, codexUserInputQuestions, compactRunnerText, isPlainRecord } from './conversation-parser.js';
|
|
8
9
|
export function resolveCodexNotificationRun(runs, input) {
|
|
9
10
|
const entries = [...runs.entries()];
|
|
@@ -137,6 +138,7 @@ export class CodexManagedRunController {
|
|
|
137
138
|
if (run === undefined)
|
|
138
139
|
return;
|
|
139
140
|
const [runId, activeRun] = run;
|
|
141
|
+
const receiptAt = activeRun.generationTiming?.timestamp();
|
|
140
142
|
if (notification.method === 'turn/started' && turnId !== undefined) {
|
|
141
143
|
const active = this.runner.execution.runs.get(runId);
|
|
142
144
|
if (active !== undefined)
|
|
@@ -144,6 +146,16 @@ export class CodexManagedRunController {
|
|
|
144
146
|
this.host.emit(runId, 'run.running', { threadId, turnId }, 'RUNNING');
|
|
145
147
|
return;
|
|
146
148
|
}
|
|
149
|
+
if (notification.method === 'rawResponseItem/completed')
|
|
150
|
+
return;
|
|
151
|
+
if (notification.method === 'rawResponse/completed') {
|
|
152
|
+
this.completeRawResponse(runId, params, activeRun, receiptAt);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (notification.method === 'thread/tokenUsage/updated') {
|
|
156
|
+
this.completeStandardUsage(runId, params, activeRun, receiptAt);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
147
159
|
if (notification.method === 'thread/goal/updated' &&
|
|
148
160
|
activeRun.goalMcp === true &&
|
|
149
161
|
turnId !== undefined &&
|
|
@@ -168,6 +180,7 @@ export class CodexManagedRunController {
|
|
|
168
180
|
if (notification.requestId !== undefined &&
|
|
169
181
|
(notification.method === 'item/commandExecution/requestApproval' ||
|
|
170
182
|
notification.method === 'item/fileChange/requestApproval')) {
|
|
183
|
+
this.pauseGeneration(runId, receiptAt);
|
|
171
184
|
this.requestApproval(runId, {
|
|
172
185
|
...notification,
|
|
173
186
|
requestId: notification.requestId
|
|
@@ -176,6 +189,7 @@ export class CodexManagedRunController {
|
|
|
176
189
|
}
|
|
177
190
|
if (notification.requestId !== undefined &&
|
|
178
191
|
notification.method === 'item/tool/requestUserInput') {
|
|
192
|
+
this.pauseGeneration(runId, receiptAt);
|
|
179
193
|
this.requestUserInput(runId, notification.requestId, params);
|
|
180
194
|
return;
|
|
181
195
|
}
|
|
@@ -200,15 +214,19 @@ export class CodexManagedRunController {
|
|
|
200
214
|
text: params.delta,
|
|
201
215
|
...(itemId === undefined ? {} : { itemId })
|
|
202
216
|
});
|
|
217
|
+
if (params.delta.length > 0)
|
|
218
|
+
this.beginGeneration(runId, receiptAt);
|
|
203
219
|
if (params.delta.length > 0)
|
|
204
220
|
this.observeResponse(runId, itemId, true);
|
|
205
221
|
return;
|
|
206
222
|
}
|
|
207
223
|
if (notification.method === 'item/reasoning/textDelta' && typeof params.delta === 'string') {
|
|
208
|
-
if (params.delta.length > 0)
|
|
224
|
+
if (params.delta.length > 0) {
|
|
225
|
+
this.beginGeneration(runId, receiptAt);
|
|
209
226
|
this.observeResponse(runId, typeof params.itemId === 'string'
|
|
210
227
|
? boundedConversationItemId('codex', params.itemId)
|
|
211
228
|
: undefined, true);
|
|
229
|
+
}
|
|
212
230
|
return;
|
|
213
231
|
}
|
|
214
232
|
if (notification.method === 'item/reasoning/summaryTextDelta' &&
|
|
@@ -225,8 +243,10 @@ export class CodexManagedRunController {
|
|
|
225
243
|
merge: true,
|
|
226
244
|
append: 'sections'
|
|
227
245
|
});
|
|
228
|
-
if (params.delta.length > 0)
|
|
246
|
+
if (params.delta.length > 0) {
|
|
247
|
+
this.beginGeneration(runId, receiptAt);
|
|
229
248
|
this.observeResponse(runId, boundedConversationItemId('codex', params.itemId), true);
|
|
249
|
+
}
|
|
230
250
|
return;
|
|
231
251
|
}
|
|
232
252
|
if (notification.method === 'item/commandExecution/outputDelta' &&
|
|
@@ -260,9 +280,25 @@ export class CodexManagedRunController {
|
|
|
260
280
|
const item = codexLiveConversationItem(params.item, notification.method === 'item/completed', attachments);
|
|
261
281
|
if (item !== undefined) {
|
|
262
282
|
this.host.emitItem(runId, item);
|
|
283
|
+
if (item.kind === 'context_compaction') {
|
|
284
|
+
activeRun.generationCompacting = notification.method === 'item/started';
|
|
285
|
+
if (activeRun.generationCompacting) {
|
|
286
|
+
for (const key of activeRun.generationKeys ?? [])
|
|
287
|
+
activeRun.generationTiming?.discard(key);
|
|
288
|
+
activeRun.generationKeys = [];
|
|
289
|
+
activeRun.currentGenerationKey = undefined;
|
|
290
|
+
}
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
263
293
|
const tracker = activeRun.responseLatency;
|
|
264
294
|
const tool = ['tool_call', 'command_execution', 'file_change'].includes(item.kind);
|
|
265
295
|
if (tool) {
|
|
296
|
+
if (notification.method === 'item/started') {
|
|
297
|
+
if (codexToolGeneratesArguments(params.item))
|
|
298
|
+
this.beginGeneration(runId, receiptAt);
|
|
299
|
+
else
|
|
300
|
+
this.pauseGeneration(runId, receiptAt);
|
|
301
|
+
}
|
|
266
302
|
if (notification.method === 'item/completed')
|
|
267
303
|
tracker?.toolCompleted(item.itemId);
|
|
268
304
|
else {
|
|
@@ -273,12 +309,15 @@ export class CodexManagedRunController {
|
|
|
273
309
|
}
|
|
274
310
|
else if (['assistant_message', 'reasoning_summary', 'plan'].includes(item.kind)) {
|
|
275
311
|
// A started reasoning item is itself a semantic response, even before its summary.
|
|
312
|
+
if (notification.method === 'item/started')
|
|
313
|
+
this.beginGeneration(runId, receiptAt);
|
|
276
314
|
this.observeResponse(runId, item.itemId);
|
|
277
315
|
}
|
|
278
316
|
}
|
|
279
317
|
return;
|
|
280
318
|
}
|
|
281
319
|
if (notification.method === 'turn/completed') {
|
|
320
|
+
this.endPendingGenerations(activeRun, receiptAt);
|
|
282
321
|
const status = turn?.status;
|
|
283
322
|
const terminal = status === 'completed' ? 'SUCCEEDED' : status === 'interrupted' ? 'INTERRUPTED' : 'FAILED';
|
|
284
323
|
if (terminal === 'FAILED')
|
|
@@ -308,6 +347,87 @@ export class CodexManagedRunController {
|
|
|
308
347
|
if (latencyMs !== undefined)
|
|
309
348
|
this.host.emit(runId, 'run.performance', { latencyMs });
|
|
310
349
|
}
|
|
350
|
+
beginGeneration(runId, observedAt) {
|
|
351
|
+
const active = this.runner.execution.runs.get(runId);
|
|
352
|
+
if (active?.generationTiming === undefined || active.generationCompacting === true)
|
|
353
|
+
return;
|
|
354
|
+
let key = active.currentGenerationKey;
|
|
355
|
+
if (key === undefined) {
|
|
356
|
+
const sequence = (active.generationRequestSequence ?? 0) + 1;
|
|
357
|
+
key = `${active.turnId ?? active.threadId}:generation:${sequence}`;
|
|
358
|
+
active.generationKeys ??= [];
|
|
359
|
+
active.generationKeys.push(key);
|
|
360
|
+
active.generationRequestSequence = sequence;
|
|
361
|
+
active.currentGenerationKey = key;
|
|
362
|
+
}
|
|
363
|
+
active.generationTiming.start(key, observedAt);
|
|
364
|
+
}
|
|
365
|
+
pauseGeneration(runId, observedAt) {
|
|
366
|
+
const active = this.runner.execution.runs.get(runId);
|
|
367
|
+
const key = active?.currentGenerationKey;
|
|
368
|
+
if (active?.generationTiming === undefined || key === undefined)
|
|
369
|
+
return;
|
|
370
|
+
active.generationTiming.pause(key, observedAt);
|
|
371
|
+
}
|
|
372
|
+
endPendingGenerations(active, observedAt) {
|
|
373
|
+
if (active.generationTiming === undefined)
|
|
374
|
+
return;
|
|
375
|
+
for (const key of active.generationKeys ?? [])
|
|
376
|
+
active.generationTiming.end(key, observedAt);
|
|
377
|
+
}
|
|
378
|
+
completeRawResponse(runId, params, active, observedAt) {
|
|
379
|
+
const responseId = typeof params.responseId === 'string' ? params.responseId : undefined;
|
|
380
|
+
if (responseId === undefined || active.completedGenerationResponseIds?.has(responseId))
|
|
381
|
+
return;
|
|
382
|
+
active.completedGenerationResponseIds ??= new Set();
|
|
383
|
+
active.completedGenerationResponseIds.add(responseId);
|
|
384
|
+
if (active.generationCompacting === true)
|
|
385
|
+
return;
|
|
386
|
+
const key = active.generationResponseIds?.get(responseId) ??
|
|
387
|
+
active.currentGenerationKey ??
|
|
388
|
+
active.generationKeys?.find((candidate) => active.generationTiming?.has(candidate));
|
|
389
|
+
if (key === undefined || active.generationTiming === undefined)
|
|
390
|
+
return;
|
|
391
|
+
active.generationResponseIds ??= new Map();
|
|
392
|
+
active.generationResponseIds.set(responseId, key);
|
|
393
|
+
active.generationTiming.end(key, observedAt);
|
|
394
|
+
if (active.currentGenerationKey === key)
|
|
395
|
+
active.currentGenerationKey = undefined;
|
|
396
|
+
const outputTokens = codexOutputTokens(params.usage);
|
|
397
|
+
if (outputTokens === undefined)
|
|
398
|
+
return;
|
|
399
|
+
this.emitGenerationTps(runId, active, key, outputTokens, observedAt);
|
|
400
|
+
}
|
|
401
|
+
completeStandardUsage(runId, params, active, observedAt) {
|
|
402
|
+
if (active.generationTiming === undefined)
|
|
403
|
+
return;
|
|
404
|
+
const usage = isPlainRecord(params.tokenUsage) ? params.tokenUsage : undefined;
|
|
405
|
+
const outputTokens = codexOutputTokens(usage?.last);
|
|
406
|
+
if (outputTokens === undefined)
|
|
407
|
+
return;
|
|
408
|
+
// TokenCount also repeats the previous request's snapshot (for example after compaction).
|
|
409
|
+
// Include total counters to distinguish equal last counts from distinct model requests.
|
|
410
|
+
const snapshot = JSON.stringify([
|
|
411
|
+
codexUsageCounters(usage?.last),
|
|
412
|
+
codexUsageCounters(usage?.total)
|
|
413
|
+
]);
|
|
414
|
+
if (active.generationUsageSnapshot === snapshot)
|
|
415
|
+
return;
|
|
416
|
+
active.generationUsageSnapshot = snapshot;
|
|
417
|
+
if (active.generationCompacting === true)
|
|
418
|
+
return;
|
|
419
|
+
const key = active.generationKeys?.find((candidate) => active.generationTiming?.has(candidate));
|
|
420
|
+
if (key !== undefined)
|
|
421
|
+
this.emitGenerationTps(runId, active, key, outputTokens, observedAt);
|
|
422
|
+
}
|
|
423
|
+
emitGenerationTps(runId, active, key, outputTokens, observedAt) {
|
|
424
|
+
const result = active.generationTiming?.settle(key, outputTokens, observedAt);
|
|
425
|
+
if (result !== undefined)
|
|
426
|
+
this.host.emit(runId, 'run.performance', { tokensPerSecond: result.tokensPerSecond });
|
|
427
|
+
active.generationKeys = active.generationKeys?.filter((candidate) => candidate !== key);
|
|
428
|
+
if (active.currentGenerationKey === key)
|
|
429
|
+
active.currentGenerationKey = undefined;
|
|
430
|
+
}
|
|
311
431
|
requestApproval(runId, notification) {
|
|
312
432
|
const approvalId = `codex:${runId}:${notification.requestId}`;
|
|
313
433
|
const expiresAt = Date.now() + 10 * 60_000;
|
|
@@ -494,7 +614,11 @@ export class CodexManagedRunController {
|
|
|
494
614
|
cwd,
|
|
495
615
|
goalMcp: hasGoalMcp(payload.mcpServers),
|
|
496
616
|
scheduleMcp: hasScheduleMcp(payload.mcpServers),
|
|
497
|
-
responseLatency: new ResponseLatencyTracker()
|
|
617
|
+
responseLatency: new ResponseLatencyTracker(),
|
|
618
|
+
generationTiming: new GenerationTimingTracker(),
|
|
619
|
+
generationKeys: [],
|
|
620
|
+
generationResponseIds: new Map(),
|
|
621
|
+
completedGenerationResponseIds: new Set()
|
|
498
622
|
});
|
|
499
623
|
turnResult = await this.runner.startTurn(threadId, input, cwd, turnConfiguration, materializedAttachments, runId);
|
|
500
624
|
break;
|
|
@@ -560,3 +684,25 @@ function hasGoalMcp(value) {
|
|
|
560
684
|
function hasScheduleMcp(value) {
|
|
561
685
|
return isPlainRecord(value) && isPlainRecord(value.mar_schedule);
|
|
562
686
|
}
|
|
687
|
+
function codexOutputTokens(value) {
|
|
688
|
+
if (!isPlainRecord(value) ||
|
|
689
|
+
typeof value.outputTokens !== 'number' ||
|
|
690
|
+
!Number.isSafeInteger(value.outputTokens) ||
|
|
691
|
+
value.outputTokens < 0)
|
|
692
|
+
return undefined;
|
|
693
|
+
return value.outputTokens;
|
|
694
|
+
}
|
|
695
|
+
function codexToolGeneratesArguments(value) {
|
|
696
|
+
return ['functionCall', 'function_call', 'customToolCall', 'custom_tool_call'].includes(typeof value.type === 'string' ? value.type : '');
|
|
697
|
+
}
|
|
698
|
+
function codexUsageCounters(value) {
|
|
699
|
+
if (!isPlainRecord(value))
|
|
700
|
+
return undefined;
|
|
701
|
+
return [
|
|
702
|
+
value.inputTokens,
|
|
703
|
+
value.cachedInputTokens,
|
|
704
|
+
value.outputTokens,
|
|
705
|
+
value.reasoningOutputTokens,
|
|
706
|
+
value.totalTokens
|
|
707
|
+
];
|
|
708
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildCompactEvidence, countModelTokens, generateModelText, MarAgentError } from '@myagentroam/agent';
|
|
2
|
+
import { highDensityCompactionPrompt } from './compact-prompt.js';
|
|
2
3
|
import { marAgentRuntimeCompactConfigurationSchema } from '@myagentroam/protocol';
|
|
3
4
|
/** Resolve optional dedicated credentials in the same factory batch without widening the SDK catalog. */
|
|
4
5
|
export function compactRuntimeConfigurations(models, raw) {
|
|
@@ -59,7 +60,7 @@ export function marAgentCompactExtensionSource(state) {
|
|
|
59
60
|
export async function generateMarAgentCompact(context, selected, signal) {
|
|
60
61
|
signal.throwIfAborted();
|
|
61
62
|
const startedAt = Date.now();
|
|
62
|
-
const instruction =
|
|
63
|
+
const instruction = highDensityCompactionPrompt();
|
|
63
64
|
const overhead = countModelTokens(system) + countModelTokens(instruction) + 128;
|
|
64
65
|
const full = buildCompactEvidence(context.messages, {
|
|
65
66
|
maxTokens: Number.MAX_SAFE_INTEGER,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function highDensityCompactionPrompt() {
|
|
2
|
+
return `Compile the visible conversation into Textual Continuation IR that lets the next agent continue correctly after the transcript is discarded. Transfer state; do not recap, address the user, call tools, advance the task, or invent facts.
|
|
3
|
+
|
|
4
|
+
Optimize for continuation fidelity first and density second. Prefer sufficient continuation state over brevity; never omit information required to resume correctly.
|
|
5
|
+
|
|
6
|
+
## Output contract
|
|
7
|
+
Use these English section names when applicable; omit empty sections:
|
|
8
|
+
1. Goal contract
|
|
9
|
+
2. Current state
|
|
10
|
+
3. Settled matters
|
|
11
|
+
4. Necessary evidence
|
|
12
|
+
5. Open loops
|
|
13
|
+
|
|
14
|
+
IR grammar and vocabulary are English: headings, keys, enums, state atoms, and control terms. Natural-language values may follow the task's dominant language. Preserve exact user-designated text and established domain terms; do not translate identifiers or invent localized IR terminology.
|
|
15
|
+
|
|
16
|
+
Prefer records over prose:
|
|
17
|
+
<object> | <key>=<value> | <key>=<value>
|
|
18
|
+
<event/condition> -> <object>.<key>=<value> | <effect>
|
|
19
|
+
|
|
20
|
+
Use | for parallel facts, = for current state, and -> only for causality, invalidation, or transition. Keep enums lowercase and semantic: required, forbidden, pending; never YES/NO. Omit empty or irrelevant unknown fields. Keep pending, not run, unverified, and failed distinct. Use a sentence only when records cannot express a multi-object cause, conflict, authorization, or safety boundary unambiguously.
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
src/discount.js | cause=verified | edit=pending | tests=not run
|
|
24
|
+
patch(src/discount.js) -> prior_read=stale | next=re-evidence
|
|
25
|
+
authorization | action=<exact> | scope=<exact> | lifetime=<exact> | status=active
|
|
26
|
+
|
|
27
|
+
## Compiler
|
|
28
|
+
1. Resolve intent. The latest user correction overrides older intent. Extract the primary objective, current deliverable, completion criteria, non-goals, and boundaries.
|
|
29
|
+
2. Reduce time. Convert before -> event -> after into the latest evidenced state. Source mutation or base/branch change makes only affected evidence stale. History replacement invalidates Responses continuation, not factual evidence. Without an invalidating event, a verified claim remains current.
|
|
30
|
+
3. Build objects. Emit one current record per file, symbol, feature, migration, test, worktree axis, selected Skill, Todo/plan item, external decision, or external side effect. Deduplicate equivalent facts.
|
|
31
|
+
4. Separate status axes when needed:
|
|
32
|
+
- completion=pending|in_progress|completed|blocked
|
|
33
|
+
- epistemic=observed|verified|inferred|proposed|disproved
|
|
34
|
+
- freshness=current|stale|superseded
|
|
35
|
+
- scope=turn|worktree|base|branch|external
|
|
36
|
+
5. Bind evidence to the claim it proves. Prefer a verified conclusion over raw snapshots or intermediate hypotheses.
|
|
37
|
+
6. Select under budget. Protect, in order: objective, progress, boundaries and authorization, non-repeatable side effects, open loops, then necessary evidence. Remove provenance and recoverable detail only after those remain sufficient.
|
|
38
|
+
|
|
39
|
+
## Retention rules
|
|
40
|
+
- Preserve active caller/user instructions outside filesystem-loaded AGENTS.md, exact authorization boundaries, confirmed decisions and exclusions, completed edits and side effects, verification actually run, reproducible failures, failed approaches that constrain the next step, unresolved risks, and the smallest established next action.
|
|
41
|
+
- Authorization is valid only for its exact action, scope, and lifetime in the active request. Completed or superseded scopes cannot revive it. Object state, completion, tool availability, and convenience never imply permission.
|
|
42
|
+
- Preserve decisive values, or an exact artifact path/command that deterministically recovers them, when a result controls later work. Counts or schemas alone are insufficient.
|
|
43
|
+
- Preserve paths, symbols, APIs, tools, models, providers, JSON keys, protocol fields, commands, arguments, error codes, commit hashes, call IDs, sequences, versions, numbers, and user-designated text verbatim.
|
|
44
|
+
- Preserve selected Skill names with only continuation-critical constraints. Preserve Todo/plan revision, item/status, and current minimum step.
|
|
45
|
+
- Keep observed HEAD, validated base, target commit, and current worktree as separate axes. Evidence updates only what it directly proves; cross-axis, cross-base, and cross-branch inference remains unverified.
|
|
46
|
+
- If evidence conflicts, use state=conflict | candidates=<short values> | next=re-evidence. Never choose a side without evidence.
|
|
47
|
+
|
|
48
|
+
## Exclusions
|
|
49
|
+
Remove greetings, progress chatter, chronology that does not affect current state, duplicate facts, superseded hypotheses without remaining constraints, long logs, source bodies, verbose payloads, cheap-to-recover content, local-only integrity hashes, hidden reasoning, secrets, credentials, and unrelated content.
|
|
50
|
+
|
|
51
|
+
Remove user/project/directory AGENTS.md text and constraints derived only from those files; the next Execution reloads them. Remove retained process/Subagent inventory, IDs, status, commands, cwd, and reuse instructions; Runtime reloads them.
|
|
52
|
+
|
|
53
|
+
Before emitting, verify that objective, current progress, authorization/boundaries, completed versus unverified work, decisive evidence, open loops, and next action are explicit and non-contradictory.
|
|
54
|
+
|
|
55
|
+
Output only compact Markdown IR.`;
|
|
56
|
+
}
|
|
@@ -832,7 +832,7 @@ export class MarAgentManagedRunController {
|
|
|
832
832
|
this.host.refreshContext?.(runId);
|
|
833
833
|
if (event.type === 'model.performance')
|
|
834
834
|
this.host.emit(runId, 'run.performance', {
|
|
835
|
-
latencyMs: event.latencyMs,
|
|
835
|
+
...(event.latencyMs === undefined ? {} : { latencyMs: event.latencyMs }),
|
|
836
836
|
...(event.tokensPerSecond === undefined ? {} : { tokensPerSecond: event.tokensPerSecond })
|
|
837
837
|
});
|
|
838
838
|
else if (event.type === 'message.delta')
|
|
@@ -64,7 +64,6 @@ export class MarAgentRuntimeModelFactory {
|
|
|
64
64
|
defaultReasoningEffort: resolveMarAgentDefaultReasoningEffort(configuration.defaultReasoningEffort, configuration.reasoningEfforts),
|
|
65
65
|
titleGeneration: configuration.titleGeneration,
|
|
66
66
|
codeMode: configuration.codeMode,
|
|
67
|
-
highDensityCompaction: configuration.highDensityCompaction,
|
|
68
67
|
...(configuration.serviceTier === undefined
|
|
69
68
|
? {}
|
|
70
69
|
: { serviceTier: configuration.serviceTier }),
|
|
@@ -5,6 +5,7 @@ import { boundedConversationItemId, compactRunnerText } from '../codex/conversat
|
|
|
5
5
|
import { openCodePatchToolPayload, openCodeQuestions } from './conversation-parser.js';
|
|
6
6
|
import { normalizeOpenCodeTool } from './tool-normalizer.js';
|
|
7
7
|
import { ResponseLatencyTracker } from '../../service/response-latency-tracker.js';
|
|
8
|
+
import { GenerationTimingTracker } from '../../service/generation-timing-tracker.js';
|
|
8
9
|
export class OpenCodeManagedRunController {
|
|
9
10
|
runner;
|
|
10
11
|
host;
|
|
@@ -272,6 +273,7 @@ export class OpenCodeManagedRunController {
|
|
|
272
273
|
execution.responsePartKinds = new Map();
|
|
273
274
|
execution.responseStartedAt = Date.now();
|
|
274
275
|
execution.responseMessages = new Set();
|
|
276
|
+
execution.generationTiming = new GenerationTimingTracker();
|
|
275
277
|
}
|
|
276
278
|
await client.prompt({
|
|
277
279
|
directory: input.cwd,
|
|
@@ -295,6 +297,8 @@ export class OpenCodeManagedRunController {
|
|
|
295
297
|
const event = record(value);
|
|
296
298
|
const properties = record(event?.properties);
|
|
297
299
|
const nested = record(properties?.part) ?? record(properties?.info);
|
|
300
|
+
const execution = this.runner.execution.get(runId);
|
|
301
|
+
const receiptAt = execution?.generationTiming?.timestamp();
|
|
298
302
|
const sessionID = typeof properties?.sessionID === 'string'
|
|
299
303
|
? properties.sessionID
|
|
300
304
|
: typeof nested?.sessionID === 'string'
|
|
@@ -325,8 +329,10 @@ export class OpenCodeManagedRunController {
|
|
|
325
329
|
if (info.role === 'assistant' &&
|
|
326
330
|
execution?.responseStartedAt !== undefined &&
|
|
327
331
|
typeof created === 'number' &&
|
|
328
|
-
created >= execution.responseStartedAt)
|
|
332
|
+
created >= execution.responseStartedAt) {
|
|
329
333
|
execution.responseMessages?.add(info.id);
|
|
334
|
+
execution.currentResponseMessageId = info.id;
|
|
335
|
+
}
|
|
330
336
|
}
|
|
331
337
|
if (info?.role === 'assistant' &&
|
|
332
338
|
typeof info.id === 'string' &&
|
|
@@ -358,8 +364,12 @@ export class OpenCodeManagedRunController {
|
|
|
358
364
|
typeof properties.delta === 'string' &&
|
|
359
365
|
properties.delta.length > 0 &&
|
|
360
366
|
typeof properties.messageID === 'string' &&
|
|
361
|
-
this.currentResponseMessage(runId, properties.messageID))
|
|
367
|
+
this.currentResponseMessage(runId, properties.messageID)) {
|
|
368
|
+
const key = this.generationKey(runId, properties.messageID, properties.partID);
|
|
369
|
+
if (key !== undefined)
|
|
370
|
+
execution?.generationTiming?.start(key, receiptAt);
|
|
362
371
|
this.observeResponse(runId, boundedConversationItemId('opencode', properties.partID), true);
|
|
372
|
+
}
|
|
363
373
|
return;
|
|
364
374
|
}
|
|
365
375
|
if (event?.type === 'message.part.updated') {
|
|
@@ -386,36 +396,91 @@ export class OpenCodeManagedRunController {
|
|
|
386
396
|
return;
|
|
387
397
|
if (typeof part.id === 'string' && typeof part.type === 'string')
|
|
388
398
|
this.runner.execution.get(runId)?.responsePartKinds?.set(part.id, part.type);
|
|
399
|
+
const tracker = this.currentResponseMessage(runId, part.messageID)
|
|
400
|
+
? execution?.generationTiming
|
|
401
|
+
: undefined;
|
|
402
|
+
if (part.type === 'step-start') {
|
|
403
|
+
if (tracker !== undefined && typeof part.id === 'string')
|
|
404
|
+
this.generationKey(runId, part.messageID, part.id, receiptAt, true);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (part.type === 'step-finish') {
|
|
408
|
+
const key = typeof part.id === 'string'
|
|
409
|
+
? (execution?.generationPartKeys?.get(part.id) ??
|
|
410
|
+
execution?.generationRequestKeys?.get(part.messageID))
|
|
411
|
+
: undefined;
|
|
412
|
+
if (tracker !== undefined && key !== undefined && typeof part.id === 'string') {
|
|
413
|
+
execution?.generationPartKeys?.set(part.id, key);
|
|
414
|
+
tracker.end(key, receiptAt);
|
|
415
|
+
if (execution?.generationRequestKeys?.get(part.messageID) === key)
|
|
416
|
+
execution.generationRequestKeys.delete(part.messageID);
|
|
417
|
+
const outputTokens = openCodeOutputTokens(part.tokens);
|
|
418
|
+
if (outputTokens !== undefined) {
|
|
419
|
+
const result = tracker.settle(key, outputTokens, receiptAt);
|
|
420
|
+
if (result !== undefined)
|
|
421
|
+
this.host.emit(runId, 'run.performance', { tokensPerSecond: result.tokensPerSecond });
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
389
426
|
const item = openCodePart(part, this.runner.execution.get(runId)?.agent === 'plan');
|
|
390
427
|
if (item !== undefined)
|
|
391
428
|
this.host.emitItem(runId, item);
|
|
392
429
|
if (typeof part.id === 'string') {
|
|
393
430
|
const itemId = boundedConversationItemId('opencode', part.id);
|
|
394
|
-
const
|
|
395
|
-
?
|
|
431
|
+
const responseLatency = this.currentResponseMessage(runId, part.messageID)
|
|
432
|
+
? execution?.responseLatency
|
|
433
|
+
: undefined;
|
|
434
|
+
const generationTiming = this.currentResponseMessage(runId, part.messageID)
|
|
435
|
+
? execution?.generationTiming
|
|
396
436
|
: undefined;
|
|
437
|
+
const generationKey = generationTiming === undefined
|
|
438
|
+
? undefined
|
|
439
|
+
: this.generationKey(runId, part.messageID, part.id);
|
|
397
440
|
if (part.type === 'tool') {
|
|
398
441
|
const state = record(part.state);
|
|
399
|
-
if (state?.status === '
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const latencyMs =
|
|
442
|
+
if (state?.status === 'pending') {
|
|
443
|
+
if (generationKey !== undefined)
|
|
444
|
+
generationTiming?.start(generationKey, receiptAt);
|
|
445
|
+
const latencyMs = responseLatency?.toolStarted(itemId);
|
|
446
|
+
if (latencyMs !== undefined)
|
|
447
|
+
this.host.emit(runId, 'run.performance', { latencyMs });
|
|
448
|
+
}
|
|
449
|
+
else if (state?.status === 'running') {
|
|
450
|
+
if (generationKey !== undefined)
|
|
451
|
+
generationTiming?.pause(generationKey, receiptAt);
|
|
452
|
+
const latencyMs = responseLatency?.toolStarted(itemId);
|
|
403
453
|
if (latencyMs !== undefined)
|
|
404
454
|
this.host.emit(runId, 'run.performance', { latencyMs });
|
|
405
455
|
}
|
|
456
|
+
else if (state?.status === 'completed' || state?.status === 'error') {
|
|
457
|
+
if (generationKey !== undefined)
|
|
458
|
+
generationTiming?.pause(generationKey, receiptAt);
|
|
459
|
+
responseLatency?.toolCompleted(itemId);
|
|
460
|
+
}
|
|
406
461
|
}
|
|
407
|
-
else if (
|
|
462
|
+
else if (generationTiming !== undefined &&
|
|
463
|
+
generationKey !== undefined &&
|
|
408
464
|
(part.type === 'text' || part.type === 'reasoning') &&
|
|
409
|
-
typeof part.text === 'string'
|
|
410
|
-
part.
|
|
411
|
-
|
|
465
|
+
typeof part.text === 'string') {
|
|
466
|
+
if (isOpenCodeGenerationStart(part.time))
|
|
467
|
+
generationTiming.start(generationKey, receiptAt);
|
|
468
|
+
if (isOpenCodeGenerationEnd(part.time))
|
|
469
|
+
generationTiming.pause(generationKey, receiptAt);
|
|
470
|
+
if (part.text.length > 0)
|
|
471
|
+
this.observeResponse(runId, itemId);
|
|
472
|
+
}
|
|
412
473
|
}
|
|
413
474
|
return;
|
|
414
475
|
}
|
|
415
|
-
if (event?.type === 'permission.asked')
|
|
476
|
+
if (event?.type === 'permission.asked') {
|
|
477
|
+
this.pauseCurrentGeneration(runId, receiptAt);
|
|
416
478
|
this.requestPermission(runId, properties);
|
|
417
|
-
|
|
479
|
+
}
|
|
480
|
+
if (event?.type === 'question.asked') {
|
|
481
|
+
this.pauseCurrentGeneration(runId, receiptAt);
|
|
418
482
|
this.requestQuestion(runId, properties);
|
|
483
|
+
}
|
|
419
484
|
}
|
|
420
485
|
currentResponseMessage(runId, messageId) {
|
|
421
486
|
const parent = this.messageParents.get(runId)?.get(messageId);
|
|
@@ -430,6 +495,37 @@ export class OpenCodeManagedRunController {
|
|
|
430
495
|
if (latencyMs !== undefined)
|
|
431
496
|
this.host.emit(runId, 'run.performance', { latencyMs });
|
|
432
497
|
}
|
|
498
|
+
pauseCurrentGeneration(runId, observedAt) {
|
|
499
|
+
const execution = this.runner.execution.get(runId);
|
|
500
|
+
const key = execution?.currentResponseMessageId === undefined
|
|
501
|
+
? undefined
|
|
502
|
+
: execution.generationRequestKeys?.get(execution.currentResponseMessageId);
|
|
503
|
+
if (key !== undefined)
|
|
504
|
+
execution?.generationTiming?.pause(key, observedAt);
|
|
505
|
+
}
|
|
506
|
+
generationKey(runId, messageId, partId, observedAt, newRequest = false) {
|
|
507
|
+
const execution = this.runner.execution.get(runId);
|
|
508
|
+
if (execution?.generationTiming === undefined)
|
|
509
|
+
return undefined;
|
|
510
|
+
execution.generationPartKeys ??= new Map();
|
|
511
|
+
const existing = execution.generationPartKeys.get(partId);
|
|
512
|
+
if (existing !== undefined)
|
|
513
|
+
return existing;
|
|
514
|
+
execution.generationRequestKeys ??= new Map();
|
|
515
|
+
let key = execution.generationRequestKeys.get(messageId);
|
|
516
|
+
if (newRequest && key !== undefined) {
|
|
517
|
+
execution.generationTiming.end(key, observedAt);
|
|
518
|
+
key = undefined;
|
|
519
|
+
}
|
|
520
|
+
if (key === undefined) {
|
|
521
|
+
const sequence = (execution.generationRequestSequence ?? 0) + 1;
|
|
522
|
+
execution.generationRequestSequence = sequence;
|
|
523
|
+
key = `${messageId}:generation:${sequence}`;
|
|
524
|
+
execution.generationRequestKeys.set(messageId, key);
|
|
525
|
+
}
|
|
526
|
+
execution.generationPartKeys.set(partId, key);
|
|
527
|
+
return key;
|
|
528
|
+
}
|
|
433
529
|
requestPermission(runId, properties) {
|
|
434
530
|
const execution = this.runner.execution.get(runId);
|
|
435
531
|
const requestId = typeof properties?.id === 'string' ? properties.id : undefined;
|
|
@@ -655,3 +751,21 @@ function record(value) {
|
|
|
655
751
|
? value
|
|
656
752
|
: undefined;
|
|
657
753
|
}
|
|
754
|
+
function isOpenCodeGenerationStart(value) {
|
|
755
|
+
return typeof record(value)?.start === 'number';
|
|
756
|
+
}
|
|
757
|
+
function isOpenCodeGenerationEnd(value) {
|
|
758
|
+
return typeof record(value)?.end === 'number';
|
|
759
|
+
}
|
|
760
|
+
function openCodeOutputTokens(value) {
|
|
761
|
+
const tokens = record(value);
|
|
762
|
+
const output = tokens?.output;
|
|
763
|
+
const reasoning = tokens?.reasoning;
|
|
764
|
+
if (!Number.isSafeInteger(output) ||
|
|
765
|
+
!Number.isSafeInteger(reasoning) ||
|
|
766
|
+
output < 0 ||
|
|
767
|
+
reasoning < 0 ||
|
|
768
|
+
output + reasoning > Number.MAX_SAFE_INTEGER)
|
|
769
|
+
return undefined;
|
|
770
|
+
return output + reasoning;
|
|
771
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export class GenerationTimingTracker {
|
|
2
|
+
now;
|
|
3
|
+
requests = new Map();
|
|
4
|
+
constructor(now = () => performance.now()) {
|
|
5
|
+
this.now = now;
|
|
6
|
+
}
|
|
7
|
+
timestamp() {
|
|
8
|
+
return this.now();
|
|
9
|
+
}
|
|
10
|
+
start(key, observedAt) {
|
|
11
|
+
const at = observedAt ?? this.now();
|
|
12
|
+
const request = this.requests.get(key) ?? {
|
|
13
|
+
started: false,
|
|
14
|
+
durationMs: 0,
|
|
15
|
+
ended: false,
|
|
16
|
+
settled: false,
|
|
17
|
+
invalid: false
|
|
18
|
+
};
|
|
19
|
+
this.observe(request, at);
|
|
20
|
+
if (request.ended || request.settled || request.invalid || request.runningAt !== undefined)
|
|
21
|
+
return false;
|
|
22
|
+
request.started = true;
|
|
23
|
+
request.runningAt = at;
|
|
24
|
+
this.requests.set(key, request);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
pause(key, observedAt) {
|
|
28
|
+
const request = this.requests.get(key);
|
|
29
|
+
if (request === undefined || request.settled)
|
|
30
|
+
return;
|
|
31
|
+
const at = observedAt ?? this.now();
|
|
32
|
+
this.observe(request, at);
|
|
33
|
+
if (request.runningAt === undefined)
|
|
34
|
+
return;
|
|
35
|
+
const duration = at - request.runningAt;
|
|
36
|
+
if (!(duration >= 0))
|
|
37
|
+
request.invalid = true;
|
|
38
|
+
else
|
|
39
|
+
request.durationMs += duration;
|
|
40
|
+
request.runningAt = undefined;
|
|
41
|
+
}
|
|
42
|
+
end(key, observedAt) {
|
|
43
|
+
const request = this.requests.get(key);
|
|
44
|
+
if (request === undefined || request.settled || request.ended)
|
|
45
|
+
return;
|
|
46
|
+
this.pause(key, observedAt);
|
|
47
|
+
request.ended = true;
|
|
48
|
+
}
|
|
49
|
+
settle(key, outputTokens, observedAt) {
|
|
50
|
+
const request = this.requests.get(key);
|
|
51
|
+
if (request === undefined ||
|
|
52
|
+
request.settled ||
|
|
53
|
+
!request.started ||
|
|
54
|
+
request.invalid ||
|
|
55
|
+
!Number.isSafeInteger(outputTokens) ||
|
|
56
|
+
outputTokens < 0)
|
|
57
|
+
return undefined;
|
|
58
|
+
this.end(key, observedAt);
|
|
59
|
+
request.settled = true;
|
|
60
|
+
if (request.invalid || request.durationMs < 1)
|
|
61
|
+
return undefined;
|
|
62
|
+
return {
|
|
63
|
+
outputTokens,
|
|
64
|
+
durationMs: request.durationMs,
|
|
65
|
+
tokensPerSecond: Math.round((outputTokens * 100_000) / request.durationMs) / 100
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
discard(key) {
|
|
69
|
+
this.requests.delete(key);
|
|
70
|
+
}
|
|
71
|
+
has(key) {
|
|
72
|
+
return this.requests.has(key);
|
|
73
|
+
}
|
|
74
|
+
observe(request, observedAt) {
|
|
75
|
+
if (!Number.isFinite(observedAt) ||
|
|
76
|
+
(request.lastAt !== undefined && observedAt < request.lastAt))
|
|
77
|
+
request.invalid = true;
|
|
78
|
+
if (Number.isFinite(observedAt))
|
|
79
|
+
request.lastAt = observedAt;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1950,7 +1950,6 @@ function modelSummary(model, index) {
|
|
|
1950
1950
|
defaultReasoningEffort: resolveMarAgentDefaultReasoningEffort(model.defaultReasoningEffort, model.reasoningEfforts),
|
|
1951
1951
|
titleGeneration: model.titleGeneration,
|
|
1952
1952
|
codeMode: model.codeMode,
|
|
1953
|
-
highDensityCompaction: model.highDensityCompaction,
|
|
1954
1953
|
...(model.fastMode === true ? { fastMode: true } : {}),
|
|
1955
1954
|
hostedWebSearch: model.hostedWebSearch,
|
|
1956
1955
|
...(model.responsesTransport === undefined
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.175",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"playwright": "1.62.1",
|
|
37
37
|
"ws": "^8.21.3",
|
|
38
38
|
"zod": "4.4.3",
|
|
39
|
-
"@myagentroam/agent": "0.9.
|
|
40
|
-
"@myagentroam/code-navigation": "0.9.129",
|
|
39
|
+
"@myagentroam/agent": "0.9.175",
|
|
41
40
|
"@myagentroam/marctl": "0.9.123",
|
|
42
|
-
"@myagentroam/protocol": "0.9.
|
|
41
|
+
"@myagentroam/protocol": "0.9.175",
|
|
42
|
+
"@myagentroam/code-navigation": "0.9.129"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/ws": "^8.18.1"
|