@prestyj/agent 5.6.0 → 5.8.0
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/index.cjs +85 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -5
- package/dist/index.d.ts +34 -5
- package/dist/index.js +87 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StreamOptions, Message, Tool, ToolResultContent, ServerToolDefinition,
|
|
1
|
+
import { StreamOptions, Message, Tool, ToolResultContent, ServerToolDefinition, Usage, StopReason, AssistantMessage } from '@prestyj/ai';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
interface StructuredToolResult {
|
|
@@ -49,11 +49,26 @@ interface AgentToolCallEndEvent {
|
|
|
49
49
|
isError: boolean;
|
|
50
50
|
durationMs: number;
|
|
51
51
|
}
|
|
52
|
+
interface AgentTurnTiming {
|
|
53
|
+
/** Logical turn start, before context transforms or provider retries. Unix epoch milliseconds. */
|
|
54
|
+
startedAt: number;
|
|
55
|
+
/** First provider event, or full-response arrival for non-streaming fallback. */
|
|
56
|
+
firstProviderEventAt?: number;
|
|
57
|
+
/** Successful provider response completion. Unix epoch milliseconds. */
|
|
58
|
+
completedAt: number;
|
|
59
|
+
/** Time spent awaiting provider attempts, including failed attempts but excluding retry backoff. */
|
|
60
|
+
providerDurationMs: number;
|
|
61
|
+
/** Time from logical turn start to the first provider event. */
|
|
62
|
+
ttftMs?: number;
|
|
63
|
+
/** Output tokens divided by total provider duration. Omitted when no rate is measurable. */
|
|
64
|
+
outputTokensPerSecond?: number;
|
|
65
|
+
}
|
|
52
66
|
interface AgentTurnEndEvent {
|
|
53
67
|
type: "turn_end";
|
|
54
68
|
turn: number;
|
|
55
69
|
stopReason: StopReason;
|
|
56
70
|
usage: Usage;
|
|
71
|
+
timing: AgentTurnTiming;
|
|
57
72
|
}
|
|
58
73
|
interface AgentDoneEvent {
|
|
59
74
|
type: "agent_done";
|
|
@@ -121,6 +136,14 @@ interface AgentFollowUpMessageEvent {
|
|
|
121
136
|
content: Message["content"];
|
|
122
137
|
}
|
|
123
138
|
type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentMaxTurnsEvent | AgentErrorEvent;
|
|
139
|
+
interface TransformContextOptions {
|
|
140
|
+
/** Force a transform after the provider reports context overflow. */
|
|
141
|
+
force?: boolean;
|
|
142
|
+
/** Latest successful provider usage, anchored at its assistant message. */
|
|
143
|
+
usage?: Usage;
|
|
144
|
+
/** Messages appended after that usage sample and not yet seen by the provider. */
|
|
145
|
+
pendingMessages: Message[];
|
|
146
|
+
}
|
|
124
147
|
interface AgentOptions {
|
|
125
148
|
provider: StreamOptions["provider"];
|
|
126
149
|
model: string;
|
|
@@ -129,6 +152,8 @@ interface AgentOptions {
|
|
|
129
152
|
priorMessages?: Message[];
|
|
130
153
|
tools?: AgentTool[];
|
|
131
154
|
serverTools?: ServerToolDefinition[];
|
|
155
|
+
/** Control whether tools may/must be called, or select a named tool when supported. */
|
|
156
|
+
toolChoice?: StreamOptions["toolChoice"];
|
|
132
157
|
maxTurns?: number;
|
|
133
158
|
maxTokens?: number;
|
|
134
159
|
temperature?: number;
|
|
@@ -163,6 +188,10 @@ interface AgentOptions {
|
|
|
163
188
|
clearToolUses?: boolean;
|
|
164
189
|
/** Max characters for a single tool result. Results exceeding this are truncated with a notice. */
|
|
165
190
|
maxToolResultChars?: number;
|
|
191
|
+
/** Aggregate budget for ALL tool results in one assistant turn. Protects
|
|
192
|
+
* against parallel fan-outs injecting huge uncached context in one turn;
|
|
193
|
+
* the largest results are trimmed (water-filling) with a re-run notice. */
|
|
194
|
+
maxTurnToolResultChars?: number;
|
|
166
195
|
/** Max consecutive pause_turn continuations before stopping (default: 5).
|
|
167
196
|
* Prevents infinite loops when server-side tools keep pausing. */
|
|
168
197
|
maxContinuations?: number;
|
|
@@ -171,12 +200,12 @@ interface AgentOptions {
|
|
|
171
200
|
* the messages array (e.g. compaction, truncation). Return the same array
|
|
172
201
|
* for no-op, or a new array to replace the conversation context.
|
|
173
202
|
*
|
|
203
|
+
* The latest provider usage is authoritative for the history through its
|
|
204
|
+
* assistant response. `pendingMessages` contains context appended afterward.
|
|
174
205
|
* When `options.force` is true, the caller should compact unconditionally
|
|
175
206
|
* (e.g. after a context overflow error from the API).
|
|
176
207
|
*/
|
|
177
|
-
transformContext?: (messages: Message[], options
|
|
178
|
-
force?: boolean;
|
|
179
|
-
}) => Message[] | Promise<Message[]>;
|
|
208
|
+
transformContext?: (messages: Message[], options: TransformContextOptions) => Message[] | Promise<Message[]>;
|
|
180
209
|
/**
|
|
181
210
|
* Polled after tool execution completes each turn. Returns user messages
|
|
182
211
|
* to inject into the conversation before the next LLM call (steering).
|
|
@@ -282,4 +311,4 @@ declare function isBillingError(err: unknown): boolean;
|
|
|
282
311
|
declare function isUsageLimitError(err: unknown): boolean;
|
|
283
312
|
declare function agentLoop(messages: Message[], options: AgentOptions): AsyncGenerator<AgentEvent, AgentResult>;
|
|
284
313
|
|
|
285
|
-
export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
|
|
314
|
+
export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, type TransformContextOptions, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StreamOptions, Message, Tool, ToolResultContent, ServerToolDefinition,
|
|
1
|
+
import { StreamOptions, Message, Tool, ToolResultContent, ServerToolDefinition, Usage, StopReason, AssistantMessage } from '@prestyj/ai';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
interface StructuredToolResult {
|
|
@@ -49,11 +49,26 @@ interface AgentToolCallEndEvent {
|
|
|
49
49
|
isError: boolean;
|
|
50
50
|
durationMs: number;
|
|
51
51
|
}
|
|
52
|
+
interface AgentTurnTiming {
|
|
53
|
+
/** Logical turn start, before context transforms or provider retries. Unix epoch milliseconds. */
|
|
54
|
+
startedAt: number;
|
|
55
|
+
/** First provider event, or full-response arrival for non-streaming fallback. */
|
|
56
|
+
firstProviderEventAt?: number;
|
|
57
|
+
/** Successful provider response completion. Unix epoch milliseconds. */
|
|
58
|
+
completedAt: number;
|
|
59
|
+
/** Time spent awaiting provider attempts, including failed attempts but excluding retry backoff. */
|
|
60
|
+
providerDurationMs: number;
|
|
61
|
+
/** Time from logical turn start to the first provider event. */
|
|
62
|
+
ttftMs?: number;
|
|
63
|
+
/** Output tokens divided by total provider duration. Omitted when no rate is measurable. */
|
|
64
|
+
outputTokensPerSecond?: number;
|
|
65
|
+
}
|
|
52
66
|
interface AgentTurnEndEvent {
|
|
53
67
|
type: "turn_end";
|
|
54
68
|
turn: number;
|
|
55
69
|
stopReason: StopReason;
|
|
56
70
|
usage: Usage;
|
|
71
|
+
timing: AgentTurnTiming;
|
|
57
72
|
}
|
|
58
73
|
interface AgentDoneEvent {
|
|
59
74
|
type: "agent_done";
|
|
@@ -121,6 +136,14 @@ interface AgentFollowUpMessageEvent {
|
|
|
121
136
|
content: Message["content"];
|
|
122
137
|
}
|
|
123
138
|
type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentMaxTurnsEvent | AgentErrorEvent;
|
|
139
|
+
interface TransformContextOptions {
|
|
140
|
+
/** Force a transform after the provider reports context overflow. */
|
|
141
|
+
force?: boolean;
|
|
142
|
+
/** Latest successful provider usage, anchored at its assistant message. */
|
|
143
|
+
usage?: Usage;
|
|
144
|
+
/** Messages appended after that usage sample and not yet seen by the provider. */
|
|
145
|
+
pendingMessages: Message[];
|
|
146
|
+
}
|
|
124
147
|
interface AgentOptions {
|
|
125
148
|
provider: StreamOptions["provider"];
|
|
126
149
|
model: string;
|
|
@@ -129,6 +152,8 @@ interface AgentOptions {
|
|
|
129
152
|
priorMessages?: Message[];
|
|
130
153
|
tools?: AgentTool[];
|
|
131
154
|
serverTools?: ServerToolDefinition[];
|
|
155
|
+
/** Control whether tools may/must be called, or select a named tool when supported. */
|
|
156
|
+
toolChoice?: StreamOptions["toolChoice"];
|
|
132
157
|
maxTurns?: number;
|
|
133
158
|
maxTokens?: number;
|
|
134
159
|
temperature?: number;
|
|
@@ -163,6 +188,10 @@ interface AgentOptions {
|
|
|
163
188
|
clearToolUses?: boolean;
|
|
164
189
|
/** Max characters for a single tool result. Results exceeding this are truncated with a notice. */
|
|
165
190
|
maxToolResultChars?: number;
|
|
191
|
+
/** Aggregate budget for ALL tool results in one assistant turn. Protects
|
|
192
|
+
* against parallel fan-outs injecting huge uncached context in one turn;
|
|
193
|
+
* the largest results are trimmed (water-filling) with a re-run notice. */
|
|
194
|
+
maxTurnToolResultChars?: number;
|
|
166
195
|
/** Max consecutive pause_turn continuations before stopping (default: 5).
|
|
167
196
|
* Prevents infinite loops when server-side tools keep pausing. */
|
|
168
197
|
maxContinuations?: number;
|
|
@@ -171,12 +200,12 @@ interface AgentOptions {
|
|
|
171
200
|
* the messages array (e.g. compaction, truncation). Return the same array
|
|
172
201
|
* for no-op, or a new array to replace the conversation context.
|
|
173
202
|
*
|
|
203
|
+
* The latest provider usage is authoritative for the history through its
|
|
204
|
+
* assistant response. `pendingMessages` contains context appended afterward.
|
|
174
205
|
* When `options.force` is true, the caller should compact unconditionally
|
|
175
206
|
* (e.g. after a context overflow error from the API).
|
|
176
207
|
*/
|
|
177
|
-
transformContext?: (messages: Message[], options
|
|
178
|
-
force?: boolean;
|
|
179
|
-
}) => Message[] | Promise<Message[]>;
|
|
208
|
+
transformContext?: (messages: Message[], options: TransformContextOptions) => Message[] | Promise<Message[]>;
|
|
180
209
|
/**
|
|
181
210
|
* Polled after tool execution completes each turn. Returns user messages
|
|
182
211
|
* to inject into the conversation before the next LLM call (steering).
|
|
@@ -282,4 +311,4 @@ declare function isBillingError(err: unknown): boolean;
|
|
|
282
311
|
declare function isUsageLimitError(err: unknown): boolean;
|
|
283
312
|
declare function agentLoop(messages: Message[], options: AgentOptions): AsyncGenerator<AgentEvent, AgentResult>;
|
|
284
313
|
|
|
285
|
-
export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
|
|
314
|
+
export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, type TransformContextOptions, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
stream,
|
|
8
8
|
EventStream,
|
|
9
9
|
EZCoderAIError,
|
|
10
|
-
isHardBillingMessage
|
|
10
|
+
isHardBillingMessage,
|
|
11
|
+
redactValue
|
|
11
12
|
} from "@prestyj/ai";
|
|
12
13
|
var DEFAULT_MAX_TURNS = 300;
|
|
13
14
|
var _diagFn = null;
|
|
@@ -126,7 +127,7 @@ function classifyOverload(err) {
|
|
|
126
127
|
if (statusCode === 529 || msg.includes("overloaded") || msg.includes("529")) {
|
|
127
128
|
return "overloaded";
|
|
128
129
|
}
|
|
129
|
-
if (statusCode === 500 || statusCode === 502 || statusCode === 503 || statusCode === 504 || msg.includes("api_error") || msg.includes("server_error") || msg.includes("internal server error") || msg.includes("bad gateway") || msg.includes("service unavailable") || msg.includes("gateway timeout")) {
|
|
130
|
+
if (statusCode === 500 || statusCode === 502 || statusCode === 503 || statusCode === 504 || statusCode === 507 || msg.includes("api_error") || msg.includes("server_error") || msg.includes("internal server error") || msg.includes("bad gateway") || msg.includes("service unavailable") || msg.includes("gateway timeout") || msg.includes("exceeded request buffer limit while retrying upstream")) {
|
|
130
131
|
return "provider_error";
|
|
131
132
|
}
|
|
132
133
|
if (isOpaqueProviderMessage(err.message)) {
|
|
@@ -232,6 +233,8 @@ async function* agentLoop(messages, options) {
|
|
|
232
233
|
const maxContinuations = options.maxContinuations ?? 5;
|
|
233
234
|
let toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
|
|
234
235
|
const totalUsage = { inputTokens: 0, outputTokens: 0 };
|
|
236
|
+
let latestProviderUsage;
|
|
237
|
+
let usageAnchorIndex;
|
|
235
238
|
let turn = 0;
|
|
236
239
|
let hitMaxTurns = false;
|
|
237
240
|
let firstTurn = true;
|
|
@@ -268,10 +271,14 @@ async function* agentLoop(messages, options) {
|
|
|
268
271
|
const initialHardTimeoutMs = isSakana ? STREAM_THINKING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
|
|
269
272
|
const MAX_TOOLCALL_DELTA_CHARS = 1e6;
|
|
270
273
|
const MAX_TOOLCALL_DELTA_EVENTS = 2e4;
|
|
274
|
+
let logicalTurnStartedAt = 0;
|
|
275
|
+
let firstProviderEventAt;
|
|
276
|
+
let providerDurationMs = 0;
|
|
271
277
|
try {
|
|
272
278
|
while (turn < maxTurns) {
|
|
273
279
|
options.signal?.throwIfAborted();
|
|
274
280
|
turn++;
|
|
281
|
+
if (logicalTurnStartedAt === 0) logicalTurnStartedAt = Date.now();
|
|
275
282
|
toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
|
|
276
283
|
if (_diagFn) {
|
|
277
284
|
let msgChars = 0;
|
|
@@ -304,7 +311,11 @@ async function* agentLoop(messages, options) {
|
|
|
304
311
|
firstTurn = false;
|
|
305
312
|
if (options.transformContext) {
|
|
306
313
|
diag("transform_start");
|
|
307
|
-
const
|
|
314
|
+
const pendingMessages = usageAnchorIndex === void 0 ? [] : messages.slice(usageAnchorIndex + 1);
|
|
315
|
+
const transformed = await options.transformContext(messages, {
|
|
316
|
+
usage: latestProviderUsage,
|
|
317
|
+
pendingMessages
|
|
318
|
+
});
|
|
308
319
|
if (transformed !== messages) {
|
|
309
320
|
diag("transform_compacted", {
|
|
310
321
|
before: messages.length,
|
|
@@ -312,6 +323,8 @@ async function* agentLoop(messages, options) {
|
|
|
312
323
|
});
|
|
313
324
|
messages.length = 0;
|
|
314
325
|
messages.push(...transformed);
|
|
326
|
+
latestProviderUsage = void 0;
|
|
327
|
+
usageAnchorIndex = void 0;
|
|
315
328
|
}
|
|
316
329
|
diag("transform_end");
|
|
317
330
|
}
|
|
@@ -321,6 +334,7 @@ async function* agentLoop(messages, options) {
|
|
|
321
334
|
let idleTimer = null;
|
|
322
335
|
let hardTimer = null;
|
|
323
336
|
let idleTimedOut = false;
|
|
337
|
+
let providerAttemptStartedAt;
|
|
324
338
|
let streamEventCount = 0;
|
|
325
339
|
let lastEventTime = Date.now();
|
|
326
340
|
let streamCallStart = Date.now();
|
|
@@ -366,12 +380,14 @@ async function* agentLoop(messages, options) {
|
|
|
366
380
|
try {
|
|
367
381
|
diag("stream_call", { nonStreaming: useNonStreamingFallback });
|
|
368
382
|
streamCallStart = Date.now();
|
|
383
|
+
providerAttemptStartedAt = streamCallStart;
|
|
369
384
|
const result = stream({
|
|
370
385
|
provider: options.provider,
|
|
371
386
|
model: options.model,
|
|
372
387
|
messages,
|
|
373
388
|
tools: options.tools,
|
|
374
389
|
serverTools: options.serverTools,
|
|
390
|
+
toolChoice: options.toolChoice,
|
|
375
391
|
webSearch: options.webSearch,
|
|
376
392
|
maxTokens: options.maxTokens,
|
|
377
393
|
temperature: options.temperature,
|
|
@@ -414,6 +430,7 @@ async function* agentLoop(messages, options) {
|
|
|
414
430
|
maxConsumerLagMs = consumerLag;
|
|
415
431
|
}
|
|
416
432
|
streamEventCount++;
|
|
433
|
+
if (firstProviderEventAt === void 0) firstProviderEventAt = pullTime;
|
|
417
434
|
eventTypeCounts[event.type] = (eventTypeCounts[event.type] ?? 0) + 1;
|
|
418
435
|
lastEventType = event.type;
|
|
419
436
|
if ((event.type === "text_delta" || event.type === "server_toolcall" || event.type === "toolcall_delta") && !hasReceivedEvent) {
|
|
@@ -506,6 +523,7 @@ async function* agentLoop(messages, options) {
|
|
|
506
523
|
eventTypes: eventTypeCounts
|
|
507
524
|
});
|
|
508
525
|
response = await abortablePromise(result.response, streamController.signal);
|
|
526
|
+
if (firstProviderEventAt === void 0) firstProviderEventAt = Date.now();
|
|
509
527
|
} catch (err) {
|
|
510
528
|
if (streamController.signal.aborted) closeIterator(streamIterator);
|
|
511
529
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -567,10 +585,17 @@ async function* agentLoop(messages, options) {
|
|
|
567
585
|
...overflowDetails
|
|
568
586
|
});
|
|
569
587
|
try {
|
|
570
|
-
const
|
|
588
|
+
const pendingMessages = usageAnchorIndex === void 0 ? [] : messages.slice(usageAnchorIndex + 1);
|
|
589
|
+
const compacted = await options.transformContext(messages, {
|
|
590
|
+
force: true,
|
|
591
|
+
usage: latestProviderUsage,
|
|
592
|
+
pendingMessages
|
|
593
|
+
});
|
|
571
594
|
if (compacted !== messages && compacted.length < messages.length) {
|
|
572
595
|
messages.length = 0;
|
|
573
596
|
messages.push(...compacted);
|
|
597
|
+
latestProviderUsage = void 0;
|
|
598
|
+
usageAnchorIndex = void 0;
|
|
574
599
|
diag("overflow_compact_success", {
|
|
575
600
|
attempt: overflowCompactionAttempts,
|
|
576
601
|
messages: messages.length,
|
|
@@ -730,6 +755,9 @@ async function* agentLoop(messages, options) {
|
|
|
730
755
|
});
|
|
731
756
|
throw err;
|
|
732
757
|
} finally {
|
|
758
|
+
if (providerAttemptStartedAt !== void 0) {
|
|
759
|
+
providerDurationMs += Date.now() - providerAttemptStartedAt;
|
|
760
|
+
}
|
|
733
761
|
if (idleTimer) clearTimeout(idleTimer);
|
|
734
762
|
if (hardTimer) clearTimeout(hardTimer);
|
|
735
763
|
options.signal?.removeEventListener("abort", forwardAbort);
|
|
@@ -773,11 +801,29 @@ async function* agentLoop(messages, options) {
|
|
|
773
801
|
totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;
|
|
774
802
|
}
|
|
775
803
|
messages.push(response.message);
|
|
804
|
+
latestProviderUsage = response.usage;
|
|
805
|
+
usageAnchorIndex = messages.length - 1;
|
|
806
|
+
const completedAt = Date.now();
|
|
807
|
+
const outputTokensPerSecond = providerDurationMs > 0 && response.usage.outputTokens > 0 ? response.usage.outputTokens / (providerDurationMs / 1e3) : void 0;
|
|
808
|
+
const timing = {
|
|
809
|
+
startedAt: logicalTurnStartedAt,
|
|
810
|
+
...firstProviderEventAt !== void 0 ? {
|
|
811
|
+
firstProviderEventAt,
|
|
812
|
+
ttftMs: Math.max(0, firstProviderEventAt - logicalTurnStartedAt)
|
|
813
|
+
} : {},
|
|
814
|
+
completedAt,
|
|
815
|
+
providerDurationMs,
|
|
816
|
+
...outputTokensPerSecond !== void 0 ? { outputTokensPerSecond } : {}
|
|
817
|
+
};
|
|
818
|
+
logicalTurnStartedAt = 0;
|
|
819
|
+
firstProviderEventAt = void 0;
|
|
820
|
+
providerDurationMs = 0;
|
|
776
821
|
yield {
|
|
777
822
|
type: "turn_end",
|
|
778
823
|
turn,
|
|
779
824
|
stopReason: response.stopReason,
|
|
780
|
-
usage: response.usage
|
|
825
|
+
usage: response.usage,
|
|
826
|
+
timing
|
|
781
827
|
};
|
|
782
828
|
if (response.stopReason === "pause_turn") {
|
|
783
829
|
consecutivePauses++;
|
|
@@ -844,6 +890,7 @@ async function* agentLoop(messages, options) {
|
|
|
844
890
|
const executionOptions = {
|
|
845
891
|
signal: options.signal,
|
|
846
892
|
maxToolResultChars: options.maxToolResultChars,
|
|
893
|
+
maxTurnToolResultChars: options.maxTurnToolResultChars,
|
|
847
894
|
toolMap,
|
|
848
895
|
invalidToolArgumentCounts,
|
|
849
896
|
markFatalToolArgumentError
|
|
@@ -961,8 +1008,8 @@ async function executeSingleToolCall(toolCall, options, pushEvent) {
|
|
|
961
1008
|
ctx.signal
|
|
962
1009
|
);
|
|
963
1010
|
const normalized = normalizeToolResult(raw);
|
|
964
|
-
resultContent = normalized.content;
|
|
965
|
-
details = normalized.details;
|
|
1011
|
+
resultContent = redactValue(normalized.content);
|
|
1012
|
+
details = redactValue(normalized.details);
|
|
966
1013
|
for (const key of options.invalidToolArgumentCounts.keys()) {
|
|
967
1014
|
if (key.startsWith(`${toolCall.name}:`)) options.invalidToolArgumentCounts.delete(key);
|
|
968
1015
|
}
|
|
@@ -990,10 +1037,12 @@ async function executeSingleToolCall(toolCall, options, pushEvent) {
|
|
|
990
1037
|
);
|
|
991
1038
|
}
|
|
992
1039
|
} else {
|
|
993
|
-
resultContent = err instanceof Error ? err.message : String(err);
|
|
1040
|
+
resultContent = redactValue(err instanceof Error ? err.message : String(err));
|
|
994
1041
|
}
|
|
995
1042
|
}
|
|
996
1043
|
}
|
|
1044
|
+
resultContent = redactValue(resultContent);
|
|
1045
|
+
details = redactValue(details);
|
|
997
1046
|
const durationMs = Date.now() - startTime;
|
|
998
1047
|
pushEvent({
|
|
999
1048
|
type: "tool_call_end",
|
|
@@ -1081,6 +1130,7 @@ async function* executeToolCallsMixed(toolCalls, initialToolResults, options) {
|
|
|
1081
1130
|
}
|
|
1082
1131
|
const toolResults = buildToolResults(initialToolResults, toolCalls, resultsById);
|
|
1083
1132
|
capToolResults(toolResults, options.maxToolResultChars);
|
|
1133
|
+
capTurnToolResults(toolResults, options.maxTurnToolResultChars);
|
|
1084
1134
|
return { toolResults, aborted };
|
|
1085
1135
|
}
|
|
1086
1136
|
async function* executeToolCallsParallel(toolCalls, initialToolResults, options) {
|
|
@@ -1120,6 +1170,7 @@ async function* executeToolCallsParallel(toolCalls, initialToolResults, options)
|
|
|
1120
1170
|
}
|
|
1121
1171
|
const toolResults = buildToolResults(initialToolResults, toolCalls, resultsById);
|
|
1122
1172
|
capToolResults(toolResults, options.maxToolResultChars);
|
|
1173
|
+
capTurnToolResults(toolResults, options.maxTurnToolResultChars);
|
|
1123
1174
|
return { toolResults, aborted };
|
|
1124
1175
|
}
|
|
1125
1176
|
function buildToolResults(initialToolResults, toolCalls, resultsById) {
|
|
@@ -1162,6 +1213,34 @@ function capToolResults(toolResults, maxToolResultChars) {
|
|
|
1162
1213
|
` + tail;
|
|
1163
1214
|
}
|
|
1164
1215
|
}
|
|
1216
|
+
function capTurnToolResults(toolResults, maxTurnToolResultChars) {
|
|
1217
|
+
if (!maxTurnToolResultChars) return;
|
|
1218
|
+
const textResults = toolResults.filter(
|
|
1219
|
+
(toolResult) => typeof toolResult.content === "string"
|
|
1220
|
+
);
|
|
1221
|
+
const total = textResults.reduce((sum, toolResult) => sum + toolResult.content.length, 0);
|
|
1222
|
+
if (total <= maxTurnToolResultChars) return;
|
|
1223
|
+
const bySize = [...textResults].sort((a, b) => a.content.length - b.content.length);
|
|
1224
|
+
let remaining = maxTurnToolResultChars;
|
|
1225
|
+
let left = bySize.length;
|
|
1226
|
+
for (const toolResult of bySize) {
|
|
1227
|
+
const fairShare = Math.floor(remaining / left);
|
|
1228
|
+
left--;
|
|
1229
|
+
if (toolResult.content.length <= fairShare) {
|
|
1230
|
+
remaining -= toolResult.content.length;
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
remaining -= fairShare;
|
|
1234
|
+
const headChars = Math.floor(fairShare * 0.7);
|
|
1235
|
+
const tailChars = fairShare - headChars;
|
|
1236
|
+
const omitted = toolResult.content.length - fairShare;
|
|
1237
|
+
toolResult.content = toolResult.content.slice(0, headChars) + `
|
|
1238
|
+
|
|
1239
|
+
[... ${omitted} characters trimmed: this turn's combined tool results exceeded the per-turn budget. Re-run this call alone with narrower filters or offset/limit if you need the omitted content ...]
|
|
1240
|
+
|
|
1241
|
+
` + (tailChars > 0 ? toolResult.content.slice(-tailChars) : "");
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1165
1244
|
function normalizeToolResult(raw) {
|
|
1166
1245
|
return typeof raw === "string" ? { content: raw } : raw;
|
|
1167
1246
|
}
|