@omercnet/paseo-omp 0.4.0-next.122.1 → 0.4.0-next.124.1
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/package.json +1 -1
- package/server/package-version.ts +1 -1
- package/server/provider/session-compaction.ts +187 -0
- package/server/provider/session-events.ts +42 -0
- package/server/provider/session-terminal.ts +307 -0
- package/server/provider/session-usage.ts +234 -0
- package/server/provider/session.ts +76 -721
|
@@ -7,7 +7,6 @@ import type {
|
|
|
7
7
|
ProviderPermissionResponse,
|
|
8
8
|
ProviderSessionConfig,
|
|
9
9
|
ProviderToolCallDetail,
|
|
10
|
-
ProviderUsage,
|
|
11
10
|
} from "@getpaseo/plugin/server/provider";
|
|
12
11
|
import type {
|
|
13
12
|
OmpOperationalFailure,
|
|
@@ -40,7 +39,6 @@ import type {
|
|
|
40
39
|
OmpModel,
|
|
41
40
|
OmpRpcEvent,
|
|
42
41
|
OmpSessionState,
|
|
43
|
-
OmpSessionStats,
|
|
44
42
|
OmpToolApprovalCancel,
|
|
45
43
|
OmpToolApprovalRequest,
|
|
46
44
|
} from "./omp-rpc-protocol";
|
|
@@ -64,8 +62,23 @@ import {
|
|
|
64
62
|
OmpPublicError,
|
|
65
63
|
utf8Bytes,
|
|
66
64
|
} from "./security";
|
|
65
|
+
import { OmpSessionCompaction } from "./session-compaction";
|
|
67
66
|
import type { OmpSessionDescriptor } from "./session-descriptors";
|
|
68
67
|
import { validateNativeSessionId } from "./session-descriptors";
|
|
68
|
+
import { isNativeTurnActivity, isPassiveUiMethod, isRuntimeConfigEvent } from "./session-events";
|
|
69
|
+
import {
|
|
70
|
+
type ActiveTurn,
|
|
71
|
+
assistantTerminalOutcome,
|
|
72
|
+
classifyTerminalCandidate,
|
|
73
|
+
createActiveTurn,
|
|
74
|
+
historyTerminalOutcome,
|
|
75
|
+
nativeEntryId,
|
|
76
|
+
type PendingUser,
|
|
77
|
+
type TerminalCandidate,
|
|
78
|
+
terminalOutcome,
|
|
79
|
+
unknownTerminalOutcomeError,
|
|
80
|
+
} from "./session-terminal";
|
|
81
|
+
import { OmpSessionUsage } from "./session-usage";
|
|
69
82
|
import { OmpSubsessionProjector } from "./subsessions";
|
|
70
83
|
import {
|
|
71
84
|
defaultOmpTimelineScheduler,
|
|
@@ -235,86 +248,6 @@ async function waitForReplay<T>(operation: Promise<T>, signal: AbortSignal): Pro
|
|
|
235
248
|
}
|
|
236
249
|
}
|
|
237
250
|
|
|
238
|
-
type PendingUser = {
|
|
239
|
-
clientMessageId: string;
|
|
240
|
-
text: string;
|
|
241
|
-
accepted: boolean;
|
|
242
|
-
fallbackOnFinish: boolean;
|
|
243
|
-
bufferedEchoes: OmpMessage[];
|
|
244
|
-
};
|
|
245
|
-
type TerminalCorrelationEvidence = "fresh-native-user" | "current-assistant";
|
|
246
|
-
|
|
247
|
-
type TerminalCorrelation = {
|
|
248
|
-
policy: "initial-turn" | "ordered-legacy" | "native-command";
|
|
249
|
-
evidence: Map<TerminalCorrelationEvidence, number>;
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
type TerminalCandidate = {
|
|
253
|
-
event: Extract<OmpRpcEvent, { type: "agent_end" }>;
|
|
254
|
-
arrivalSequence: number;
|
|
255
|
-
confidence:
|
|
256
|
-
| "keyed"
|
|
257
|
-
| "initial-turn"
|
|
258
|
-
| "ordered-legacy"
|
|
259
|
-
| "native-command"
|
|
260
|
-
| "interrupted"
|
|
261
|
-
| "ambiguous";
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
type ActiveTurn = {
|
|
265
|
-
turnId: string;
|
|
266
|
-
clientMessageId: string;
|
|
267
|
-
generation: number;
|
|
268
|
-
promptResultEmitted: boolean;
|
|
269
|
-
started: boolean;
|
|
270
|
-
terminal: boolean;
|
|
271
|
-
interrupted: boolean;
|
|
272
|
-
starting: boolean;
|
|
273
|
-
nativeActivity: boolean;
|
|
274
|
-
userEchoObserved: boolean;
|
|
275
|
-
localOnlyDisabled: boolean;
|
|
276
|
-
localOnlyEligible: boolean;
|
|
277
|
-
awaitingPermissionEvidence: boolean;
|
|
278
|
-
activitySequence: number;
|
|
279
|
-
acknowledged: boolean;
|
|
280
|
-
terminalCorrelation: TerminalCorrelation;
|
|
281
|
-
replayingBufferedEvents: boolean;
|
|
282
|
-
agentInvoked?: boolean;
|
|
283
|
-
nativeRequestId?: string;
|
|
284
|
-
promptAcceptedEventIndex?: number;
|
|
285
|
-
localOnlyTimer?: unknown;
|
|
286
|
-
usagePollTimer?: unknown;
|
|
287
|
-
usagePoll?: Promise<void>;
|
|
288
|
-
usageSampleFloor: number;
|
|
289
|
-
manualCompaction: boolean;
|
|
290
|
-
manualCompactionPending: boolean;
|
|
291
|
-
manualCompactionDeadlineTimer?: unknown;
|
|
292
|
-
agentEndPending: boolean;
|
|
293
|
-
agentEndRetryTimer?: unknown;
|
|
294
|
-
agentEndDeadlineTimer?: unknown;
|
|
295
|
-
agentEndCheck?: Promise<void>;
|
|
296
|
-
ambiguousTerminalTimer?: unknown;
|
|
297
|
-
terminalizing: boolean;
|
|
298
|
-
terminalization?: Promise<void>;
|
|
299
|
-
terminalOutcome?: TurnOutcome;
|
|
300
|
-
operationalTerminalStage?: "failed" | "unresolved";
|
|
301
|
-
terminalWake?: VoidDeferred;
|
|
302
|
-
steerReady: VoidDeferred;
|
|
303
|
-
steersInFlight: number;
|
|
304
|
-
deferredAgentEnd?: TerminalCandidate;
|
|
305
|
-
activeToolCallIds: Set<string>;
|
|
306
|
-
bufferedEvents: OmpRpcEvent[];
|
|
307
|
-
pendingUsers: PendingUser[];
|
|
308
|
-
userEchoes: OmpMessage[];
|
|
309
|
-
userCorrelationActive: boolean;
|
|
310
|
-
userLookups: Set<Promise<void>>;
|
|
311
|
-
completedMessageCount: number;
|
|
312
|
-
streamedMessageEntryIds: string[];
|
|
313
|
-
streamedMessageIdentityComplete: boolean;
|
|
314
|
-
lastCompletedAssistantOutcome?: AgentEndOutcome;
|
|
315
|
-
lastCompletedAssistantEntryId?: string;
|
|
316
|
-
};
|
|
317
|
-
|
|
318
251
|
type PendingAbort = {
|
|
319
252
|
turn: ActiveTurn;
|
|
320
253
|
generation: number;
|
|
@@ -362,28 +295,6 @@ function permissionFingerprint(request: OmpQuestionRequest): string {
|
|
|
362
295
|
return createHash("sha256").update(JSON.stringify(request)).digest("base64url");
|
|
363
296
|
}
|
|
364
297
|
|
|
365
|
-
type VoidDeferred = {
|
|
366
|
-
promise: Promise<void>;
|
|
367
|
-
resolve(value?: void | PromiseLike<void>): void;
|
|
368
|
-
reject(reason?: unknown): void;
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
type TurnOutcome = {
|
|
372
|
-
state: "completed" | "failed" | "canceled";
|
|
373
|
-
error?: ProviderError;
|
|
374
|
-
usageSampled: boolean;
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
type ActiveCompaction = {
|
|
378
|
-
id: string;
|
|
379
|
-
trigger: "auto" | "manual";
|
|
380
|
-
turnId: string;
|
|
381
|
-
generation: number;
|
|
382
|
-
retrying: boolean;
|
|
383
|
-
action?: string;
|
|
384
|
-
preTokens?: number;
|
|
385
|
-
};
|
|
386
|
-
|
|
387
298
|
function providerError(error: unknown, fallback: string): { message: string } {
|
|
388
299
|
return { message: isOmpPublicError(error) ? error.message : fallback };
|
|
389
300
|
}
|
|
@@ -408,258 +319,6 @@ async function settleSessionCleanup(promises: readonly Promise<void>[]): Promise
|
|
|
408
319
|
throw new AggregateError(failures, "OMP session initialization cleanup failed");
|
|
409
320
|
}
|
|
410
321
|
}
|
|
411
|
-
function nativeEntryId(message: OmpMessage): string | undefined {
|
|
412
|
-
return message.entryId;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
type AgentEndOutcome = "completed" | "failed" | "canceled";
|
|
416
|
-
type AssistantTerminalStatus = AgentEndOutcome | "unavailable";
|
|
417
|
-
|
|
418
|
-
function assistantTerminalOutcome(
|
|
419
|
-
message: Extract<OmpMessage, { role: "assistant" }>,
|
|
420
|
-
): AgentEndOutcome {
|
|
421
|
-
const stopReason = message.stopReason?.toLowerCase();
|
|
422
|
-
if (stopReason === "aborted" || stopReason === "canceled" || stopReason === "cancelled") {
|
|
423
|
-
return "canceled";
|
|
424
|
-
}
|
|
425
|
-
return stopReason === "error" || message.errorMessage ? "failed" : "completed";
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
function lastAssistantStatus(
|
|
429
|
-
messages: readonly OmpMessage[],
|
|
430
|
-
startIndex = 0,
|
|
431
|
-
): AssistantTerminalStatus {
|
|
432
|
-
for (let index = messages.length - 1; index >= startIndex; index -= 1) {
|
|
433
|
-
const message = messages[index];
|
|
434
|
-
if (message?.role !== "assistant") continue;
|
|
435
|
-
return assistantTerminalOutcome(message);
|
|
436
|
-
}
|
|
437
|
-
return "unavailable";
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function terminalOutcome(
|
|
441
|
-
event: Extract<OmpRpcEvent, { type: "agent_end" }>,
|
|
442
|
-
turn: Pick<ActiveTurn, "completedMessageCount" | "lastCompletedAssistantOutcome">,
|
|
443
|
-
): AgentEndOutcome | undefined {
|
|
444
|
-
const messages = event.messages;
|
|
445
|
-
if (
|
|
446
|
-
messages !== undefined &&
|
|
447
|
-
(event.messageCount === undefined || messages.length >= event.messageCount)
|
|
448
|
-
) {
|
|
449
|
-
const status = lastAssistantStatus(messages);
|
|
450
|
-
return status === "unavailable" ? "completed" : status;
|
|
451
|
-
}
|
|
452
|
-
if (event.messageCount === 0) return "completed";
|
|
453
|
-
if (
|
|
454
|
-
turn.lastCompletedAssistantOutcome !== undefined &&
|
|
455
|
-
(event.messageCount === undefined || turn.completedMessageCount >= event.messageCount)
|
|
456
|
-
) {
|
|
457
|
-
return turn.lastCompletedAssistantOutcome;
|
|
458
|
-
}
|
|
459
|
-
return undefined;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function historyTerminalOutcome(
|
|
463
|
-
messages: readonly OmpMessage[],
|
|
464
|
-
declaredCount: number,
|
|
465
|
-
turn: Pick<
|
|
466
|
-
ActiveTurn,
|
|
467
|
-
| "streamedMessageEntryIds"
|
|
468
|
-
| "streamedMessageIdentityComplete"
|
|
469
|
-
| "lastCompletedAssistantOutcome"
|
|
470
|
-
| "lastCompletedAssistantEntryId"
|
|
471
|
-
>,
|
|
472
|
-
retainedMessages: readonly OmpMessage[],
|
|
473
|
-
): AgentEndOutcome | undefined {
|
|
474
|
-
if (
|
|
475
|
-
messages.length < declaredCount ||
|
|
476
|
-
!turn.streamedMessageIdentityComplete ||
|
|
477
|
-
turn.streamedMessageEntryIds.length === 0
|
|
478
|
-
) {
|
|
479
|
-
return undefined;
|
|
480
|
-
}
|
|
481
|
-
const startIndex = messages.length - declaredCount;
|
|
482
|
-
let historyIndex = startIndex;
|
|
483
|
-
for (const entryId of turn.streamedMessageEntryIds) {
|
|
484
|
-
while (historyIndex < messages.length) {
|
|
485
|
-
const historyMessage = messages[historyIndex];
|
|
486
|
-
if (historyMessage && nativeEntryId(historyMessage) === entryId) break;
|
|
487
|
-
historyIndex += 1;
|
|
488
|
-
}
|
|
489
|
-
if (historyIndex >= messages.length) return undefined;
|
|
490
|
-
const correlated = messages[historyIndex];
|
|
491
|
-
if (
|
|
492
|
-
entryId === turn.lastCompletedAssistantEntryId &&
|
|
493
|
-
(correlated?.role !== "assistant" ||
|
|
494
|
-
assistantTerminalOutcome(correlated) !== turn.lastCompletedAssistantOutcome)
|
|
495
|
-
)
|
|
496
|
-
return undefined;
|
|
497
|
-
historyIndex += 1;
|
|
498
|
-
}
|
|
499
|
-
historyIndex = startIndex;
|
|
500
|
-
for (const retained of retainedMessages) {
|
|
501
|
-
const entryId = nativeEntryId(retained);
|
|
502
|
-
if (!entryId) return undefined;
|
|
503
|
-
while (historyIndex < messages.length) {
|
|
504
|
-
const historyMessage = messages[historyIndex];
|
|
505
|
-
if (historyMessage && nativeEntryId(historyMessage) === entryId) break;
|
|
506
|
-
historyIndex += 1;
|
|
507
|
-
}
|
|
508
|
-
if (historyIndex >= messages.length) return undefined;
|
|
509
|
-
const correlated = messages[historyIndex];
|
|
510
|
-
if (!correlated || correlated.role !== retained.role) return undefined;
|
|
511
|
-
if (
|
|
512
|
-
retained.role === "assistant" &&
|
|
513
|
-
correlated.role === "assistant" &&
|
|
514
|
-
assistantTerminalOutcome(retained) !== assistantTerminalOutcome(correlated)
|
|
515
|
-
)
|
|
516
|
-
return undefined;
|
|
517
|
-
historyIndex += 1;
|
|
518
|
-
}
|
|
519
|
-
const status = lastAssistantStatus(messages, startIndex);
|
|
520
|
-
return status === "unavailable" ? undefined : status;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
function unknownTerminalOutcomeError(
|
|
524
|
-
event: Extract<OmpRpcEvent, { type: "agent_end" }>,
|
|
525
|
-
turn: Pick<ActiveTurn, "completedMessageCount" | "lastCompletedAssistantOutcome">,
|
|
526
|
-
): string {
|
|
527
|
-
const retainedMessages = event.messages?.length ?? 0;
|
|
528
|
-
const retainedStatus = event.messages ? lastAssistantStatus(event.messages) : "unavailable";
|
|
529
|
-
const lastStatus =
|
|
530
|
-
retainedStatus === "unavailable"
|
|
531
|
-
? (turn.lastCompletedAssistantOutcome ?? "unavailable")
|
|
532
|
-
: retainedStatus;
|
|
533
|
-
return (
|
|
534
|
-
"OMP agent_end omitted terminal messages; outcome is unknown " +
|
|
535
|
-
`(declaredCount=${event.messageCount ?? "unavailable"}, ` +
|
|
536
|
-
`observedCount=${turn.completedMessageCount}, ` +
|
|
537
|
-
`retainedTerminalMessages=${retainedMessages}, lastAssistantStatus=${lastStatus})`
|
|
538
|
-
);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function isNativeTurnActivity(event: OmpRpcEvent): boolean {
|
|
542
|
-
if (
|
|
543
|
-
event.type === "agent_start" ||
|
|
544
|
-
event.type === "turn_start" ||
|
|
545
|
-
event.type === "turn_end" ||
|
|
546
|
-
event.type === "agent_end" ||
|
|
547
|
-
event.type === "auto_compaction_start" ||
|
|
548
|
-
event.type === "auto_compaction_end"
|
|
549
|
-
) {
|
|
550
|
-
return true;
|
|
551
|
-
}
|
|
552
|
-
if (
|
|
553
|
-
event.type === "message_start" ||
|
|
554
|
-
event.type === "message_update" ||
|
|
555
|
-
event.type === "message_end"
|
|
556
|
-
) {
|
|
557
|
-
return event.message.role === "assistant";
|
|
558
|
-
}
|
|
559
|
-
return event.type.startsWith("tool_execution_");
|
|
560
|
-
}
|
|
561
|
-
function isRuntimeConfigEvent(event: OmpRpcEvent): boolean {
|
|
562
|
-
return (
|
|
563
|
-
event.type === "model_changed" ||
|
|
564
|
-
event.type === "thinking_level_changed" ||
|
|
565
|
-
event.type === "retry_fallback_applied" ||
|
|
566
|
-
event.type === "retry_fallback_succeeded"
|
|
567
|
-
);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function isPassiveUiMethod(method: string): boolean {
|
|
571
|
-
return (
|
|
572
|
-
method === "cancel" ||
|
|
573
|
-
method === "notify" ||
|
|
574
|
-
method === "open_url" ||
|
|
575
|
-
method === "setStatus" ||
|
|
576
|
-
method === "setWidget" ||
|
|
577
|
-
method === "setTitle" ||
|
|
578
|
-
method === "set_editor_text"
|
|
579
|
-
);
|
|
580
|
-
}
|
|
581
|
-
function createActiveTurn(
|
|
582
|
-
clientMessageId: string,
|
|
583
|
-
text: string,
|
|
584
|
-
generation: number,
|
|
585
|
-
terminalPolicy: TerminalCorrelation["policy"],
|
|
586
|
-
manualCompaction = false,
|
|
587
|
-
): ActiveTurn {
|
|
588
|
-
return {
|
|
589
|
-
turnId: randomUUID(),
|
|
590
|
-
clientMessageId,
|
|
591
|
-
agentInvoked: undefined,
|
|
592
|
-
generation,
|
|
593
|
-
promptResultEmitted: false,
|
|
594
|
-
started: false,
|
|
595
|
-
terminal: false,
|
|
596
|
-
awaitingPermissionEvidence: false,
|
|
597
|
-
interrupted: false,
|
|
598
|
-
starting: true,
|
|
599
|
-
nativeActivity: false,
|
|
600
|
-
userEchoObserved: false,
|
|
601
|
-
localOnlyDisabled: false,
|
|
602
|
-
localOnlyEligible: false,
|
|
603
|
-
usageSampleFloor: 0,
|
|
604
|
-
agentEndPending: false,
|
|
605
|
-
terminalizing: false,
|
|
606
|
-
manualCompactionPending: manualCompaction,
|
|
607
|
-
manualCompaction,
|
|
608
|
-
activitySequence: 0,
|
|
609
|
-
acknowledged: false,
|
|
610
|
-
terminalCorrelation: { policy: terminalPolicy, evidence: new Map() },
|
|
611
|
-
replayingBufferedEvents: false,
|
|
612
|
-
steersInFlight: 0,
|
|
613
|
-
activeToolCallIds: new Set(),
|
|
614
|
-
steerReady: Promise.withResolvers<void>(),
|
|
615
|
-
userCorrelationActive: false,
|
|
616
|
-
userLookups: new Set(),
|
|
617
|
-
userEchoes: [],
|
|
618
|
-
completedMessageCount: 0,
|
|
619
|
-
bufferedEvents: [],
|
|
620
|
-
streamedMessageEntryIds: [],
|
|
621
|
-
streamedMessageIdentityComplete: true,
|
|
622
|
-
pendingUsers: [
|
|
623
|
-
{
|
|
624
|
-
clientMessageId,
|
|
625
|
-
text,
|
|
626
|
-
accepted: true,
|
|
627
|
-
fallbackOnFinish: true,
|
|
628
|
-
bufferedEchoes: [],
|
|
629
|
-
},
|
|
630
|
-
],
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
function classifyTerminalCandidate(
|
|
634
|
-
turn: ActiveTurn,
|
|
635
|
-
event: Extract<OmpRpcEvent, { type: "agent_end" }>,
|
|
636
|
-
arrivalSequence = turn.activitySequence,
|
|
637
|
-
): TerminalCandidate {
|
|
638
|
-
if (event.requestId !== undefined) return { event, confidence: "keyed", arrivalSequence };
|
|
639
|
-
if (turn.agentInvoked === false) return { event, confidence: "ambiguous", arrivalSequence };
|
|
640
|
-
if (turn.terminalCorrelation.policy === "initial-turn") {
|
|
641
|
-
return { event, confidence: "initial-turn", arrivalSequence };
|
|
642
|
-
}
|
|
643
|
-
const assistantSequence = turn.terminalCorrelation.evidence.get("current-assistant");
|
|
644
|
-
if (
|
|
645
|
-
turn.terminalCorrelation.policy === "native-command" &&
|
|
646
|
-
assistantSequence !== undefined &&
|
|
647
|
-
assistantSequence <= arrivalSequence
|
|
648
|
-
) {
|
|
649
|
-
return { event, confidence: "native-command", arrivalSequence };
|
|
650
|
-
}
|
|
651
|
-
const userSequence = turn.terminalCorrelation.evidence.get("fresh-native-user");
|
|
652
|
-
if (
|
|
653
|
-
userSequence !== undefined &&
|
|
654
|
-
assistantSequence !== undefined &&
|
|
655
|
-
userSequence < assistantSequence &&
|
|
656
|
-
assistantSequence <= arrivalSequence
|
|
657
|
-
) {
|
|
658
|
-
return { event, confidence: "ordered-legacy", arrivalSequence };
|
|
659
|
-
}
|
|
660
|
-
return { event, confidence: "ambiguous", arrivalSequence };
|
|
661
|
-
}
|
|
662
|
-
|
|
663
322
|
export class OmpProviderSession {
|
|
664
323
|
readonly id: string;
|
|
665
324
|
readonly cwd: string;
|
|
@@ -695,19 +354,8 @@ export class OmpProviderSession {
|
|
|
695
354
|
private configRevision = 0;
|
|
696
355
|
private recoveryUsesNativeConfig = false;
|
|
697
356
|
private activeAbort: PendingAbort | null = null;
|
|
698
|
-
private
|
|
699
|
-
private
|
|
700
|
-
private usageSample: {
|
|
701
|
-
turn: ActiveTurn;
|
|
702
|
-
generation: number;
|
|
703
|
-
runtime: OmpRuntimeSession;
|
|
704
|
-
epoch: number;
|
|
705
|
-
sequence: number;
|
|
706
|
-
promise: Promise<OmpSessionState | undefined>;
|
|
707
|
-
} | null = null;
|
|
708
|
-
private activeCompaction: ActiveCompaction | null = null;
|
|
709
|
-
private discardedCompactionEnds = 0;
|
|
710
|
-
private lastUsage: ProviderUsage | null = null;
|
|
357
|
+
private readonly usage: OmpSessionUsage;
|
|
358
|
+
private readonly compaction: OmpSessionCompaction;
|
|
711
359
|
private revertInFlight = false;
|
|
712
360
|
private runtimeTurnCompleted = false;
|
|
713
361
|
private commandCatalog: OmpAvailableCommand[];
|
|
@@ -751,6 +399,26 @@ export class OmpProviderSession {
|
|
|
751
399
|
this.id = id;
|
|
752
400
|
this.cwd = config.cwd;
|
|
753
401
|
this.scheduler = scheduler;
|
|
402
|
+
this.usage = new OmpSessionUsage(
|
|
403
|
+
id,
|
|
404
|
+
emit,
|
|
405
|
+
scheduler,
|
|
406
|
+
() => ({
|
|
407
|
+
closed: this.closed,
|
|
408
|
+
runtimeDead: this.runtimeDead !== null,
|
|
409
|
+
generation: this.generation,
|
|
410
|
+
runtime: this.runtime,
|
|
411
|
+
activeTurn: this.activeTurn,
|
|
412
|
+
}),
|
|
413
|
+
(turn) =>
|
|
414
|
+
!this.closed &&
|
|
415
|
+
!this.runtimeDead &&
|
|
416
|
+
!turn.terminal &&
|
|
417
|
+
!turn.terminalizing &&
|
|
418
|
+
!turn.agentEndPending &&
|
|
419
|
+
turn.generation === this.generation &&
|
|
420
|
+
this.activeTurn === turn,
|
|
421
|
+
);
|
|
754
422
|
this.dataFilter = new OmpPublicDataSerializer(outputRedactionValues);
|
|
755
423
|
this.nativeModelsByPublicId = nativeModelsByPublicId;
|
|
756
424
|
this.commandCatalog = commandCatalog;
|
|
@@ -764,6 +432,13 @@ export class OmpProviderSession {
|
|
|
764
432
|
capabilities.includes("timeline.plugin"),
|
|
765
433
|
hostTools.labels,
|
|
766
434
|
);
|
|
435
|
+
this.compaction = new OmpSessionCompaction(
|
|
436
|
+
id,
|
|
437
|
+
emit,
|
|
438
|
+
this.projector,
|
|
439
|
+
this.dataFilter,
|
|
440
|
+
this.usage,
|
|
441
|
+
);
|
|
767
442
|
this.subsessions = capabilities.includes("session.subsession")
|
|
768
443
|
? new OmpSubsessionProjector(
|
|
769
444
|
id,
|
|
@@ -1105,289 +780,6 @@ export class OmpProviderSession {
|
|
|
1105
780
|
});
|
|
1106
781
|
}
|
|
1107
782
|
|
|
1108
|
-
private usageFrom(
|
|
1109
|
-
state: OmpSessionState | undefined,
|
|
1110
|
-
stats: OmpSessionStats | undefined,
|
|
1111
|
-
): ProviderUsage | undefined {
|
|
1112
|
-
if (!state?.contextUsage && !stats) return undefined;
|
|
1113
|
-
const stateContext = state?.contextUsage;
|
|
1114
|
-
const statsContext = stats?.contextUsage;
|
|
1115
|
-
const modelCapacity = state?.model?.contextWindow;
|
|
1116
|
-
const inputTokens = stats?.tokens?.input;
|
|
1117
|
-
const cachedInputTokens = stats?.tokens?.cacheRead;
|
|
1118
|
-
const outputTokens = stats?.tokens?.output;
|
|
1119
|
-
const totalCostUsd = stats?.cost;
|
|
1120
|
-
const contextTokens =
|
|
1121
|
-
typeof stateContext?.tokens === "number"
|
|
1122
|
-
? stateContext.tokens
|
|
1123
|
-
: typeof statsContext?.tokens === "number"
|
|
1124
|
-
? statsContext.tokens
|
|
1125
|
-
: undefined;
|
|
1126
|
-
const contextWindow =
|
|
1127
|
-
typeof stateContext?.contextWindow === "number" && stateContext.contextWindow > 0
|
|
1128
|
-
? stateContext.contextWindow
|
|
1129
|
-
: typeof statsContext?.contextWindow === "number" && statsContext.contextWindow > 0
|
|
1130
|
-
? statsContext.contextWindow
|
|
1131
|
-
: typeof modelCapacity === "number" && modelCapacity > 0
|
|
1132
|
-
? modelCapacity
|
|
1133
|
-
: undefined;
|
|
1134
|
-
const usage: ProviderUsage = {
|
|
1135
|
-
...(typeof inputTokens === "number" ? { inputTokens } : {}),
|
|
1136
|
-
...(typeof cachedInputTokens === "number" ? { cachedInputTokens } : {}),
|
|
1137
|
-
...(typeof outputTokens === "number" ? { outputTokens } : {}),
|
|
1138
|
-
...(typeof totalCostUsd === "number" ? { totalCostUsd } : {}),
|
|
1139
|
-
...(contextTokens !== undefined ? { contextWindowUsedTokens: contextTokens } : {}),
|
|
1140
|
-
...(contextWindow !== undefined ? { contextWindowMaxTokens: contextWindow } : {}),
|
|
1141
|
-
};
|
|
1142
|
-
return Object.keys(usage).length > 0 ? usage : undefined;
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
private ownsUsageSample(
|
|
1146
|
-
turn: ActiveTurn,
|
|
1147
|
-
generation: number,
|
|
1148
|
-
runtime: OmpRuntimeSession,
|
|
1149
|
-
): boolean {
|
|
1150
|
-
return (
|
|
1151
|
-
!this.closed &&
|
|
1152
|
-
!this.runtimeDead &&
|
|
1153
|
-
!turn.terminal &&
|
|
1154
|
-
generation === this.generation &&
|
|
1155
|
-
runtime === this.runtime &&
|
|
1156
|
-
this.activeTurn === turn
|
|
1157
|
-
);
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
private publishUsageSnapshot(
|
|
1161
|
-
turn: ActiveTurn,
|
|
1162
|
-
minimumEpoch = this.usageEpoch,
|
|
1163
|
-
minimumSequence = turn.usageSampleFloor,
|
|
1164
|
-
): Promise<OmpSessionState | undefined> {
|
|
1165
|
-
const generation = turn.generation;
|
|
1166
|
-
const runtime = this.runtime;
|
|
1167
|
-
if (!this.ownsUsageSample(turn, generation, runtime)) return Promise.resolve(undefined);
|
|
1168
|
-
const current = this.usageSample;
|
|
1169
|
-
if (current) {
|
|
1170
|
-
if (
|
|
1171
|
-
current.turn === turn &&
|
|
1172
|
-
current.generation === generation &&
|
|
1173
|
-
current.runtime === runtime &&
|
|
1174
|
-
current.epoch >= minimumEpoch &&
|
|
1175
|
-
current.sequence >= minimumSequence
|
|
1176
|
-
) {
|
|
1177
|
-
return current.promise;
|
|
1178
|
-
}
|
|
1179
|
-
return current.promise.then(() => {
|
|
1180
|
-
if (!this.ownsUsageSample(turn, generation, runtime)) return undefined;
|
|
1181
|
-
return this.publishUsageSnapshot(turn, minimumEpoch, minimumSequence);
|
|
1182
|
-
});
|
|
1183
|
-
}
|
|
1184
|
-
const epoch = this.usageEpoch;
|
|
1185
|
-
const sequence = ++this.usageSequence;
|
|
1186
|
-
const promise = Promise.allSettled([runtime.getState(), runtime.getSessionStats()]).then(
|
|
1187
|
-
([stateResult, statsResult]) => {
|
|
1188
|
-
if (
|
|
1189
|
-
!this.ownsUsageSample(turn, generation, runtime) ||
|
|
1190
|
-
epoch !== this.usageEpoch ||
|
|
1191
|
-
epoch < minimumEpoch ||
|
|
1192
|
-
sequence < turn.usageSampleFloor ||
|
|
1193
|
-
sequence < minimumSequence
|
|
1194
|
-
) {
|
|
1195
|
-
return undefined;
|
|
1196
|
-
}
|
|
1197
|
-
const state = stateResult.status === "fulfilled" ? stateResult.value : undefined;
|
|
1198
|
-
const stats = statsResult.status === "fulfilled" ? statsResult.value : undefined;
|
|
1199
|
-
const usage = this.usageFrom(state, stats);
|
|
1200
|
-
if (usage) {
|
|
1201
|
-
this.lastUsage = usage;
|
|
1202
|
-
this.emit({ type: "session.usage", sessionId: this.id, turnId: turn.turnId, usage });
|
|
1203
|
-
}
|
|
1204
|
-
return state;
|
|
1205
|
-
},
|
|
1206
|
-
);
|
|
1207
|
-
this.usageSample = { turn, generation, runtime, epoch, sequence, promise };
|
|
1208
|
-
void promise.finally(() => {
|
|
1209
|
-
if (this.usageSample?.promise === promise) this.usageSample = null;
|
|
1210
|
-
});
|
|
1211
|
-
return promise;
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
private async boundedUsageSnapshot(
|
|
1215
|
-
turn: ActiveTurn,
|
|
1216
|
-
timeoutMs: number,
|
|
1217
|
-
): Promise<OmpSessionState | undefined> {
|
|
1218
|
-
const timeout = Promise.withResolvers<undefined>();
|
|
1219
|
-
const timer = this.scheduler.set(() => timeout.resolve(undefined), timeoutMs);
|
|
1220
|
-
try {
|
|
1221
|
-
return await Promise.race([this.publishUsageSnapshot(turn), timeout.promise]);
|
|
1222
|
-
} finally {
|
|
1223
|
-
this.scheduler.clear(timer);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
private async boundedTerminalState(
|
|
1228
|
-
turn: ActiveTurn,
|
|
1229
|
-
timeoutMs: number,
|
|
1230
|
-
): Promise<OmpSessionState | undefined> {
|
|
1231
|
-
const generation = turn.generation;
|
|
1232
|
-
const runtime = this.runtime;
|
|
1233
|
-
const timeout = Promise.withResolvers<undefined>();
|
|
1234
|
-
const timer = this.scheduler.set(() => timeout.resolve(undefined), timeoutMs);
|
|
1235
|
-
try {
|
|
1236
|
-
const state = await Promise.race([runtime.getState(), timeout.promise]);
|
|
1237
|
-
return this.ownsUsageSample(turn, generation, runtime) ? state : undefined;
|
|
1238
|
-
} catch {
|
|
1239
|
-
return undefined;
|
|
1240
|
-
} finally {
|
|
1241
|
-
this.scheduler.clear(timer);
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
private isActiveTurn(turn: ActiveTurn): boolean {
|
|
1246
|
-
return (
|
|
1247
|
-
!this.closed &&
|
|
1248
|
-
!this.runtimeDead &&
|
|
1249
|
-
!turn.terminal &&
|
|
1250
|
-
!turn.terminalizing &&
|
|
1251
|
-
!turn.agentEndPending &&
|
|
1252
|
-
turn.generation === this.generation &&
|
|
1253
|
-
this.activeTurn === turn
|
|
1254
|
-
);
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
private scheduleUsagePoll(turn: ActiveTurn, delayMs = USAGE_POLL_MS): void {
|
|
1258
|
-
if (!this.isActiveTurn(turn) || turn.usagePollTimer !== undefined) return;
|
|
1259
|
-
turn.usagePollTimer = this.scheduler.set(() => {
|
|
1260
|
-
turn.usagePollTimer = undefined;
|
|
1261
|
-
this.pollUsage(turn);
|
|
1262
|
-
}, delayMs);
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
private pollUsage(turn: ActiveTurn): void {
|
|
1266
|
-
if (!this.isActiveTurn(turn) || turn.usagePoll) return;
|
|
1267
|
-
const poll = this.publishUsageSnapshot(turn).then(() => undefined);
|
|
1268
|
-
turn.usagePoll = poll;
|
|
1269
|
-
void poll.finally(() => {
|
|
1270
|
-
if (turn.usagePoll === poll) turn.usagePoll = undefined;
|
|
1271
|
-
this.scheduleUsagePoll(turn);
|
|
1272
|
-
});
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
private stopUsagePoll(turn: ActiveTurn): void {
|
|
1276
|
-
if (turn.usagePollTimer === undefined) return;
|
|
1277
|
-
this.scheduler.clear(turn.usagePollTimer);
|
|
1278
|
-
turn.usagePollTimer = undefined;
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
private startCompaction(turn: ActiveTurn, trigger: "auto" | "manual", action?: string): void {
|
|
1282
|
-
if (this.discardedCompactionEnds > 0) {
|
|
1283
|
-
this.discardedCompactionEnds += 1;
|
|
1284
|
-
return;
|
|
1285
|
-
}
|
|
1286
|
-
const active = this.activeCompaction;
|
|
1287
|
-
if (active && active.trigger === trigger && active.action === action) {
|
|
1288
|
-
active.retrying = false;
|
|
1289
|
-
return;
|
|
1290
|
-
}
|
|
1291
|
-
if (active) {
|
|
1292
|
-
this.retireCompaction("OMP emitted overlapping compactions");
|
|
1293
|
-
this.discardedCompactionEnds = 2;
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1296
|
-
const operation: ActiveCompaction = {
|
|
1297
|
-
id: randomUUID(),
|
|
1298
|
-
trigger,
|
|
1299
|
-
turnId: turn.turnId,
|
|
1300
|
-
generation: turn.generation,
|
|
1301
|
-
action,
|
|
1302
|
-
retrying: false,
|
|
1303
|
-
preTokens: this.lastUsage?.contextWindowUsedTokens,
|
|
1304
|
-
};
|
|
1305
|
-
this.activeCompaction = operation;
|
|
1306
|
-
this.projector.flush(true);
|
|
1307
|
-
this.emit({
|
|
1308
|
-
type: "timeline.item",
|
|
1309
|
-
sessionId: this.id,
|
|
1310
|
-
item: {
|
|
1311
|
-
id: operation.id,
|
|
1312
|
-
type: "compaction",
|
|
1313
|
-
status: "loading",
|
|
1314
|
-
trigger,
|
|
1315
|
-
...(operation.preTokens !== undefined ? { preTokens: operation.preTokens } : {}),
|
|
1316
|
-
},
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
private finishCompaction(
|
|
1321
|
-
state: "completed" | "failed" | "canceled" | "skipped",
|
|
1322
|
-
options: { tokensBefore?: number | null; message?: string } = {},
|
|
1323
|
-
): void {
|
|
1324
|
-
const operation = this.activeCompaction;
|
|
1325
|
-
if (!operation) return;
|
|
1326
|
-
this.activeCompaction = null;
|
|
1327
|
-
this.projector.flush(true);
|
|
1328
|
-
if (state === "completed") {
|
|
1329
|
-
this.usageEpoch += 1;
|
|
1330
|
-
const staleSample = this.usageSample;
|
|
1331
|
-
if (staleSample && staleSample.epoch < this.usageEpoch) {
|
|
1332
|
-
this.usageSample = null;
|
|
1333
|
-
staleSample.turn.usagePoll = undefined;
|
|
1334
|
-
}
|
|
1335
|
-
this.lastUsage = null;
|
|
1336
|
-
}
|
|
1337
|
-
if (state !== "completed") {
|
|
1338
|
-
const defaultMessage =
|
|
1339
|
-
state === "failed"
|
|
1340
|
-
? "OMP compaction failed"
|
|
1341
|
-
: state === "canceled"
|
|
1342
|
-
? "OMP compaction canceled"
|
|
1343
|
-
: "OMP compaction skipped";
|
|
1344
|
-
this.emit({
|
|
1345
|
-
type: "timeline.item",
|
|
1346
|
-
sessionId: this.id,
|
|
1347
|
-
item: {
|
|
1348
|
-
id: operation.id,
|
|
1349
|
-
type: "notification",
|
|
1350
|
-
level: state === "failed" ? "error" : "info",
|
|
1351
|
-
message: this.dataFilter.text(options.message ?? defaultMessage, 4_096),
|
|
1352
|
-
},
|
|
1353
|
-
});
|
|
1354
|
-
return;
|
|
1355
|
-
}
|
|
1356
|
-
const tokensBefore = options.tokensBefore ?? operation.preTokens;
|
|
1357
|
-
this.emit({
|
|
1358
|
-
type: "timeline.item",
|
|
1359
|
-
sessionId: this.id,
|
|
1360
|
-
item: {
|
|
1361
|
-
id: operation.id,
|
|
1362
|
-
type: "compaction",
|
|
1363
|
-
status: "completed",
|
|
1364
|
-
trigger: operation.trigger,
|
|
1365
|
-
...(tokensBefore !== undefined ? { preTokens: tokensBefore } : {}),
|
|
1366
|
-
},
|
|
1367
|
-
});
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
private retireCompaction(message: string): void {
|
|
1371
|
-
const operation = this.activeCompaction;
|
|
1372
|
-
if (!operation) return;
|
|
1373
|
-
this.activeCompaction = null;
|
|
1374
|
-
this.emit({
|
|
1375
|
-
type: "timeline.item",
|
|
1376
|
-
sessionId: this.id,
|
|
1377
|
-
item: {
|
|
1378
|
-
id: operation.id,
|
|
1379
|
-
type: "compaction",
|
|
1380
|
-
status: "completed",
|
|
1381
|
-
trigger: operation.trigger,
|
|
1382
|
-
},
|
|
1383
|
-
});
|
|
1384
|
-
this.emit({
|
|
1385
|
-
type: "timeline.item",
|
|
1386
|
-
sessionId: this.id,
|
|
1387
|
-
item: { id: `${operation.id}:error`, type: "error", message },
|
|
1388
|
-
});
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
783
|
private async replayHistory(preferPersistedTranscript = false): Promise<void> {
|
|
1392
784
|
if (!this.runtime.canReplayHistory) {
|
|
1393
785
|
throw new OmpPublicError("OMP session history cannot be replayed safely");
|
|
@@ -1780,8 +1172,8 @@ export class OmpProviderSession {
|
|
|
1780
1172
|
turn.starting = false;
|
|
1781
1173
|
turn.acknowledged = true;
|
|
1782
1174
|
this.startTurn(turn, false);
|
|
1783
|
-
this.
|
|
1784
|
-
this.
|
|
1175
|
+
this.compaction.start(turn, "manual");
|
|
1176
|
+
this.usage.poll(turn);
|
|
1785
1177
|
const bufferedEvents = turn.bufferedEvents.splice(0);
|
|
1786
1178
|
turn.replayingBufferedEvents = true;
|
|
1787
1179
|
for (const event of bufferedEvents) this.handleTurnEvent(turn, event);
|
|
@@ -1872,7 +1264,7 @@ export class OmpProviderSession {
|
|
|
1872
1264
|
else {
|
|
1873
1265
|
turn.steerReady.resolve();
|
|
1874
1266
|
turn.terminal = true;
|
|
1875
|
-
this.
|
|
1267
|
+
this.compaction.finish("failed", { message: "OMP prompt failed" });
|
|
1876
1268
|
this.resolveTurnPermissions(turn.turnId);
|
|
1877
1269
|
this.projector.finishTurn(turn.turnId);
|
|
1878
1270
|
if (this.activeTurn === turn) this.activeTurn = null;
|
|
@@ -2045,7 +1437,7 @@ export class OmpProviderSession {
|
|
|
2045
1437
|
const result = await compaction;
|
|
2046
1438
|
turn.manualCompactionPending = false;
|
|
2047
1439
|
if (this.closed || this.runtimeDead || turn.generation !== this.generation) return;
|
|
2048
|
-
this.
|
|
1440
|
+
this.compaction.finish("completed", { tokensBefore: result.tokensBefore });
|
|
2049
1441
|
await this.finishTurn(turn, turn.interrupted ? "canceled" : "completed");
|
|
2050
1442
|
} catch (error) {
|
|
2051
1443
|
if (this.closed || this.runtimeDead || turn.generation !== this.generation) return;
|
|
@@ -2053,7 +1445,7 @@ export class OmpProviderSession {
|
|
|
2053
1445
|
const raw = providerError(error, "OMP compaction failed");
|
|
2054
1446
|
const failure = { message: this.dataFilter.text(raw.message, 4_096) };
|
|
2055
1447
|
const state = turn.interrupted ? "canceled" : "failed";
|
|
2056
|
-
this.
|
|
1448
|
+
this.compaction.finish(state, { message: failure.message });
|
|
2057
1449
|
await this.finishTurn(turn, state, state === "failed" ? failure : undefined, true, true);
|
|
2058
1450
|
}
|
|
2059
1451
|
}
|
|
@@ -2630,11 +2022,11 @@ export class OmpProviderSession {
|
|
|
2630
2022
|
if (turn.started) await this.finishTurn(turn, "canceled", undefined, true, true);
|
|
2631
2023
|
else {
|
|
2632
2024
|
this.settleUnstartedTurn(turn);
|
|
2633
|
-
this.
|
|
2025
|
+
this.compaction.finish("canceled");
|
|
2634
2026
|
}
|
|
2635
2027
|
}
|
|
2636
2028
|
this.imageMaterializer.clear();
|
|
2637
|
-
this.
|
|
2029
|
+
this.compaction.finish("canceled");
|
|
2638
2030
|
this.resolveAllPermissions(true);
|
|
2639
2031
|
this.closed = true;
|
|
2640
2032
|
this.configRefreshAttempts = 0;
|
|
@@ -2794,7 +2186,7 @@ export class OmpProviderSession {
|
|
|
2794
2186
|
}
|
|
2795
2187
|
if (this.subsessions) await recovered.setSubagentSubscription("events");
|
|
2796
2188
|
this.generation += 1;
|
|
2797
|
-
this.
|
|
2189
|
+
this.usage.clearLatest();
|
|
2798
2190
|
this.runtimeDead = null;
|
|
2799
2191
|
this.runtimeDisposal = null;
|
|
2800
2192
|
unsubscribeBootstrap();
|
|
@@ -3141,43 +2533,13 @@ export class OmpProviderSession {
|
|
|
3141
2533
|
if (event.type === "auto_compaction_start") {
|
|
3142
2534
|
turn.nativeActivity = true;
|
|
3143
2535
|
this.cancelLocalOnlyCompletion(turn);
|
|
3144
|
-
this.
|
|
2536
|
+
this.compaction.start(turn, "auto", event.action);
|
|
3145
2537
|
return;
|
|
3146
2538
|
}
|
|
3147
2539
|
if (event.type === "auto_compaction_end") {
|
|
3148
|
-
if (this.
|
|
3149
|
-
this.
|
|
3150
|
-
return;
|
|
3151
|
-
}
|
|
3152
|
-
const operation = this.activeCompaction;
|
|
3153
|
-
if (
|
|
3154
|
-
operation?.trigger !== "auto" ||
|
|
3155
|
-
operation.turnId !== turn.turnId ||
|
|
3156
|
-
operation.generation !== turn.generation
|
|
3157
|
-
) {
|
|
3158
|
-
return;
|
|
3159
|
-
}
|
|
3160
|
-
if (event.action !== undefined && operation.action !== event.action) {
|
|
3161
|
-
this.retireCompaction("OMP emitted overlapping compactions");
|
|
3162
|
-
this.discardedCompactionEnds = 1;
|
|
3163
|
-
return;
|
|
3164
|
-
}
|
|
3165
|
-
if (event.willRetry) {
|
|
3166
|
-
operation.retrying = true;
|
|
3167
|
-
return;
|
|
2540
|
+
if (this.compaction.handleAutoEnd(turn, event)) {
|
|
2541
|
+
this.usage.schedulePoll(turn, USAGE_REFRESH_MS);
|
|
3168
2542
|
}
|
|
3169
|
-
const state = event.aborted
|
|
3170
|
-
? "canceled"
|
|
3171
|
-
: event.errorMessage
|
|
3172
|
-
? "failed"
|
|
3173
|
-
: event.skipped
|
|
3174
|
-
? "skipped"
|
|
3175
|
-
: "completed";
|
|
3176
|
-
this.finishCompaction(state, {
|
|
3177
|
-
tokensBefore: event.result?.tokensBefore ?? event.result?.preTokens,
|
|
3178
|
-
message: event.errorMessage,
|
|
3179
|
-
});
|
|
3180
|
-
this.scheduleUsagePoll(turn, USAGE_REFRESH_MS);
|
|
3181
2543
|
return;
|
|
3182
2544
|
}
|
|
3183
2545
|
if (event.type === "agent_end" && turn.manualCompactionPending) return;
|
|
@@ -4324,7 +3686,7 @@ export class OmpProviderSession {
|
|
|
4324
3686
|
await Promise.allSettled(turn.userLookups);
|
|
4325
3687
|
this.refreshAmbiguousTerminal(turn);
|
|
4326
3688
|
if (turn.terminal || this.activeTurn !== turn || turn.deferredAgentEnd !== candidate) return;
|
|
4327
|
-
const state = await this.boundedTerminalState(turn, FINAL_USAGE_WAIT_MS);
|
|
3689
|
+
const state = await this.usage.boundedTerminalState(turn, FINAL_USAGE_WAIT_MS);
|
|
4328
3690
|
if (turn.terminal || this.activeTurn !== turn || turn.deferredAgentEnd !== candidate) return;
|
|
4329
3691
|
if (!state) {
|
|
4330
3692
|
turn.operationalTerminalStage = "unresolved";
|
|
@@ -4354,10 +3716,7 @@ export class OmpProviderSession {
|
|
|
4354
3716
|
) {
|
|
4355
3717
|
return;
|
|
4356
3718
|
}
|
|
4357
|
-
|
|
4358
|
-
if (this.usageSample?.turn === turn && this.usageSample.sequence < turn.usageSampleFloor) {
|
|
4359
|
-
this.usageSample = null;
|
|
4360
|
-
}
|
|
3719
|
+
this.usage.invalidateBeforeNextSample(turn);
|
|
4361
3720
|
this.publishPendingUsers(turn);
|
|
4362
3721
|
await this.finishTurn(turn, "completed", undefined, false, false, true);
|
|
4363
3722
|
}
|
|
@@ -4386,7 +3745,7 @@ export class OmpProviderSession {
|
|
|
4386
3745
|
this.beginTerminalization(turn, replacement);
|
|
4387
3746
|
return;
|
|
4388
3747
|
}
|
|
4389
|
-
this.
|
|
3748
|
+
this.usage.poll(turn);
|
|
4390
3749
|
}
|
|
4391
3750
|
|
|
4392
3751
|
private beginTerminalization(turn: ActiveTurn, candidate: TerminalCandidate): void {
|
|
@@ -4400,11 +3759,8 @@ export class OmpProviderSession {
|
|
|
4400
3759
|
}
|
|
4401
3760
|
turn.terminalizing = true;
|
|
4402
3761
|
this.cancelAmbiguousTerminal(turn);
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
this.usageSample = null;
|
|
4406
|
-
}
|
|
4407
|
-
this.stopUsagePoll(turn);
|
|
3762
|
+
this.usage.invalidateBeforeNextSample(turn);
|
|
3763
|
+
this.usage.stopPoll(turn);
|
|
4408
3764
|
turn.agentEndDeadlineTimer = this.scheduler.set(() => {
|
|
4409
3765
|
turn.agentEndDeadlineTimer = undefined;
|
|
4410
3766
|
if (!turn.agentEndPending || turn.terminal || this.activeTurn !== turn) return;
|
|
@@ -4496,14 +3852,14 @@ export class OmpProviderSession {
|
|
|
4496
3852
|
) {
|
|
4497
3853
|
this.resetAgentEndProbe(turn);
|
|
4498
3854
|
turn.deferredAgentEnd = candidate;
|
|
4499
|
-
this.
|
|
3855
|
+
this.usage.poll(turn);
|
|
4500
3856
|
return;
|
|
4501
3857
|
}
|
|
4502
3858
|
if (turn.interrupted) {
|
|
4503
3859
|
await this.completeAgentEnd(turn, candidate.event);
|
|
4504
3860
|
return;
|
|
4505
3861
|
}
|
|
4506
|
-
const state = await this.boundedTerminalState(turn, FINAL_USAGE_WAIT_MS);
|
|
3862
|
+
const state = await this.usage.boundedTerminalState(turn, FINAL_USAGE_WAIT_MS);
|
|
4507
3863
|
if (!turn.agentEndPending || turn.terminal || this.activeTurn !== turn) return;
|
|
4508
3864
|
if (!turn.interrupted && this.subsessions?.hasActiveChildren()) {
|
|
4509
3865
|
this.resetAgentEndProbe(turn);
|
|
@@ -4516,7 +3872,7 @@ export class OmpProviderSession {
|
|
|
4516
3872
|
) {
|
|
4517
3873
|
this.resetAgentEndProbe(turn);
|
|
4518
3874
|
turn.deferredAgentEnd = candidate;
|
|
4519
|
-
this.
|
|
3875
|
+
this.usage.poll(turn);
|
|
4520
3876
|
return;
|
|
4521
3877
|
}
|
|
4522
3878
|
if (state) {
|
|
@@ -4558,7 +3914,7 @@ export class OmpProviderSession {
|
|
|
4558
3914
|
!turn.interrupted &&
|
|
4559
3915
|
this.activeTurn === turn
|
|
4560
3916
|
) {
|
|
4561
|
-
const state = await this.boundedTerminalState(turn, FINAL_USAGE_WAIT_MS);
|
|
3917
|
+
const state = await this.usage.boundedTerminalState(turn, FINAL_USAGE_WAIT_MS);
|
|
4562
3918
|
if (
|
|
4563
3919
|
turn.terminal ||
|
|
4564
3920
|
this.activeTurn !== turn ||
|
|
@@ -4636,7 +3992,7 @@ export class OmpProviderSession {
|
|
|
4636
3992
|
turn.steerReady.resolve();
|
|
4637
3993
|
turn.starting = false;
|
|
4638
3994
|
turn.terminal = true;
|
|
4639
|
-
this.
|
|
3995
|
+
this.usage.stopPoll(turn);
|
|
4640
3996
|
this.cancelAmbiguousTerminal(turn);
|
|
4641
3997
|
this.resolveTurnPermissions(turn.turnId);
|
|
4642
3998
|
this.projector.finishTurn(turn.turnId);
|
|
@@ -4663,7 +4019,7 @@ export class OmpProviderSession {
|
|
|
4663
4019
|
turn.starting = false;
|
|
4664
4020
|
turn.steerReady.resolve();
|
|
4665
4021
|
this.emit({ type: "session.turn", sessionId: this.id, turnId: turn.turnId, state: "started" });
|
|
4666
|
-
if (pollUsage) this.
|
|
4022
|
+
if (pollUsage) this.usage.poll(turn);
|
|
4667
4023
|
}
|
|
4668
4024
|
private finishTurn(
|
|
4669
4025
|
turn: ActiveTurn,
|
|
@@ -4693,7 +4049,7 @@ export class OmpProviderSession {
|
|
|
4693
4049
|
turn.agentEndPending = false;
|
|
4694
4050
|
this.cancelLocalOnlyCompletion(turn);
|
|
4695
4051
|
this.cancelAmbiguousTerminal(turn);
|
|
4696
|
-
this.
|
|
4052
|
+
this.usage.stopPoll(turn);
|
|
4697
4053
|
if (turn.manualCompactionDeadlineTimer !== undefined) {
|
|
4698
4054
|
this.scheduler.clear(turn.manualCompactionDeadlineTimer);
|
|
4699
4055
|
turn.manualCompactionDeadlineTimer = undefined;
|
|
@@ -4712,7 +4068,7 @@ export class OmpProviderSession {
|
|
|
4712
4068
|
const terminalization = (async () => {
|
|
4713
4069
|
if (!turn.terminalOutcome?.usageSampled && !this.closed && !this.runtimeDead) {
|
|
4714
4070
|
await Promise.race([
|
|
4715
|
-
this.
|
|
4071
|
+
this.usage.boundedSnapshot(turn, FINAL_USAGE_WAIT_MS),
|
|
4716
4072
|
wake.promise.then(() => undefined),
|
|
4717
4073
|
]);
|
|
4718
4074
|
}
|
|
@@ -4739,11 +4095,11 @@ export class OmpProviderSession {
|
|
|
4739
4095
|
}
|
|
4740
4096
|
this.imageMaterializer.clear();
|
|
4741
4097
|
turn.terminal = true;
|
|
4742
|
-
|
|
4743
|
-
if (!preserveCompactions && this.
|
|
4744
|
-
if (outcome.state === "canceled") this.
|
|
4098
|
+
this.usage.clearSample(turn);
|
|
4099
|
+
if (!preserveCompactions && this.compaction.active) {
|
|
4100
|
+
if (outcome.state === "canceled") this.compaction.finish("canceled");
|
|
4745
4101
|
else {
|
|
4746
|
-
this.
|
|
4102
|
+
this.compaction.finish("failed", {
|
|
4747
4103
|
message: outcome.error?.message ?? "OMP compaction ended without a terminal result",
|
|
4748
4104
|
});
|
|
4749
4105
|
}
|
|
@@ -4780,15 +4136,14 @@ export class OmpProviderSession {
|
|
|
4780
4136
|
): void {
|
|
4781
4137
|
this.imageMaterializer.clear();
|
|
4782
4138
|
if (this.closed || this.runtimeDead) return;
|
|
4783
|
-
this.
|
|
4139
|
+
this.compaction.resetDiscardedEnds();
|
|
4784
4140
|
this.resolveAllPermissions();
|
|
4785
4141
|
this.recoveryUsesNativeConfig ||=
|
|
4786
4142
|
this.configRefreshInFlight !== null || this.configRefreshDirty || this.configMutationInFlight;
|
|
4787
4143
|
const turn = this.activeTurn;
|
|
4788
|
-
if (turn) this.
|
|
4789
|
-
this.
|
|
4790
|
-
this.
|
|
4791
|
-
this.lastUsage = null;
|
|
4144
|
+
if (turn) this.usage.stopPoll(turn);
|
|
4145
|
+
this.usage.reset();
|
|
4146
|
+
this.compaction.finish(compactionState, { message });
|
|
4792
4147
|
this.generation += 1;
|
|
4793
4148
|
this.projector.resetRuntimeGeneration("OMP runtime ended during compaction");
|
|
4794
4149
|
this.runtimeDead = message;
|