@trigger.dev/sdk 4.6.1 → 4.6.3
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/commonjs/v3/ai.d.ts +32 -10
- package/dist/commonjs/v3/ai.js +122 -36
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/auth.js +8 -2
- package/dist/commonjs/v3/auth.js.map +1 -1
- package/dist/commonjs/v3/compactionResponse.d.ts +9 -0
- package/dist/commonjs/v3/compactionResponse.js +32 -0
- package/dist/commonjs/v3/compactionResponse.js.map +1 -0
- package/dist/commonjs/v3/runs.d.ts +5 -0
- package/dist/commonjs/v3/sessionTracing.d.ts +7 -0
- package/dist/commonjs/v3/sessionTracing.js +44 -0
- package/dist/commonjs/v3/sessionTracing.js.map +1 -0
- package/dist/commonjs/v3/sessions.js +31 -34
- package/dist/commonjs/v3/sessions.js.map +1 -1
- package/dist/commonjs/v3/shared.js +8 -5
- package/dist/commonjs/v3/shared.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai.d.ts +32 -10
- package/dist/esm/v3/ai.js +122 -36
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/auth.js +8 -2
- package/dist/esm/v3/auth.js.map +1 -1
- package/dist/esm/v3/compactionResponse.d.ts +9 -0
- package/dist/esm/v3/compactionResponse.js +29 -0
- package/dist/esm/v3/compactionResponse.js.map +1 -0
- package/dist/esm/v3/runs.d.ts +5 -0
- package/dist/esm/v3/sessionTracing.d.ts +7 -0
- package/dist/esm/v3/sessionTracing.js +40 -0
- package/dist/esm/v3/sessionTracing.js.map +1 -0
- package/dist/esm/v3/sessions.js +31 -34
- package/dist/esm/v3/sessions.js.map +1 -1
- package/dist/esm/v3/shared.js +9 -6
- package/dist/esm/v3/shared.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/docs/ai-chat/lifecycle-hooks.mdx +4 -2
- package/docs/ai-chat/reference.mdx +4 -2
- package/package.json +2 -2
package/dist/esm/v3/ai.js
CHANGED
|
@@ -2,8 +2,10 @@ import { accessoryAttributes, apiClientManager, controlSubtype, generateJWT, get
|
|
|
2
2
|
// Runtime VALUES go through the ESM/CJS shim so the CJS build can `require`
|
|
3
3
|
// ESM-only `ai@7` (see ../imports/ai-runtime.ts).
|
|
4
4
|
import { trace } from "@opentelemetry/api";
|
|
5
|
+
import { traceSessionIdle } from "./sessionTracing.js";
|
|
5
6
|
import { tool as aiTool, convertToModelMessages, dynamicTool, generateId as generateMessageId, getToolName, isToolUIPart, jsonSchema, readUIMessageStream, streamText as aiStreamText, zodSchema, } from "../imports/ai-runtime.js";
|
|
6
7
|
import { createTranscriptShadow, defaultStorage, diffTranscript, fingerprintMessage, parseTranscriptRuntimeState, restoreModelLane, } from "./transcriptStorage.js";
|
|
8
|
+
import { responseAfterCompaction } from "./compactionResponse.js";
|
|
7
9
|
let transcriptStorageOverride;
|
|
8
10
|
/**
|
|
9
11
|
* Test-only override for the storage `chat.agent` persists through, so a
|
|
@@ -1087,7 +1089,7 @@ async function waitOnChatRoute(route, options) {
|
|
|
1087
1089
|
return tracer.startActiveSpan(options.spanName ?? `chat.${route}.wait()`, async (span) => {
|
|
1088
1090
|
const idleMs = (options.idleTimeoutInSeconds ?? 0) * 1000;
|
|
1089
1091
|
if (idleMs > 0) {
|
|
1090
|
-
const warm = await router.next(route, { timeoutMs: idleMs });
|
|
1092
|
+
const warm = await traceSessionIdle(session.id, idleMs / 1000, () => router.next(route, { timeoutMs: idleMs }));
|
|
1091
1093
|
if (warm) {
|
|
1092
1094
|
span.setAttribute("wait.resolved", "idle");
|
|
1093
1095
|
return { ok: true, output: warm.data, record: warm };
|
|
@@ -1141,10 +1143,6 @@ async function waitOnChatRoute(route, options) {
|
|
|
1141
1143
|
session: session.id,
|
|
1142
1144
|
io: "in",
|
|
1143
1145
|
route,
|
|
1144
|
-
...accessoryAttributes({
|
|
1145
|
-
items: [{ text: `${session.id}.in:${route}`, variant: "normal" }],
|
|
1146
|
-
style: "codepath",
|
|
1147
|
-
}),
|
|
1148
1146
|
},
|
|
1149
1147
|
});
|
|
1150
1148
|
}
|
|
@@ -1820,6 +1818,14 @@ const chatHandoverPartialKey = locals.create("chat.handoverPartial");
|
|
|
1820
1818
|
* @internal
|
|
1821
1819
|
*/
|
|
1822
1820
|
const chatHandoverMessageIdKey = locals.create("chat.handoverMessageId");
|
|
1821
|
+
/**
|
|
1822
|
+
* The model messages a turn-0 head-start splice contributed to the model lane,
|
|
1823
|
+
* keyed by the UI message id it was synthesized under. When the agent's response
|
|
1824
|
+
* completes that message under the same id, this is the run to replace: the UI
|
|
1825
|
+
* form alone converts to something else (its pending tool calls drop out, the
|
|
1826
|
+
* approval round was never on it), so it cannot locate the run itself.
|
|
1827
|
+
*/
|
|
1828
|
+
const chatHandoverSplicedRunKey = locals.create("chat.handoverSplicedRun");
|
|
1823
1829
|
/**
|
|
1824
1830
|
* Run-scoped slot indicating that the customer's step-1 head-start
|
|
1825
1831
|
* response is the FINAL turn response. When true, turn 0 runs through
|
|
@@ -1903,16 +1909,19 @@ function synthesizeHandoverUIMessage(partial, messageId) {
|
|
|
1903
1909
|
*/
|
|
1904
1910
|
function spliceHandoverPartial(modelMessages, uiMessages, signal) {
|
|
1905
1911
|
if (!signal.partialAssistantMessage || signal.partialAssistantMessage.length === 0) {
|
|
1906
|
-
return;
|
|
1912
|
+
return undefined;
|
|
1907
1913
|
}
|
|
1908
1914
|
// Skip if the hydrated chain already persisted the partial under this id.
|
|
1909
1915
|
const alreadyInChain = signal.messageId !== undefined && uiMessages.some((m) => m.id === signal.messageId);
|
|
1910
1916
|
if (alreadyInChain)
|
|
1911
|
-
return;
|
|
1912
|
-
|
|
1917
|
+
return undefined;
|
|
1918
|
+
const run = [...signal.partialAssistantMessage];
|
|
1919
|
+
modelMessages.push(...run);
|
|
1913
1920
|
const partialUI = synthesizeHandoverUIMessage(signal.partialAssistantMessage, signal.messageId);
|
|
1914
|
-
if (partialUI)
|
|
1915
|
-
|
|
1921
|
+
if (!partialUI)
|
|
1922
|
+
return undefined;
|
|
1923
|
+
uiMessages.push(partialUI);
|
|
1924
|
+
return { id: partialUI.id, run };
|
|
1916
1925
|
}
|
|
1917
1926
|
/**
|
|
1918
1927
|
* Per-turn background context queue. Messages added via `chat.backgroundWork.inject()`
|
|
@@ -2782,6 +2791,7 @@ async function chatCompact(messages, steps, options) {
|
|
|
2782
2791
|
locals.set(chatCompactionStateKey, {
|
|
2783
2792
|
summary,
|
|
2784
2793
|
baseResponseMessageCount: currentStep.response.messages.length,
|
|
2794
|
+
baseResponseStepCount: steps.length,
|
|
2785
2795
|
});
|
|
2786
2796
|
// Set model-only override — UI messages stay intact for persistence.
|
|
2787
2797
|
// The summary becomes the model message history for the next turn,
|
|
@@ -3652,8 +3662,8 @@ function isActionTurn(value) {
|
|
|
3652
3662
|
* tail does not match the old message's conversion, nothing is changed and
|
|
3653
3663
|
* `false` is returned so the caller can fall back to a full reconversion.
|
|
3654
3664
|
*/
|
|
3655
|
-
async function replaceModelRun(lane, oldUi, newUi, tailAfter) {
|
|
3656
|
-
const oldRun = await toModelMessages([stripProviderMetadata(oldUi)]);
|
|
3665
|
+
async function replaceModelRun(lane, oldUi, newUi, tailAfter, knownOldRun) {
|
|
3666
|
+
const oldRun = knownOldRun ?? (await toModelMessages([stripProviderMetadata(oldUi)]));
|
|
3657
3667
|
const newRun = await toModelMessages([stripProviderMetadata(newUi)]);
|
|
3658
3668
|
// A message that converts to nothing (a pending tool call with no output yet,
|
|
3659
3669
|
// which `ignoreIncompleteToolCalls` drops) locates no run in the lane. Matching
|
|
@@ -4785,7 +4795,7 @@ function chatAgent(options) {
|
|
|
4785
4795
|
const preloadResult = await messagesInput.waitWithIdleTimeout({
|
|
4786
4796
|
idleTimeoutInSeconds: effectivePreloadIdleTimeout,
|
|
4787
4797
|
timeout: effectivePreloadTimeout,
|
|
4788
|
-
spanName: "
|
|
4798
|
+
spanName: "first message",
|
|
4789
4799
|
skipSuspend: exitAfterPreloadIdle,
|
|
4790
4800
|
onSuspend: onChatSuspend
|
|
4791
4801
|
? async () => {
|
|
@@ -4937,7 +4947,7 @@ function chatAgent(options) {
|
|
|
4937
4947
|
const continuationResult = await messagesInput.waitWithIdleTimeout({
|
|
4938
4948
|
idleTimeoutInSeconds: effectiveIdleTimeout,
|
|
4939
4949
|
timeout: effectiveTurnTimeout,
|
|
4940
|
-
spanName: "
|
|
4950
|
+
spanName: "first message (continuation)",
|
|
4941
4951
|
onSuspend: onChatSuspend
|
|
4942
4952
|
? async () => {
|
|
4943
4953
|
await tracer.startActiveSpan("onChatSuspend()", async () => {
|
|
@@ -5466,10 +5476,12 @@ function chatAgent(options) {
|
|
|
5466
5476
|
// `UIMessageStreamError: No tool invocation found`.
|
|
5467
5477
|
const pendingHandoverPartial = locals.get(chatHandoverPartialKey);
|
|
5468
5478
|
if (pendingHandoverPartial && pendingHandoverPartial.length > 0) {
|
|
5469
|
-
spliceHandoverPartial(accumulatedMessages, accumulatedUIMessages, {
|
|
5479
|
+
const spliced = spliceHandoverPartial(accumulatedMessages, accumulatedUIMessages, {
|
|
5470
5480
|
partialAssistantMessage: pendingHandoverPartial,
|
|
5471
5481
|
messageId: locals.get(chatHandoverMessageIdKey),
|
|
5472
5482
|
});
|
|
5483
|
+
if (spliced)
|
|
5484
|
+
locals.set(chatHandoverSplicedRunKey, spliced);
|
|
5473
5485
|
locals.set(chatHandoverPartialKey, []); // consume once
|
|
5474
5486
|
splicedHandoverPartial = true;
|
|
5475
5487
|
}
|
|
@@ -5814,6 +5826,7 @@ function chatAgent(options) {
|
|
|
5814
5826
|
// never reports final usage), which would block the turn loop
|
|
5815
5827
|
// from ever firing onTurnComplete / writeTurnComplete.
|
|
5816
5828
|
let turnUsage;
|
|
5829
|
+
let lastStepUsage;
|
|
5817
5830
|
if (runResult != null &&
|
|
5818
5831
|
typeof runResult.totalUsage?.then === "function") {
|
|
5819
5832
|
try {
|
|
@@ -5826,6 +5839,18 @@ function chatAgent(options) {
|
|
|
5826
5839
|
/* non-fatal — usage capture failed */
|
|
5827
5840
|
}
|
|
5828
5841
|
}
|
|
5842
|
+
const lastStepUsagePromise = runResult != null ? runResult.usage : undefined;
|
|
5843
|
+
if (typeof lastStepUsagePromise?.then === "function") {
|
|
5844
|
+
try {
|
|
5845
|
+
lastStepUsage = (await Promise.race([
|
|
5846
|
+
lastStepUsagePromise,
|
|
5847
|
+
new Promise((r) => setTimeout(() => r(undefined), 2_000)),
|
|
5848
|
+
]));
|
|
5849
|
+
}
|
|
5850
|
+
catch {
|
|
5851
|
+
/* non-fatal — usage capture failed */
|
|
5852
|
+
}
|
|
5853
|
+
}
|
|
5829
5854
|
if (turnUsage) {
|
|
5830
5855
|
cumulativeUsage = addUsage(cumulativeUsage, turnUsage);
|
|
5831
5856
|
previousTurnUsage = turnUsage;
|
|
@@ -5876,6 +5901,13 @@ function chatAgent(options) {
|
|
|
5876
5901
|
// Check if compaction set a model-only override (preserves UI messages).
|
|
5877
5902
|
// Apply compactUIMessages/compactModelMessages callbacks if configured.
|
|
5878
5903
|
const modelOnlyOverride = locals.get(chatOverrideModelMessagesKey);
|
|
5904
|
+
const responseCompaction = modelOnlyOverride
|
|
5905
|
+
? locals.get(chatCompactionStateKey)
|
|
5906
|
+
: undefined;
|
|
5907
|
+
// Capture the original assistant before compactUIMessages can remove it.
|
|
5908
|
+
const originalResponse = responseCompaction && capturedResponseMessage
|
|
5909
|
+
? accumulatedUIMessages.find((m) => m.id === capturedResponseMessage?.id)
|
|
5910
|
+
: undefined;
|
|
5879
5911
|
if (modelOnlyOverride) {
|
|
5880
5912
|
const compactionSummary = locals.get(chatCompactionStateKey)?.summary ?? "";
|
|
5881
5913
|
const taskCompactionConfig = locals.get(chatAgentCompactionKey);
|
|
@@ -5967,12 +5999,39 @@ function chatAgent(options) {
|
|
|
5967
5999
|
// rationale (TRI-9137).
|
|
5968
6000
|
recordToolCallIdsFromMessage(capturedResponseMessage);
|
|
5969
6001
|
try {
|
|
6002
|
+
const responseForModel = responseAfterCompaction(capturedResponseMessage, responseCompaction?.baseResponseStepCount, originalResponse);
|
|
6003
|
+
// Preserve the complete persistence response, including same-ID
|
|
6004
|
+
// replacements whose old tool parts can contain new results.
|
|
6005
|
+
// Convert prefix and suffix separately so each tool output is
|
|
6006
|
+
// converted once, while only the suffix enters model context.
|
|
6007
|
+
const responsePrefixMessages = responseCompaction
|
|
6008
|
+
? await toModelMessages([
|
|
6009
|
+
stripProviderMetadata({
|
|
6010
|
+
...capturedResponseMessage,
|
|
6011
|
+
parts: capturedResponseMessage.parts.slice(0, capturedResponseMessage.parts.length -
|
|
6012
|
+
responseForModel.parts.length),
|
|
6013
|
+
}),
|
|
6014
|
+
])
|
|
6015
|
+
: [];
|
|
5970
6016
|
const responseModelMessages = await toModelMessages([
|
|
5971
|
-
stripProviderMetadata(
|
|
6017
|
+
stripProviderMetadata(responseForModel),
|
|
5972
6018
|
]);
|
|
5973
|
-
if (
|
|
6019
|
+
if (responseCompaction) {
|
|
6020
|
+
// The summary already replaced the original response, including
|
|
6021
|
+
// a same-ID approval/handover prefix. Replacing its old model run
|
|
6022
|
+
// would miss and fall back to the full, uncompacted UI history.
|
|
6023
|
+
accumulatedMessages.push(...responseModelMessages);
|
|
6024
|
+
locals.set(chatHandoverSplicedRunKey, undefined);
|
|
6025
|
+
}
|
|
6026
|
+
else if (existingIdx !== -1) {
|
|
6027
|
+
const spliced = locals.get(chatHandoverSplicedRunKey);
|
|
6028
|
+
const splicedRun = spliced && previousAtIdx && spliced.id === previousAtIdx.id
|
|
6029
|
+
? spliced.run
|
|
6030
|
+
: undefined;
|
|
5974
6031
|
const ok = previousAtIdx !== undefined &&
|
|
5975
|
-
(await replaceModelRun(accumulatedMessages, previousAtIdx, capturedResponseMessage, steerTailThisTurn));
|
|
6032
|
+
(await replaceModelRun(accumulatedMessages, previousAtIdx, capturedResponseMessage, steerTailThisTurn, splicedRun));
|
|
6033
|
+
if (splicedRun)
|
|
6034
|
+
locals.set(chatHandoverSplicedRunKey, undefined);
|
|
5976
6035
|
if (!ok) {
|
|
5977
6036
|
logger.warn("chat.agent: replaced response not found at the model lane tail; reconverting the lane");
|
|
5978
6037
|
accumulatedMessages = await toModelMessages(accumulatedUIMessages);
|
|
@@ -5983,7 +6042,7 @@ function chatAgent(options) {
|
|
|
5983
6042
|
else {
|
|
5984
6043
|
accumulatedMessages.push(...responseModelMessages);
|
|
5985
6044
|
}
|
|
5986
|
-
turnNewModelMessages.push(...responseModelMessages);
|
|
6045
|
+
turnNewModelMessages.push(...responsePrefixMessages, ...responseModelMessages);
|
|
5987
6046
|
}
|
|
5988
6047
|
catch {
|
|
5989
6048
|
// Conversion failed — skip accumulation for this turn
|
|
@@ -6032,12 +6091,14 @@ function chatAgent(options) {
|
|
|
6032
6091
|
const outerCompaction = locals.get(chatAgentCompactionKey);
|
|
6033
6092
|
const innerCompactionState = locals.get(chatCompactionStateKey);
|
|
6034
6093
|
if (outerCompaction && !innerCompactionState && turnUsage && !wasStopped) {
|
|
6094
|
+
const contextUsage = lastStepUsage ?? turnUsage;
|
|
6035
6095
|
const shouldTrigger = await outerCompaction.shouldCompact({
|
|
6036
6096
|
messages: accumulatedMessages,
|
|
6037
|
-
totalTokens:
|
|
6038
|
-
inputTokens:
|
|
6039
|
-
outputTokens:
|
|
6040
|
-
usage:
|
|
6097
|
+
totalTokens: contextUsage.totalTokens,
|
|
6098
|
+
inputTokens: contextUsage.inputTokens,
|
|
6099
|
+
outputTokens: contextUsage.outputTokens,
|
|
6100
|
+
usage: contextUsage,
|
|
6101
|
+
turnUsage,
|
|
6041
6102
|
totalUsage: cumulativeUsage,
|
|
6042
6103
|
chatId: currentWirePayload.chatId,
|
|
6043
6104
|
turn,
|
|
@@ -6362,7 +6423,7 @@ function chatAgent(options) {
|
|
|
6362
6423
|
const next = await messagesInput.waitWithIdleTimeout({
|
|
6363
6424
|
idleTimeoutInSeconds: effectiveIdleTimeout,
|
|
6364
6425
|
timeout: effectiveTurnTimeout,
|
|
6365
|
-
spanName: "
|
|
6426
|
+
spanName: "next message",
|
|
6366
6427
|
onSuspend: onChatSuspend
|
|
6367
6428
|
? async () => {
|
|
6368
6429
|
await tracer.startActiveSpan("onChatSuspend()", async () => {
|
|
@@ -6699,7 +6760,7 @@ function chatAgent(options) {
|
|
|
6699
6760
|
const next = await messagesInput.waitWithIdleTimeout({
|
|
6700
6761
|
idleTimeoutInSeconds: effectiveIdleTimeout,
|
|
6701
6762
|
timeout: effectiveTurnTimeout,
|
|
6702
|
-
spanName: "
|
|
6763
|
+
spanName: "next message (after error)",
|
|
6703
6764
|
});
|
|
6704
6765
|
if (!next.ok) {
|
|
6705
6766
|
return; // Timed out — end run gracefully
|
|
@@ -7739,6 +7800,8 @@ class ChatMessageAccumulator {
|
|
|
7739
7800
|
modelMessages = [];
|
|
7740
7801
|
uiMessages = [];
|
|
7741
7802
|
_compaction;
|
|
7803
|
+
/** The run a spliced head-start partial contributed, until its response replaces it. */
|
|
7804
|
+
_handoverRun;
|
|
7742
7805
|
_pendingMessages;
|
|
7743
7806
|
_steeringQueue = [];
|
|
7744
7807
|
constructor(options) {
|
|
@@ -7782,7 +7845,7 @@ class ChatMessageAccumulator {
|
|
|
7782
7845
|
* `consumeHandover` for the wait+seed+apply convenience.
|
|
7783
7846
|
*/
|
|
7784
7847
|
applyHandover(signal) {
|
|
7785
|
-
spliceHandoverPartial(this.modelMessages, this.uiMessages, signal);
|
|
7848
|
+
this._handoverRun = spliceHandoverPartial(this.modelMessages, this.uiMessages, signal);
|
|
7786
7849
|
}
|
|
7787
7850
|
/**
|
|
7788
7851
|
* One-call `chat.headStart` handover for a custom-agent loop: waits for the
|
|
@@ -7825,8 +7888,13 @@ class ChatMessageAccumulator {
|
|
|
7825
7888
|
if (existingIdx !== -1) {
|
|
7826
7889
|
const previous = this.uiMessages[existingIdx];
|
|
7827
7890
|
this.uiMessages[existingIdx] = response;
|
|
7891
|
+
const handoverRun = this._handoverRun && this._handoverRun.id === previous.id
|
|
7892
|
+
? this._handoverRun.run
|
|
7893
|
+
: undefined;
|
|
7894
|
+
if (handoverRun)
|
|
7895
|
+
this._handoverRun = undefined;
|
|
7828
7896
|
try {
|
|
7829
|
-
if (!(await replaceModelRun(this.modelMessages, previous, response, 0))) {
|
|
7897
|
+
if (!(await replaceModelRun(this.modelMessages, previous, response, 0, handoverRun))) {
|
|
7830
7898
|
this.modelMessages = await toModelMessages(this.uiMessages.map((m) => stripProviderMetadata(m)));
|
|
7831
7899
|
}
|
|
7832
7900
|
}
|
|
@@ -7929,8 +7997,10 @@ class ChatMessageAccumulator {
|
|
|
7929
7997
|
}
|
|
7930
7998
|
/**
|
|
7931
7999
|
* Run outer-loop compaction if needed. Call after adding the response
|
|
7932
|
-
* and capturing usage.
|
|
7933
|
-
*
|
|
8000
|
+
* and capturing usage. Pass the LAST step's usage (`result.usage`), which is
|
|
8001
|
+
* the context the model held on its final call; `result.totalUsage` sums every
|
|
8002
|
+
* step of a tool-using turn and belongs in `context.turnUsage`. Applies
|
|
8003
|
+
* `compactModelMessages` and `compactUIMessages` callbacks if configured.
|
|
7934
8004
|
*
|
|
7935
8005
|
* @returns `true` if compaction was performed, `false` otherwise.
|
|
7936
8006
|
*/
|
|
@@ -7943,6 +8013,7 @@ class ChatMessageAccumulator {
|
|
|
7943
8013
|
inputTokens: usage.inputTokens,
|
|
7944
8014
|
outputTokens: usage.outputTokens,
|
|
7945
8015
|
usage,
|
|
8016
|
+
turnUsage: context?.turnUsage,
|
|
7946
8017
|
totalUsage: context?.totalUsage,
|
|
7947
8018
|
chatId: context?.chatId,
|
|
7948
8019
|
turn: context?.turn,
|
|
@@ -8175,8 +8246,8 @@ function createChatSession(payload, options) {
|
|
|
8175
8246
|
idleTimeoutInSeconds: sessionIdleTimeoutOpt ?? currentPayload.idleTimeoutInSeconds ?? 30,
|
|
8176
8247
|
timeout,
|
|
8177
8248
|
spanName: currentPayload.trigger === "preload"
|
|
8178
|
-
? "
|
|
8179
|
-
: "
|
|
8249
|
+
? "first message"
|
|
8250
|
+
: "first message (continuation)",
|
|
8180
8251
|
});
|
|
8181
8252
|
if (!result.ok || runSignal.aborted) {
|
|
8182
8253
|
stop.cleanup();
|
|
@@ -8213,7 +8284,7 @@ function createChatSession(payload, options) {
|
|
|
8213
8284
|
const next = await messagesInput.waitWithIdleTimeout({
|
|
8214
8285
|
idleTimeoutInSeconds,
|
|
8215
8286
|
timeout,
|
|
8216
|
-
spanName: "
|
|
8287
|
+
spanName: "next message",
|
|
8217
8288
|
});
|
|
8218
8289
|
if (!next.ok || runSignal.aborted) {
|
|
8219
8290
|
stop.cleanup();
|
|
@@ -8424,6 +8495,7 @@ function createChatSession(payload, options) {
|
|
|
8424
8495
|
// indefinitely, which would wedge the turn loop (same guard as
|
|
8425
8496
|
// chat.agent's turn loop).
|
|
8426
8497
|
let turnUsage;
|
|
8498
|
+
let lastStepUsage;
|
|
8427
8499
|
if (typeof source.totalUsage?.then === "function") {
|
|
8428
8500
|
try {
|
|
8429
8501
|
const usage = (await Promise.race([
|
|
@@ -8440,14 +8512,28 @@ function createChatSession(payload, options) {
|
|
|
8440
8512
|
/* non-fatal */
|
|
8441
8513
|
}
|
|
8442
8514
|
}
|
|
8515
|
+
const lastStepUsagePromise = source.usage;
|
|
8516
|
+
if (typeof lastStepUsagePromise?.then === "function") {
|
|
8517
|
+
try {
|
|
8518
|
+
lastStepUsage = (await Promise.race([
|
|
8519
|
+
lastStepUsagePromise,
|
|
8520
|
+
new Promise((r) => setTimeout(() => r(undefined), 2_000)),
|
|
8521
|
+
]));
|
|
8522
|
+
}
|
|
8523
|
+
catch {
|
|
8524
|
+
/* non-fatal */
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8443
8527
|
// Outer-loop compaction (same logic as chat.agent)
|
|
8444
8528
|
if (sessionCompaction && turnUsage && !turnObj.stopped) {
|
|
8529
|
+
const contextUsage = lastStepUsage ?? turnUsage;
|
|
8445
8530
|
const shouldTrigger = await sessionCompaction.shouldCompact({
|
|
8446
8531
|
messages: accumulator.modelMessages,
|
|
8447
|
-
totalTokens:
|
|
8448
|
-
inputTokens:
|
|
8449
|
-
outputTokens:
|
|
8450
|
-
usage:
|
|
8532
|
+
totalTokens: contextUsage.totalTokens,
|
|
8533
|
+
inputTokens: contextUsage.inputTokens,
|
|
8534
|
+
outputTokens: contextUsage.outputTokens,
|
|
8535
|
+
usage: contextUsage,
|
|
8536
|
+
turnUsage,
|
|
8451
8537
|
totalUsage: cumulativeUsage,
|
|
8452
8538
|
chatId: currentPayload.chatId,
|
|
8453
8539
|
turn,
|