@leanandmean/coding-agent 0.74.1-scramjet.66 → 0.74.1-scramjet.68
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/README.md +2 -0
- package/dist/core/agent-session.d.ts +6 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +190 -25
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +2 -2
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/docs/compaction.md +10 -0
- package/docs/extensions.md +11 -3
- package/docs/json.md +2 -0
- package/docs/sdk.md +3 -0
- package/docs/session-format.md +3 -0
- package/package.json +4 -4
|
@@ -87,6 +87,9 @@ function rejectDeferred(deferred, error) {
|
|
|
87
87
|
function settleDeferred(deferred, error) {
|
|
88
88
|
return error ? rejectDeferred(deferred, error) : resolveDeferred(deferred, undefined);
|
|
89
89
|
}
|
|
90
|
+
function validRecordedCost(value) {
|
|
91
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
92
|
+
}
|
|
90
93
|
export class AgentSession {
|
|
91
94
|
agent;
|
|
92
95
|
sessionManager;
|
|
@@ -97,6 +100,10 @@ export class AgentSession {
|
|
|
97
100
|
_unsubscribeAgent;
|
|
98
101
|
_eventListeners = [];
|
|
99
102
|
_agentEventQueue = Promise.resolve();
|
|
103
|
+
// SCRAMJET-DIVERGENCE: execution ownership survives final-event queue latency until append succeeds (#598).
|
|
104
|
+
_toolCostExecutions = new Map();
|
|
105
|
+
_pendingToolCosts = new Set();
|
|
106
|
+
_historicalCostDefinitions = new Map();
|
|
100
107
|
_outputThroughputTracker = new OutputThroughputTracker();
|
|
101
108
|
_outputThroughputHistory;
|
|
102
109
|
_outputThroughputGeneration;
|
|
@@ -115,6 +122,7 @@ export class AgentSession {
|
|
|
115
122
|
_inputSessionEntries = new WeakMap();
|
|
116
123
|
// Compaction state
|
|
117
124
|
_compactionAbortController = undefined;
|
|
125
|
+
_manualCompactionDrain;
|
|
118
126
|
_autoCompactionAbortController = undefined;
|
|
119
127
|
_overflowRecoveryAttempted = false;
|
|
120
128
|
// Branch summarization state
|
|
@@ -299,10 +307,13 @@ export class AgentSession {
|
|
|
299
307
|
}
|
|
300
308
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
301
309
|
_handleAgentEvent = (event) => {
|
|
310
|
+
if (this._disposed)
|
|
311
|
+
return;
|
|
302
312
|
this._captureOutputThroughput(event);
|
|
313
|
+
const toolCost = this._captureToolCost(event);
|
|
303
314
|
const runSettlement = this._captureAgentRunSettlement(event);
|
|
304
315
|
const harnessAck = this._harnessPersistenceAckForEvent(event);
|
|
305
|
-
const processing = this._agentEventQueue.then(() => this._processAgentEventTracked(event, runSettlement, harnessAck), () => this._processAgentEventTracked(event, runSettlement, harnessAck));
|
|
316
|
+
const processing = this._agentEventQueue.then(() => this._processAgentEventTracked(event, runSettlement, harnessAck, toolCost), () => this._processAgentEventTracked(event, runSettlement, harnessAck, toolCost));
|
|
306
317
|
this._agentEventQueue = processing;
|
|
307
318
|
if (harnessAck)
|
|
308
319
|
harnessAck.persistenceTail = processing;
|
|
@@ -313,6 +324,42 @@ export class AgentSession {
|
|
|
313
324
|
if (event.type === "turn_end")
|
|
314
325
|
return this._agentEventQueue;
|
|
315
326
|
};
|
|
327
|
+
// SCRAMJET-DIVERGENCE: scalar reports are captured before mutable events enter async hooks (#598).
|
|
328
|
+
_captureToolCost(event) {
|
|
329
|
+
if (event.type === "tool_execution_start") {
|
|
330
|
+
const execution = {};
|
|
331
|
+
this._toolCostExecutions.set(event.toolCallId, execution);
|
|
332
|
+
this._pendingToolCosts.add(execution);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
let toolCallId;
|
|
336
|
+
let reported;
|
|
337
|
+
const final = event.type === "message_end" && event.message.role === "toolResult";
|
|
338
|
+
if (event.type === "tool_execution_update" || event.type === "tool_execution_end") {
|
|
339
|
+
toolCallId = event.toolCallId;
|
|
340
|
+
reported = event.type === "tool_execution_update" ? event.partialResult?.cost : event.result?.cost;
|
|
341
|
+
}
|
|
342
|
+
else if (event.type === "message_end" && event.message.role === "toolResult") {
|
|
343
|
+
toolCallId = event.message.toolCallId;
|
|
344
|
+
reported = event.message.cost;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
let execution = this._toolCostExecutions.get(toolCallId);
|
|
350
|
+
if (!execution && validRecordedCost(reported)) {
|
|
351
|
+
execution = {};
|
|
352
|
+
this._toolCostExecutions.set(toolCallId, execution);
|
|
353
|
+
this._pendingToolCosts.add(execution);
|
|
354
|
+
}
|
|
355
|
+
if (execution && validRecordedCost(reported))
|
|
356
|
+
execution.cost = reported;
|
|
357
|
+
if (final) {
|
|
358
|
+
this._toolCostExecutions.delete(toolCallId);
|
|
359
|
+
return execution;
|
|
360
|
+
}
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
316
363
|
// SCRAMJET-DIVERGENCE: Capture provider events synchronously and reset stale throughput lifecycles (#476).
|
|
317
364
|
_captureOutputThroughput(event) {
|
|
318
365
|
if (event.type === "message_start" && event.message.role === "assistant" && event.message.origin === "provider") {
|
|
@@ -391,16 +438,18 @@ export class AgentSession {
|
|
|
391
438
|
}
|
|
392
439
|
// SCRAMJET-DIVERGENCE: harness-tool persisted settlement (#341). Synchronous ownership keeps queued
|
|
393
440
|
// invocation events attributed after early failures; public settlement awaits execution and the final persistence tail.
|
|
394
|
-
async _processAgentEventTracked(event, run, harnessAck) {
|
|
441
|
+
async _processAgentEventTracked(event, run, harnessAck, toolCost) {
|
|
395
442
|
try {
|
|
396
|
-
await this._processAgentEvent(event, run, harnessAck !== undefined);
|
|
443
|
+
await this._processAgentEvent(event, run, harnessAck !== undefined, toolCost);
|
|
397
444
|
}
|
|
398
445
|
catch (err) {
|
|
399
446
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
447
|
+
if (this._manualCompactionDrain)
|
|
448
|
+
this._manualCompactionDrain.firstError ??= error;
|
|
400
449
|
if (harnessAck) {
|
|
401
450
|
harnessAck.firstPersistenceError ??= error;
|
|
402
451
|
}
|
|
403
|
-
else if (run) {
|
|
452
|
+
else if (run && !this._manualCompactionDrain) {
|
|
404
453
|
if (event.type === "message_end" && event.message.role === "assistant") {
|
|
405
454
|
run.assistantPersistenceError = error;
|
|
406
455
|
}
|
|
@@ -437,7 +486,19 @@ export class AgentSession {
|
|
|
437
486
|
}
|
|
438
487
|
return toolCallId === undefined ? undefined : this._harnessPersistenceAcks.get(toolCallId);
|
|
439
488
|
}
|
|
440
|
-
async _processAgentEvent(event, run, fromHarnessInvocation = false) {
|
|
489
|
+
async _processAgentEvent(event, run, fromHarnessInvocation = false, toolCost) {
|
|
490
|
+
if (this._disposed)
|
|
491
|
+
return;
|
|
492
|
+
// SCRAMJET-DIVERGENCE: queued lifecycle events must respect manual compaction ownership (#598).
|
|
493
|
+
if (this._manualCompactionDrain && event.type !== "message_end" && event.type !== "agent_end")
|
|
494
|
+
return;
|
|
495
|
+
if (this._manualCompactionDrain && event.type === "agent_end") {
|
|
496
|
+
if (this._retry)
|
|
497
|
+
this._finishCancelledRetry("cancelled_during_continuation");
|
|
498
|
+
if (run)
|
|
499
|
+
this._completeAgentRun(run, this._manualCompactionDrain.firstError);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
441
502
|
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
|
442
503
|
// This ensures the UI sees the updated queue state
|
|
443
504
|
if (event.type === "message_start" && event.message.role === "user") {
|
|
@@ -462,6 +523,18 @@ export class AgentSession {
|
|
|
462
523
|
}
|
|
463
524
|
// Emit to extensions first
|
|
464
525
|
await this._emitExtensionEvent(event);
|
|
526
|
+
if (event.type === "message_end" && event.message.role === "toolResult" && toolCost?.cost !== undefined) {
|
|
527
|
+
event.message.cost = toolCost.cost;
|
|
528
|
+
}
|
|
529
|
+
// An awaited hook may have acquired manual ownership while this event was in flight.
|
|
530
|
+
if (this._manualCompactionDrain && event.type !== "message_end") {
|
|
531
|
+
if (event.type === "agent_end" && run) {
|
|
532
|
+
if (this._retry)
|
|
533
|
+
this._finishCancelledRetry("cancelled_during_continuation");
|
|
534
|
+
this._completeAgentRun(run, this._manualCompactionDrain.firstError);
|
|
535
|
+
}
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
465
538
|
// Notify all listeners
|
|
466
539
|
this._emit(event);
|
|
467
540
|
if (this._disposed)
|
|
@@ -482,6 +555,8 @@ export class AgentSession {
|
|
|
482
555
|
? JSON.parse(JSON.stringify(event.message))
|
|
483
556
|
: event.message;
|
|
484
557
|
const messageEntryId = this.sessionManager.appendMessage(persistedMessage);
|
|
558
|
+
if (toolCost)
|
|
559
|
+
this._pendingToolCosts.delete(toolCost);
|
|
485
560
|
if (persistedMessage.role === "assistant" && !fromHarnessInvocation && run) {
|
|
486
561
|
run.persistedAssistantSnapshot = persistedMessage;
|
|
487
562
|
}
|
|
@@ -514,7 +589,8 @@ export class AgentSession {
|
|
|
514
589
|
if (event.message.role === "assistant" &&
|
|
515
590
|
event.message.stopReason !== "error" &&
|
|
516
591
|
event.message.stopReason !== "aborted" &&
|
|
517
|
-
!fromHarnessInvocation
|
|
592
|
+
!fromHarnessInvocation &&
|
|
593
|
+
!this._manualCompactionDrain) {
|
|
518
594
|
this._overflowRecoveryAttempted = false;
|
|
519
595
|
if (this._retry)
|
|
520
596
|
this._retry = { ...this._retry, attempt: 0 };
|
|
@@ -523,6 +599,12 @@ export class AgentSession {
|
|
|
523
599
|
if (event.type === "agent_end") {
|
|
524
600
|
if (this._disposed || !run)
|
|
525
601
|
return;
|
|
602
|
+
if (this._manualCompactionDrain) {
|
|
603
|
+
if (this._retry)
|
|
604
|
+
this._finishCancelledRetry("cancelled_during_continuation");
|
|
605
|
+
this._completeAgentRun(run, this._manualCompactionDrain.firstError);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
526
608
|
if (run.assistantPersistenceError) {
|
|
527
609
|
const error = run.assistantPersistenceError;
|
|
528
610
|
run.assistantPersistenceError = undefined;
|
|
@@ -741,8 +823,8 @@ export class AgentSession {
|
|
|
741
823
|
* Preserves all existing listeners.
|
|
742
824
|
*/
|
|
743
825
|
_reconnectToAgent() {
|
|
744
|
-
if (this._unsubscribeAgent)
|
|
745
|
-
return; // Already connected
|
|
826
|
+
if (this._disposed || this._unsubscribeAgent)
|
|
827
|
+
return; // Already connected or disposed
|
|
746
828
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
747
829
|
}
|
|
748
830
|
/**
|
|
@@ -755,6 +837,8 @@ export class AgentSession {
|
|
|
755
837
|
if (this._disposed)
|
|
756
838
|
return;
|
|
757
839
|
this._disposed = true;
|
|
840
|
+
this._toolCostExecutions.clear();
|
|
841
|
+
this._pendingToolCosts.clear();
|
|
758
842
|
this._resetOutputThroughput();
|
|
759
843
|
const retryDisposeError = new Error("AgentSession disposed before retry settlement completed.");
|
|
760
844
|
const unsettledRuns = [...this._unsettledAgentRuns];
|
|
@@ -966,6 +1050,10 @@ export class AgentSession {
|
|
|
966
1050
|
if (this._disposed) {
|
|
967
1051
|
throw new Error(`Cannot invoke harness tool "${name}": the session has been disposed.`);
|
|
968
1052
|
}
|
|
1053
|
+
// SCRAMJET-DIVERGENCE: reject new session work before allocating invocation ownership (#598).
|
|
1054
|
+
if (this._compactionAbortController) {
|
|
1055
|
+
throw new Error(`Cannot invoke harness tool "${name}": manual compaction is in progress. Retry after it settles.`);
|
|
1056
|
+
}
|
|
969
1057
|
const tool = this._toolRegistry.get(name);
|
|
970
1058
|
if (!tool) {
|
|
971
1059
|
throw new Error(`Cannot invoke harness tool "${name}": no tool with that name is registered.`);
|
|
@@ -1759,12 +1847,32 @@ export class AgentSession {
|
|
|
1759
1847
|
* @param customInstructions Optional instructions for the compaction summary
|
|
1760
1848
|
*/
|
|
1761
1849
|
async compact(customInstructions) {
|
|
1762
|
-
|
|
1763
|
-
this.
|
|
1764
|
-
|
|
1765
|
-
this.
|
|
1766
|
-
|
|
1850
|
+
// SCRAMJET-DIVERGENCE: acquire one owner before awaits and drain terminal persistence (#598).
|
|
1851
|
+
if (this._disposed)
|
|
1852
|
+
throw new Error("Cannot compact a disposed session.");
|
|
1853
|
+
if (this.isCompacting)
|
|
1854
|
+
throw new Error("Compaction or branch summarization is already in progress. Retry after it settles.");
|
|
1855
|
+
if (this.agent.hasActiveTransientHarnessTools) {
|
|
1856
|
+
throw new Error("A harness tool is still executing outside an Agent run. Retry compaction after its persisted settlement.");
|
|
1857
|
+
}
|
|
1858
|
+
const controller = new AbortController();
|
|
1859
|
+
this._compactionAbortController = controller;
|
|
1860
|
+
const drain = {};
|
|
1861
|
+
this._manualCompactionDrain = drain;
|
|
1862
|
+
const runs = [...this._unsettledAgentRuns];
|
|
1767
1863
|
try {
|
|
1864
|
+
this._resetOutputThroughput();
|
|
1865
|
+
this._disconnectFromAgent();
|
|
1866
|
+
this._unsubscribeAgent = this.agent.subscribe((event) => this._handleAgentEvent(event));
|
|
1867
|
+
this._emit({ type: "compaction_start", reason: "manual" });
|
|
1868
|
+
await this.abort();
|
|
1869
|
+
await this._agentEventQueue;
|
|
1870
|
+
if (drain.firstError)
|
|
1871
|
+
throw drain.firstError;
|
|
1872
|
+
if (this._disposed)
|
|
1873
|
+
throw new Error("Session disposed during compaction.");
|
|
1874
|
+
if (controller.signal.aborted)
|
|
1875
|
+
throw new Error("Compaction cancelled");
|
|
1768
1876
|
if (!this.model) {
|
|
1769
1877
|
throw new Error(formatNoModelSelectedMessage());
|
|
1770
1878
|
}
|
|
@@ -1788,7 +1896,7 @@ export class AgentSession {
|
|
|
1788
1896
|
preparation,
|
|
1789
1897
|
branchEntries: pathEntries,
|
|
1790
1898
|
customInstructions,
|
|
1791
|
-
signal:
|
|
1899
|
+
signal: controller.signal,
|
|
1792
1900
|
}));
|
|
1793
1901
|
if (result?.cancel) {
|
|
1794
1902
|
throw new Error("Compaction cancelled");
|
|
@@ -1811,13 +1919,15 @@ export class AgentSession {
|
|
|
1811
1919
|
}
|
|
1812
1920
|
else {
|
|
1813
1921
|
// Generate compaction result
|
|
1814
|
-
const result = await compact(preparation, this.model, apiKey, headers, customInstructions,
|
|
1922
|
+
const result = await compact(preparation, this.model, apiKey, headers, customInstructions, controller.signal, this.thinkingLevel);
|
|
1815
1923
|
summary = result.summary;
|
|
1816
1924
|
firstKeptEntryId = result.firstKeptEntryId;
|
|
1817
1925
|
tokensBefore = result.tokensBefore;
|
|
1818
1926
|
details = result.details;
|
|
1819
1927
|
}
|
|
1820
|
-
if (this.
|
|
1928
|
+
if (this._disposed)
|
|
1929
|
+
throw new Error("Session disposed during compaction.");
|
|
1930
|
+
if (controller.signal.aborted) {
|
|
1821
1931
|
throw new Error("Compaction cancelled");
|
|
1822
1932
|
}
|
|
1823
1933
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
|
|
@@ -1849,8 +1959,13 @@ export class AgentSession {
|
|
|
1849
1959
|
return compactionResult;
|
|
1850
1960
|
}
|
|
1851
1961
|
catch (error) {
|
|
1852
|
-
const
|
|
1853
|
-
const
|
|
1962
|
+
const failure = drain.firstError ?? error;
|
|
1963
|
+
const settlementError = failure instanceof Error ? failure : new Error(String(failure));
|
|
1964
|
+
this._releaseRetryForFailedRun(settlementError);
|
|
1965
|
+
for (const run of runs)
|
|
1966
|
+
this._completeAgentRun(run, settlementError);
|
|
1967
|
+
const message = settlementError.message;
|
|
1968
|
+
const aborted = message === "Compaction cancelled" || settlementError.name === "AbortError";
|
|
1854
1969
|
this._emit({
|
|
1855
1970
|
type: "compaction_end",
|
|
1856
1971
|
reason: "manual",
|
|
@@ -1859,9 +1974,11 @@ export class AgentSession {
|
|
|
1859
1974
|
willRetry: false,
|
|
1860
1975
|
errorMessage: aborted ? undefined : `Compaction failed: ${message}`,
|
|
1861
1976
|
});
|
|
1862
|
-
throw
|
|
1977
|
+
throw failure;
|
|
1863
1978
|
}
|
|
1864
1979
|
finally {
|
|
1980
|
+
this._disconnectFromAgent();
|
|
1981
|
+
this._manualCompactionDrain = undefined;
|
|
1865
1982
|
this._compactionAbortController = undefined;
|
|
1866
1983
|
this._reconnectToAgent();
|
|
1867
1984
|
}
|
|
@@ -1891,6 +2008,8 @@ export class AgentSession {
|
|
|
1891
2008
|
* @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
|
|
1892
2009
|
*/
|
|
1893
2010
|
async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
|
|
2011
|
+
if (this._manualCompactionDrain)
|
|
2012
|
+
return;
|
|
1894
2013
|
const settings = this.settingsManager.getCompactionSettings();
|
|
1895
2014
|
if (!settings.enabled)
|
|
1896
2015
|
return;
|
|
@@ -1969,9 +2088,10 @@ export class AgentSession {
|
|
|
1969
2088
|
*/
|
|
1970
2089
|
async _runAutoCompaction(reason, willRetry) {
|
|
1971
2090
|
const settings = this.settingsManager.getCompactionSettings();
|
|
1972
|
-
|
|
2091
|
+
// SCRAMJET-DIVERGENCE: reentrant subscribers must observe compaction ownership (#598).
|
|
1973
2092
|
this._autoCompactionAbortController = new AbortController();
|
|
1974
2093
|
try {
|
|
2094
|
+
this._emit({ type: "compaction_start", reason });
|
|
1975
2095
|
if (!this.model) {
|
|
1976
2096
|
this._emit({
|
|
1977
2097
|
type: "compaction_end",
|
|
@@ -2316,7 +2436,13 @@ export class AgentSession {
|
|
|
2316
2436
|
definition,
|
|
2317
2437
|
sourceInfo: createSyntheticSourceInfo(`<sdk:${definition.name}>`, { source: "sdk" }),
|
|
2318
2438
|
})),
|
|
2319
|
-
]
|
|
2439
|
+
];
|
|
2440
|
+
// SCRAMJET-DIVERGENCE: historical interpretation follows definition collision winners, not execution allowlists (#598).
|
|
2441
|
+
this._historicalCostDefinitions = new Map(this._baseToolDefinitions);
|
|
2442
|
+
for (const tool of allCustomTools) {
|
|
2443
|
+
this._historicalCostDefinitions.set(tool.definition.name, tool.definition);
|
|
2444
|
+
}
|
|
2445
|
+
const allowedCustomTools = allCustomTools.filter((tool) => isAllowedTool(tool.definition.name));
|
|
2320
2446
|
const definitionRegistry = new Map(Array.from(this._baseToolDefinitions.entries())
|
|
2321
2447
|
.filter(([name]) => isAllowedTool(name))
|
|
2322
2448
|
.map(([name, definition]) => [
|
|
@@ -2326,7 +2452,7 @@ export class AgentSession {
|
|
|
2326
2452
|
sourceInfo: createSyntheticSourceInfo(`<builtin:${name}>`, { source: "builtin" }),
|
|
2327
2453
|
},
|
|
2328
2454
|
]));
|
|
2329
|
-
for (const tool of
|
|
2455
|
+
for (const tool of allowedCustomTools) {
|
|
2330
2456
|
definitionRegistry.set(tool.definition.name, {
|
|
2331
2457
|
definition: tool.definition,
|
|
2332
2458
|
sourceInfo: tool.sourceInfo,
|
|
@@ -2346,7 +2472,7 @@ export class AgentSession {
|
|
|
2346
2472
|
})
|
|
2347
2473
|
.filter((entry) => entry !== undefined));
|
|
2348
2474
|
const runner = this._extensionRunner;
|
|
2349
|
-
const wrappedExtensionTools = wrapRegisteredTools(
|
|
2475
|
+
const wrappedExtensionTools = wrapRegisteredTools(allowedCustomTools, runner);
|
|
2350
2476
|
const wrappedBuiltInTools = wrapRegisteredTools(Array.from(this._baseToolDefinitions.values())
|
|
2351
2477
|
.filter((definition) => isAllowedTool(definition.name))
|
|
2352
2478
|
.map((definition) => ({
|
|
@@ -2498,7 +2624,7 @@ export class AgentSession {
|
|
|
2498
2624
|
this._appendAutoRetryRecord(record);
|
|
2499
2625
|
}
|
|
2500
2626
|
catch (error) {
|
|
2501
|
-
if (retry) {
|
|
2627
|
+
if (retry && !this._manualCompactionDrain) {
|
|
2502
2628
|
this._emit({
|
|
2503
2629
|
type: "auto_retry_end",
|
|
2504
2630
|
success: false,
|
|
@@ -2508,7 +2634,7 @@ export class AgentSession {
|
|
|
2508
2634
|
}
|
|
2509
2635
|
throw this._reportRetryFailure(error);
|
|
2510
2636
|
}
|
|
2511
|
-
if (retry && end)
|
|
2637
|
+
if (retry && end && !this._manualCompactionDrain)
|
|
2512
2638
|
this._emit(end);
|
|
2513
2639
|
}
|
|
2514
2640
|
_finishRetryWithoutSuccess(record, message) {
|
|
@@ -2645,6 +2771,8 @@ export class AgentSession {
|
|
|
2645
2771
|
if (controller.signal.aborted || this._disposed) {
|
|
2646
2772
|
return this._finishCancelledRetry("cancelled_during_backoff", backoff);
|
|
2647
2773
|
}
|
|
2774
|
+
if (this._manualCompactionDrain)
|
|
2775
|
+
return this._finishCancelledRetry("cancelled_during_backoff", backoff);
|
|
2648
2776
|
// Hand the chain to the continuation before `continue()`; the run that eventually emits
|
|
2649
2777
|
// `agent_start` adopts it (see `_captureAgentRunSettlement`), however long Agent defers that start.
|
|
2650
2778
|
const continuing = { phase: "continuing", attempt, chain: run.chain };
|
|
@@ -3051,6 +3179,43 @@ export class AgentSession {
|
|
|
3051
3179
|
}
|
|
3052
3180
|
return result;
|
|
3053
3181
|
}
|
|
3182
|
+
// SCRAMJET-DIVERGENCE: whole-journal parent/tool costs plus reports awaiting successful append (#598).
|
|
3183
|
+
getRecordedSessionCost() {
|
|
3184
|
+
let total = 0;
|
|
3185
|
+
for (const entry of this.sessionManager.getEntries()) {
|
|
3186
|
+
if (entry.type !== "message")
|
|
3187
|
+
continue;
|
|
3188
|
+
const message = entry.message;
|
|
3189
|
+
if (message.role === "assistant") {
|
|
3190
|
+
if (validRecordedCost(message.usage?.cost?.total))
|
|
3191
|
+
total += message.usage.cost.total;
|
|
3192
|
+
}
|
|
3193
|
+
else if (message.role === "toolResult") {
|
|
3194
|
+
if (validRecordedCost(message.cost)) {
|
|
3195
|
+
total += message.cost;
|
|
3196
|
+
continue;
|
|
3197
|
+
}
|
|
3198
|
+
const definition = this._historicalCostDefinitions.get(message.toolName);
|
|
3199
|
+
if (!definition?.getHistoricalCost)
|
|
3200
|
+
continue;
|
|
3201
|
+
try {
|
|
3202
|
+
const cost = definition.getHistoricalCost(message.details);
|
|
3203
|
+
if (validRecordedCost(cost))
|
|
3204
|
+
total += cost;
|
|
3205
|
+
}
|
|
3206
|
+
catch (error) {
|
|
3207
|
+
this._extensionRunner.emitError({
|
|
3208
|
+
extensionPath: `tool:${message.toolName}`,
|
|
3209
|
+
event: "historical_cost",
|
|
3210
|
+
error: `Failed to interpret historical tool cost: ${error instanceof Error ? error.message : String(error)}`,
|
|
3211
|
+
});
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
for (const execution of this._pendingToolCosts)
|
|
3216
|
+
total += execution.cost ?? 0;
|
|
3217
|
+
return total;
|
|
3218
|
+
}
|
|
3054
3219
|
/**
|
|
3055
3220
|
* Get session statistics.
|
|
3056
3221
|
*/
|