@adonis-agora/agent 0.92.0 → 0.94.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/CHANGELOG.md +23 -0
- package/dist/assets/spa/assets/index-DUq09Q_k.js +9 -0
- package/dist/assets/spa/index.html +1 -1
- package/dist/providers/agent_provider.d.ts.map +1 -1
- package/dist/providers/agent_provider.js +51 -0
- package/dist/providers/agent_provider.js.map +1 -1
- package/dist/src/agent-loop.d.ts +8 -0
- package/dist/src/agent-loop.d.ts.map +1 -1
- package/dist/src/agent-loop.js +196 -87
- package/dist/src/agent-loop.js.map +1 -1
- package/dist/src/agent-service.d.ts +76 -1
- package/dist/src/agent-service.d.ts.map +1 -1
- package/dist/src/agent-service.js +111 -0
- package/dist/src/agent-service.js.map +1 -1
- package/dist/src/diagnostics.d.ts +67 -0
- package/dist/src/diagnostics.d.ts.map +1 -1
- package/dist/src/diagnostics.js +18 -4
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/durable/agent-run-workflow.d.ts.map +1 -1
- package/dist/src/durable/agent-run-workflow.js +11 -0
- package/dist/src/durable/agent-run-workflow.js.map +1 -1
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/opencode/turn.d.ts.map +1 -1
- package/dist/src/opencode/turn.js +4 -1
- package/dist/src/opencode/turn.js.map +1 -1
- package/dist/src/run-timings.d.ts +16 -0
- package/dist/src/run-timings.d.ts.map +1 -0
- package/dist/src/run-timings.js +59 -0
- package/dist/src/run-timings.js.map +1 -0
- package/dist/src/runners/inline-agent-runner.d.ts.map +1 -1
- package/dist/src/runners/inline-agent-runner.js +5 -1
- package/dist/src/runners/inline-agent-runner.js.map +1 -1
- package/dist/src/spi/agent-store.d.ts +90 -1
- package/dist/src/spi/agent-store.d.ts.map +1 -1
- package/dist/src/spi/agent-store.js +7 -0
- package/dist/src/spi/agent-store.js.map +1 -1
- package/dist/src/spi/governance-queries.d.ts +103 -0
- package/dist/src/spi/governance-queries.d.ts.map +1 -1
- package/dist/src/spi/governance-queries.js +16 -1
- package/dist/src/spi/governance-queries.js.map +1 -1
- package/dist/src/stores/lucid-governance-queries.d.ts +18 -1
- package/dist/src/stores/lucid-governance-queries.d.ts.map +1 -1
- package/dist/src/stores/lucid-governance-queries.js +180 -2
- package/dist/src/stores/lucid-governance-queries.js.map +1 -1
- package/dist/src/stores/lucid-schema.d.ts +22 -0
- package/dist/src/stores/lucid-schema.d.ts.map +1 -1
- package/dist/src/stores/lucid-schema.js +184 -20
- package/dist/src/stores/lucid-schema.js.map +1 -1
- package/dist/src/stores/lucid.d.ts +14 -0
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +58 -4
- package/dist/src/stores/lucid.js.map +1 -1
- package/dist/src/testing/in-memory-governance-queries.d.ts +11 -1
- package/dist/src/testing/in-memory-governance-queries.d.ts.map +1 -1
- package/dist/src/testing/in-memory-governance-queries.js +95 -2
- package/dist/src/testing/in-memory-governance-queries.js.map +1 -1
- package/dist/src/testing/in-memory-store.d.ts +15 -2
- package/dist/src/testing/in-memory-store.d.ts.map +1 -1
- package/dist/src/testing/in-memory-store.js +25 -3
- package/dist/src/testing/in-memory-store.js.map +1 -1
- package/dist/src/types.d.ts +6 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/usage-attribution.d.ts +10 -0
- package/dist/src/usage-attribution.d.ts.map +1 -0
- package/dist/src/usage-attribution.js +12 -0
- package/dist/src/usage-attribution.js.map +1 -0
- package/dist/src/usage-ledger.d.ts +9 -0
- package/dist/src/usage-ledger.d.ts.map +1 -0
- package/dist/src/usage-ledger.js +36 -0
- package/dist/src/usage-ledger.js.map +1 -0
- package/package.json +1 -1
- package/skills/agent-governance/SKILL.md +1 -1
- package/dist/assets/spa/assets/index-BsGunSCg.js +0 -9
package/dist/src/agent-loop.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { turnChannel } from './genui/channels.js';
|
|
2
2
|
import { GenuiTurnTelemetry, observeGenuiFrames } from './genui-telemetry.js';
|
|
3
3
|
import { DEFAULT_REFUSAL_REASON } from './refusal.js';
|
|
4
|
+
import { rollupRunTimings, toolsWallClockMs } from './run-timings.js';
|
|
4
5
|
import { observeFirstText } from './turn-timing.js';
|
|
6
|
+
import { usageAttribution } from './usage-attribution.js';
|
|
7
|
+
import { recordUsageRow } from './usage-ledger.js';
|
|
5
8
|
export { DEFAULT_REFUSAL_REASON } from './refusal.js';
|
|
6
9
|
import { createHash } from 'node:crypto';
|
|
7
10
|
import { resolveActionProposalApproval } from './action-proposal-approval.js';
|
|
@@ -9,7 +12,7 @@ import { prepareActionProposal } from './action-proposal-preparation.js';
|
|
|
9
12
|
import { actionProposalReceipt } from './action-proposal-receipt.js';
|
|
10
13
|
import { danglingToolCallIds, settleDanglingToolCalls } from './dangling-tool-calls.js';
|
|
11
14
|
import { detachedDelivered, detachedStarted, } from './delegation.js';
|
|
12
|
-
import { publishAgentDelegated, publishAgentMessage, publishAgentRetrieved, publishAgentRunCancelled, publishAgentRunFailed, publishAgentRunFinished, publishAgentRunStarted, publishAgentToolCall, publishAgentToolRetry, spannedAgent, } from './diagnostics.js';
|
|
15
|
+
import { publishAgentDelegated, publishAgentMessage, publishAgentRetrieved, publishAgentRunCancelled, publishAgentRunFailed, publishAgentRunFinished, publishAgentRunStarted, publishAgentToolCall, publishAgentToolRetry, spannedAgent, timedSpan, } from './diagnostics.js';
|
|
13
16
|
import { ASK_TOOL_NAME, askInputSchema, askToolDefinition, DEFAULT_INTAKE_PREAMBLE, isHumanDecision, normalizeElicitationReply, settleElicitation, } from './elicitation.js';
|
|
14
17
|
import { renderHistorySummary } from './history-window.js';
|
|
15
18
|
import { buildMemoryBlock, offerMemories, REMEMBER_TOOL_NAME, rememberInputSchema, rememberToolDefinition, writeMemory, } from './memory.js';
|
|
@@ -512,8 +515,9 @@ async function applyHistoryWindow(window, deps, input, hooks, messages, step) {
|
|
|
512
515
|
const summary = await step('history:summarize', () => summarize(drop, ctx));
|
|
513
516
|
if (summary.usage !== undefined) {
|
|
514
517
|
const usage = summary.usage;
|
|
515
|
-
await step('persist:usage:history', () => deps.store
|
|
518
|
+
await step('persist:usage:history', () => recordUsageRow(deps.store, {
|
|
516
519
|
threadId: input.threadId,
|
|
520
|
+
...usageAttribution(input),
|
|
517
521
|
actorRef: input.actor.id,
|
|
518
522
|
runId: hooks.runId,
|
|
519
523
|
modelId: summary.modelId ?? deps.modelId ?? 'unknown',
|
|
@@ -727,6 +731,26 @@ const PARALLEL_TOOLS_PATCH = 'agent:parallel-tools';
|
|
|
727
731
|
* supply.
|
|
728
732
|
*/
|
|
729
733
|
const MESSAGE_TOOL_RESULTS_PATCH = 'agent:message-tool-results';
|
|
734
|
+
/** Note a settled call's journaled execution window on its step. */
|
|
735
|
+
function noteToolTiming(turn, call, journaled) {
|
|
736
|
+
if (turn.toolTimings === undefined || journaled === undefined || journaled === null)
|
|
737
|
+
return;
|
|
738
|
+
if (typeof journaled.ms !== 'number')
|
|
739
|
+
return;
|
|
740
|
+
turn.toolTimings.push({
|
|
741
|
+
toolCallId: call.id,
|
|
742
|
+
toolName: call.name,
|
|
743
|
+
ms: journaled.ms,
|
|
744
|
+
...(typeof journaled.at === 'number' ? { at: journaled.at } : {}),
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
/** The execution window a persist checkpoint journals, from the outcome measured in this process. */
|
|
748
|
+
function journalTiming(outcome) {
|
|
749
|
+
return {
|
|
750
|
+
...(outcome.durationMs !== undefined ? { ms: outcome.durationMs } : {}),
|
|
751
|
+
...(outcome.startedAt !== undefined ? { at: outcome.startedAt } : {}),
|
|
752
|
+
};
|
|
753
|
+
}
|
|
730
754
|
/**
|
|
731
755
|
* Record the call and settle its KIND, which decides this call's control flow: an `action` suspends
|
|
732
756
|
* the run on an approval signal (`tool:<runId>:<callId>`), an `agent` delegates to a child run,
|
|
@@ -1065,7 +1089,7 @@ async function invokeClaimedTool(turn, claimed, presentationOnly) {
|
|
|
1065
1089
|
const invokeOptions = allowedTools === undefined ? {} : { allowedTools };
|
|
1066
1090
|
// Set only when the step's body runs here: a checkpoint read back from the journal has no timing.
|
|
1067
1091
|
let ranAt;
|
|
1068
|
-
const timing = () => ranAt === undefined ? { replayed: true } : { durationMs: Date.now() - ranAt };
|
|
1092
|
+
const timing = () => ranAt === undefined ? { replayed: true } : { durationMs: Date.now() - ranAt, startedAt: ranAt };
|
|
1069
1093
|
try {
|
|
1070
1094
|
const raw = await hooks.step(`tool:${call.id}`, async () => {
|
|
1071
1095
|
ranAt = Date.now();
|
|
@@ -1226,7 +1250,7 @@ async function recordToolOutcome(turn, claimed, outcome, deciderRef = turn.input
|
|
|
1226
1250
|
});
|
|
1227
1251
|
};
|
|
1228
1252
|
if (outcome.status === 'failed') {
|
|
1229
|
-
await hooks.step(`persist:toolfail:${call.id}`, async () => {
|
|
1253
|
+
const journaled = await hooks.step(`persist:toolfail:${call.id}`, async () => {
|
|
1230
1254
|
await deps.store.updateToolCall({
|
|
1231
1255
|
toolCallId: call.id,
|
|
1232
1256
|
runId: hooks.runId,
|
|
@@ -1236,7 +1260,10 @@ async function recordToolOutcome(turn, claimed, outcome, deciderRef = turn.input
|
|
|
1236
1260
|
...decided,
|
|
1237
1261
|
});
|
|
1238
1262
|
await settle();
|
|
1263
|
+
// Journaled with the checkpoint that records it, so a replay counts the call's time once.
|
|
1264
|
+
return journalTiming(outcome);
|
|
1239
1265
|
});
|
|
1266
|
+
noteToolTiming(turn, call, journaled);
|
|
1240
1267
|
publishAgentToolCall({
|
|
1241
1268
|
runId: hooks.runId,
|
|
1242
1269
|
toolCallId: call.id,
|
|
@@ -1253,7 +1280,7 @@ async function recordToolOutcome(turn, claimed, outcome, deciderRef = turn.input
|
|
|
1253
1280
|
...(outcome.denied ? { denied: true } : {}),
|
|
1254
1281
|
};
|
|
1255
1282
|
}
|
|
1256
|
-
await hooks.step(`persist:toolexec:${call.id}`, async () => {
|
|
1283
|
+
const journaled = await hooks.step(`persist:toolexec:${call.id}`, async () => {
|
|
1257
1284
|
await deps.store.updateToolCall({
|
|
1258
1285
|
toolCallId: call.id,
|
|
1259
1286
|
runId: hooks.runId,
|
|
@@ -1264,7 +1291,10 @@ async function recordToolOutcome(turn, claimed, outcome, deciderRef = turn.input
|
|
|
1264
1291
|
...decided,
|
|
1265
1292
|
});
|
|
1266
1293
|
await settle();
|
|
1294
|
+
// Journaled with the checkpoint that records it, so a replay counts the call's time once.
|
|
1295
|
+
return journalTiming(outcome);
|
|
1267
1296
|
});
|
|
1297
|
+
noteToolTiming(turn, call, journaled);
|
|
1268
1298
|
publishAgentToolCall({
|
|
1269
1299
|
runId: hooks.runId,
|
|
1270
1300
|
toolCallId: call.id,
|
|
@@ -1906,8 +1936,9 @@ async function structureAnswer(args) {
|
|
|
1906
1936
|
...(turn.modelId !== undefined ? { modelId: turn.modelId } : {}),
|
|
1907
1937
|
};
|
|
1908
1938
|
});
|
|
1909
|
-
await hooks.step(`persist:usage:structured:${step}:${attempt}`, () => deps.store
|
|
1939
|
+
await hooks.step(`persist:usage:structured:${step}:${attempt}`, () => recordUsageRow(deps.store, {
|
|
1910
1940
|
threadId: input.threadId,
|
|
1941
|
+
...usageAttribution(input),
|
|
1911
1942
|
actorRef: input.actor.id,
|
|
1912
1943
|
runId: hooks.runId,
|
|
1913
1944
|
modelId: reply.modelId ?? deps.modelId ?? 'unknown',
|
|
@@ -2077,14 +2108,29 @@ export function streamErrorFrame(error, runId) {
|
|
|
2077
2108
|
*/
|
|
2078
2109
|
export async function runAgentLoop(boundDeps, requested, hooks) {
|
|
2079
2110
|
const startedAt = Date.now();
|
|
2111
|
+
const clock = {};
|
|
2080
2112
|
try {
|
|
2081
|
-
return await runAgentTurn(boundDeps, requested, hooks);
|
|
2113
|
+
return await runAgentTurn(boundDeps, requested, hooks, clock);
|
|
2082
2114
|
}
|
|
2083
2115
|
catch (error) {
|
|
2084
|
-
publishRunEnded(error, requested, hooks, startedAt);
|
|
2116
|
+
publishRunEnded(error, requested, hooks, startedAt, clock);
|
|
2085
2117
|
throw error;
|
|
2086
2118
|
}
|
|
2087
2119
|
}
|
|
2120
|
+
/** The queue time the runner reports, rounded; `undefined` when it has none (or it fails). */
|
|
2121
|
+
async function readQueueMs(hooks) {
|
|
2122
|
+
if (hooks.queueMs === undefined)
|
|
2123
|
+
return undefined;
|
|
2124
|
+
try {
|
|
2125
|
+
const value = await hooks.queueMs();
|
|
2126
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
2127
|
+
? Math.max(0, Math.round(value))
|
|
2128
|
+
: undefined;
|
|
2129
|
+
}
|
|
2130
|
+
catch {
|
|
2131
|
+
return undefined;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2088
2134
|
/** Who and what the run is, for its terminal diagnostics. */
|
|
2089
2135
|
function runContext(input) {
|
|
2090
2136
|
return {
|
|
@@ -2102,22 +2148,36 @@ function runContext(input) {
|
|
|
2102
2148
|
* nothing; a run whose abort signal fired was stopped; anything else failed, under the same code the
|
|
2103
2149
|
* stream's `error` frame carries.
|
|
2104
2150
|
*/
|
|
2105
|
-
function publishRunEnded(error, input, hooks, startedAt) {
|
|
2151
|
+
function publishRunEnded(error, input, hooks, startedAt, clock) {
|
|
2106
2152
|
if (!isReplayIntegrityError(error) && hooks.isControlFlowError?.(error) === true)
|
|
2107
2153
|
return;
|
|
2108
2154
|
const base = {
|
|
2109
2155
|
runId: hooks.runId,
|
|
2110
2156
|
threadId: input.threadId,
|
|
2111
2157
|
durationMs: Date.now() - startedAt,
|
|
2158
|
+
...(clock.queueMs !== undefined ? { queueMs: clock.queueMs } : {}),
|
|
2112
2159
|
...runContext(input),
|
|
2113
2160
|
};
|
|
2114
2161
|
if (hooks.abortSignal?.aborted === true) {
|
|
2115
2162
|
publishAgentRunCancelled(base);
|
|
2116
2163
|
return;
|
|
2117
2164
|
}
|
|
2118
|
-
|
|
2165
|
+
const timings = clock.steps !== undefined ? rollupRunTimings(clock.steps) : undefined;
|
|
2166
|
+
publishAgentRunFailed({
|
|
2167
|
+
...base,
|
|
2168
|
+
code: streamErrorCode(error),
|
|
2169
|
+
...(timings !== undefined
|
|
2170
|
+
? {
|
|
2171
|
+
modelMs: timings.modelMs,
|
|
2172
|
+
toolsMs: timings.toolsMs,
|
|
2173
|
+
modelCalls: timings.modelCalls,
|
|
2174
|
+
toolCalls: timings.toolCalls,
|
|
2175
|
+
stepTimings: timings.steps,
|
|
2176
|
+
}
|
|
2177
|
+
: {}),
|
|
2178
|
+
});
|
|
2119
2179
|
}
|
|
2120
|
-
async function runAgentTurn(boundDeps, requested, hooks) {
|
|
2180
|
+
async function runAgentTurn(boundDeps, requested, hooks, clock) {
|
|
2121
2181
|
// The persona, FIRST and only when the run names one by id: its definition is read from config
|
|
2122
2182
|
// once and frozen in this checkpoint, so a run resumed after the persona was edited or removed
|
|
2123
2183
|
// keeps the prompt and allow-list it started with. A run that names none spends no position here
|
|
@@ -2177,14 +2237,28 @@ async function runAgentTurn(boundDeps, requested, hooks) {
|
|
|
2177
2237
|
// Open the run (turn) row FIRST — before the quota gate — so even a quota-rejected run is tracked
|
|
2178
2238
|
// (the runner settles it `failed`). A checkpointed step so a durable replay reuses the ONE row (no
|
|
2179
2239
|
// duplicate on resume); `run_id` is then stamped onto every message / tool call / usage row below.
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2240
|
+
//
|
|
2241
|
+
// The run's queue time is read INSIDE this checkpoint and journaled with it: the body runs once per
|
|
2242
|
+
// slice of a durable run, and only the first execution of this step is the moment it left the queue.
|
|
2243
|
+
const opened = await hooks.step('persist:run:start', async () => {
|
|
2244
|
+
const queueMs = await readQueueMs(hooks);
|
|
2245
|
+
await deps.store.recordRunStart({
|
|
2246
|
+
runId: hooks.runId,
|
|
2247
|
+
threadId: input.threadId,
|
|
2248
|
+
actor: input.actor,
|
|
2249
|
+
durable: hooks.durable ?? false,
|
|
2250
|
+
...(input.agentName !== undefined ? { agentName: input.agentName } : {}),
|
|
2251
|
+
...(input.parentRunId !== undefined ? { parentRunId: input.parentRunId } : {}),
|
|
2252
|
+
...(queueMs !== undefined ? { queueMs } : {}),
|
|
2253
|
+
});
|
|
2254
|
+
return queueMs !== undefined ? { queueMs } : {};
|
|
2255
|
+
});
|
|
2256
|
+
if (typeof opened === 'object' && opened !== null && typeof opened.queueMs === 'number') {
|
|
2257
|
+
clock.queueMs = opened.queueMs;
|
|
2258
|
+
}
|
|
2259
|
+
// Where each step's time went, filled as the steps run (or replay from their checkpoints).
|
|
2260
|
+
const stepTimings = [];
|
|
2261
|
+
clock.steps = stepTimings;
|
|
2188
2262
|
if (deps.quota !== undefined) {
|
|
2189
2263
|
const quota = deps.quota;
|
|
2190
2264
|
const state = await hooks.step('quota:check', () => quota.check(input.actor.id, deps.day));
|
|
@@ -2351,8 +2425,9 @@ async function runAgentTurn(boundDeps, requested, hooks) {
|
|
|
2351
2425
|
// placeholder faria a linha parecer precificável e sair como $0.00 — pior do que a ausência.
|
|
2352
2426
|
if (retrieved.usage !== undefined && retrieved.usage.inputTokens > 0) {
|
|
2353
2427
|
const embeddingUsage = retrieved.usage;
|
|
2354
|
-
await hooks.step('persist:usage:embedding', () => deps.store
|
|
2428
|
+
await hooks.step('persist:usage:embedding', () => recordUsageRow(deps.store, {
|
|
2355
2429
|
threadId: input.threadId,
|
|
2430
|
+
...usageAttribution(input),
|
|
2356
2431
|
actorRef: input.actor.id,
|
|
2357
2432
|
runId: hooks.runId,
|
|
2358
2433
|
modelId: embeddingUsage.modelId ?? 'unknown',
|
|
@@ -2457,72 +2532,86 @@ async function runAgentTurn(boundDeps, requested, hooks) {
|
|
|
2457
2532
|
// Stamped from THIS process's registry, which is the one that built `tools` for the call above —
|
|
2458
2533
|
// and stamped INSIDE the checkpoint, so the kinds are journaled with the calls they describe
|
|
2459
2534
|
// rather than re-derived by whatever process replays this turn.
|
|
2460
|
-
let turn = await hooks.step(`llm:${i}`, async () =>
|
|
2461
|
-
const
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2535
|
+
let turn = await hooks.step(`llm:${i}`, async () => {
|
|
2536
|
+
const timed = await timedSpan('llm.turn', hooks.runId, { runId: hooks.runId, step: i }, async () => {
|
|
2537
|
+
const incremental = gateMode === 'incremental'
|
|
2538
|
+
? createIncrementalGate({
|
|
2539
|
+
processors: outputProcessors,
|
|
2540
|
+
ctx: processorContext(i),
|
|
2541
|
+
lookbackChars: gateLookback,
|
|
2542
|
+
writer,
|
|
2543
|
+
})
|
|
2544
|
+
: undefined;
|
|
2545
|
+
const buffer = gateMode === 'whole' ? createFrameBuffer() : undefined;
|
|
2546
|
+
// Written from inside this checkpoint, like every frame the model streams, so a replay
|
|
2547
|
+
// that returns the journaled turn never opens the step twice.
|
|
2548
|
+
await writer.write({ t: 'event', event: { kind: 'step-start' } });
|
|
2549
|
+
const announced = announcedToolCalls(incremental?.writer ?? buffer?.writer ?? writer);
|
|
2550
|
+
// A tool that previews its input (genui's streaming tree) gets partial `ui` frames
|
|
2551
|
+
// while the model writes its arguments. Added BELOW the observer: a preview is shown,
|
|
2552
|
+
// never part of what the turn persists.
|
|
2553
|
+
const previews = previewToolInputs(announced.writer, async (name, toolCallId) => tools.some((definition) => definition.name === name)
|
|
2554
|
+
? deps.registry.previewInput(name, {
|
|
2555
|
+
actor: input.actor,
|
|
2556
|
+
threadId: input.threadId,
|
|
2557
|
+
toolCallId,
|
|
2558
|
+
...(input.agentName !== undefined ? { agentName: input.agentName } : {}),
|
|
2559
|
+
...(input.uiCapabilities !== undefined
|
|
2560
|
+
? { uiCapabilities: input.uiCapabilities }
|
|
2561
|
+
: {}),
|
|
2562
|
+
channel: turnChannel(input.pageContext),
|
|
2563
|
+
})
|
|
2564
|
+
: undefined);
|
|
2565
|
+
// Reasoning and pushed UI are read off the frames INSIDE this checkpoint, so they are
|
|
2566
|
+
// journaled with the turn and a replay persists the same thinking instead of none.
|
|
2567
|
+
const frames = observeTurnFrames(previews.writer);
|
|
2568
|
+
const result = withTurnFrames(await abortableModelCall(hooks, (signal) => deps.model.runTurn({
|
|
2569
|
+
system: prompt.system,
|
|
2570
|
+
messages: prompt.messages,
|
|
2571
|
+
tools,
|
|
2572
|
+
sink: frames.writer,
|
|
2573
|
+
...signal,
|
|
2574
|
+
})), frames.summary());
|
|
2575
|
+
// A provider that streams no tool frames still gets its calls announced — an outcome
|
|
2576
|
+
// for a call the client never saw announced drops the whole message there.
|
|
2577
|
+
await announced.announceRest(result.toolCalls, tools);
|
|
2578
|
+
// Journaled with the turn, so whichever process settles the calls withdraws the same
|
|
2579
|
+
// previews the stream showed.
|
|
2580
|
+
const shownPreviews = previews.shown();
|
|
2581
|
+
const previewed = shownPreviews.length > 0 ? { previews: shownPreviews } : {};
|
|
2582
|
+
if (incremental !== undefined) {
|
|
2583
|
+
await incremental.settled();
|
|
2584
|
+
const refusal = incremental.rejection();
|
|
2585
|
+
return {
|
|
2586
|
+
...result,
|
|
2587
|
+
...previewed,
|
|
2588
|
+
releasedText: incremental.released(),
|
|
2589
|
+
...(refusal !== undefined ? { gateRejection: refusal } : {}),
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
return buffer === undefined
|
|
2593
|
+
? { ...result, ...previewed }
|
|
2594
|
+
: { ...result, ...previewed, bufferedFrames: buffer.frames() };
|
|
2595
|
+
}, (result) => ({
|
|
2596
|
+
...(result.modelId !== undefined ? { modelId: result.modelId } : {}),
|
|
2597
|
+
inputTokens: result.usage.inputTokens,
|
|
2598
|
+
outputTokens: result.usage.outputTokens,
|
|
2599
|
+
textLength: result.text.length,
|
|
2600
|
+
toolCalls: result.toolCalls.length,
|
|
2601
|
+
}));
|
|
2602
|
+
// The span's own duration rides the journaled turn, so a replay counts the call once.
|
|
2603
|
+
return {
|
|
2604
|
+
...(await stampToolKinds(timed.value, { ...deps, actionApprovalMode: thread.actionApprovalMode ?? 'blocking' }, input, hooks)),
|
|
2605
|
+
modelMs: Math.round(timed.durationMs),
|
|
2606
|
+
};
|
|
2607
|
+
});
|
|
2608
|
+
const stepTiming = {
|
|
2609
|
+
step: i,
|
|
2610
|
+
...(typeof turn.modelMs === 'number' ? { modelMs: turn.modelMs } : {}),
|
|
2611
|
+
tools: [],
|
|
2612
|
+
toolsMs: 0,
|
|
2613
|
+
};
|
|
2614
|
+
stepTimings.push(stepTiming);
|
|
2526
2615
|
// The gate runs before ANYTHING downstream: before the held frames reach the subscriber, before
|
|
2527
2616
|
// the assistant message is persisted, before this text becomes the next step's context.
|
|
2528
2617
|
let rejection;
|
|
@@ -2576,8 +2665,9 @@ async function runAgentTurn(boundDeps, requested, hooks) {
|
|
|
2576
2665
|
// `resolveModelPrice` e não `priceByModel.get`: o provider responde com o snapshot datado
|
|
2577
2666
|
// (`gpt-4o-mini-2024-07-18`) e o operador precifica o alias (`gpt-4o-mini`). Ver o helper.
|
|
2578
2667
|
const costUsd = resolveCostUsd(turn.usage, turn.costUsd, resolveModelPrice(priceByModel, resolvedModelId));
|
|
2579
|
-
await hooks.step(`persist:usage:${i}`, () => deps.store
|
|
2668
|
+
await hooks.step(`persist:usage:${i}`, () => recordUsageRow(deps.store, {
|
|
2580
2669
|
threadId: input.threadId,
|
|
2670
|
+
...usageAttribution(input),
|
|
2581
2671
|
actorRef: input.actor.id,
|
|
2582
2672
|
runId: hooks.runId,
|
|
2583
2673
|
modelId: resolvedModelId,
|
|
@@ -2772,6 +2862,7 @@ async function runAgentTurn(boundDeps, requested, hooks) {
|
|
|
2772
2862
|
actionApprovalMode: thread.actionApprovalMode ?? 'blocking',
|
|
2773
2863
|
...(skillOffer !== undefined ? { skills: skillOffer } : {}),
|
|
2774
2864
|
...(memoryDigest !== undefined ? { memory: memoryDigest } : {}),
|
|
2865
|
+
toolTimings: [],
|
|
2775
2866
|
};
|
|
2776
2867
|
// A model routinely asks for several tools at once, and running them back to back makes the
|
|
2777
2868
|
// turn cost their sum. Overlapping them is safe here because a checkpoint position is handed
|
|
@@ -2817,6 +2908,16 @@ async function runAgentTurn(boundDeps, requested, hooks) {
|
|
|
2817
2908
|
results.push(await runClaimedToolCall(turnCalls, await claimToolCall(turnCalls, call)));
|
|
2818
2909
|
}
|
|
2819
2910
|
}
|
|
2911
|
+
// Settled, in call order: the step's tool time, parallel calls counted once.
|
|
2912
|
+
const ranTools = turnCalls.toolTimings ?? [];
|
|
2913
|
+
const order = new Map(turn.toolCalls.map((call, index) => [call.id, index]));
|
|
2914
|
+
ranTools.sort((left, right) => (order.get(left.toolCallId) ?? 0) - (order.get(right.toolCallId) ?? 0));
|
|
2915
|
+
stepTiming.tools = ranTools.map((entry) => ({
|
|
2916
|
+
toolCallId: entry.toolCallId,
|
|
2917
|
+
toolName: entry.toolName,
|
|
2918
|
+
ms: entry.ms,
|
|
2919
|
+
}));
|
|
2920
|
+
stepTiming.toolsMs = toolsWallClockMs(ranTools);
|
|
2820
2921
|
// The outputs land on the assistant message that made the calls. A reader reopening the thread
|
|
2821
2922
|
// pairs a call with its result off THAT message, so results that only reached the tool-call
|
|
2822
2923
|
// table leave every tool in the turn rendering as one still running.
|
|
@@ -2956,6 +3057,7 @@ async function runAgentTurn(boundDeps, requested, hooks) {
|
|
|
2956
3057
|
}
|
|
2957
3058
|
// Every text frame of the turn has gone out by now, so its time to first token is known.
|
|
2958
3059
|
const firstTextAt = firstText.firstTextAt();
|
|
3060
|
+
const timings = rollupRunTimings(stepTimings);
|
|
2959
3061
|
// Settle the run `completed` with its rollup. Normal completion only — the loop never records a
|
|
2960
3062
|
// failure (it doesn't catch its own crash; that's the runner's job). A checkpointed step so a
|
|
2961
3063
|
// resumed durable replay settles the ONE row exactly once, and `recordRunEnd` is first-terminal so
|
|
@@ -2970,6 +3072,7 @@ async function runAgentTurn(boundDeps, requested, hooks) {
|
|
|
2970
3072
|
...(hasCost ? { costUsd: totalCost } : {}),
|
|
2971
3073
|
...(firstTextAt !== undefined ? { ttftMs: firstTextAt - turnStartedAt } : {}),
|
|
2972
3074
|
...(lastModelId !== undefined ? { modelId: lastModelId } : {}),
|
|
3075
|
+
timings,
|
|
2973
3076
|
}));
|
|
2974
3077
|
await writer.end();
|
|
2975
3078
|
publishAgentRunFinished({
|
|
@@ -2982,6 +3085,12 @@ async function runAgentTurn(boundDeps, requested, hooks) {
|
|
|
2982
3085
|
...(firstTextAt !== undefined ? { ttftMs: firstTextAt - turnStartedAt } : {}),
|
|
2983
3086
|
...(hasCost ? { costUsd: totalCost } : {}),
|
|
2984
3087
|
...(lastModelId !== undefined ? { modelId: lastModelId } : {}),
|
|
3088
|
+
...(clock.queueMs !== undefined ? { queueMs: clock.queueMs } : {}),
|
|
3089
|
+
modelMs: timings.modelMs,
|
|
3090
|
+
toolsMs: timings.toolsMs,
|
|
3091
|
+
modelCalls: timings.modelCalls,
|
|
3092
|
+
toolCalls: timings.toolCalls,
|
|
3093
|
+
stepTimings: timings.steps,
|
|
2985
3094
|
...runContext(input),
|
|
2986
3095
|
});
|
|
2987
3096
|
return { text: lastText, ...(structured !== undefined ? { object: structured } : {}) };
|