@agentfield/sdk 0.1.110 → 0.1.111-rc.2
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.d.ts +486 -94
- package/dist/index.js +597 -57
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/triggers/fixtures/cron.json +4 -0
- package/src/triggers/fixtures/generic_bearer.json +11 -0
- package/src/triggers/fixtures/generic_hmac.json +14 -0
- package/src/triggers/fixtures/github.json +42 -0
- package/src/triggers/fixtures/slack.json +17 -0
- package/src/triggers/fixtures/stripe.json +28 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs, { promises } from 'fs';
|
|
1
|
+
import fs, { promises, readFileSync } from 'fs';
|
|
2
2
|
import * as path2 from 'path';
|
|
3
|
-
import path2__default, { resolve } from 'path';
|
|
3
|
+
import path2__default, { resolve, dirname } from 'path';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import express from 'express';
|
|
@@ -25,6 +25,7 @@ import { Buffer as Buffer$1 } from 'buffer';
|
|
|
25
25
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
26
26
|
import { generateKeyPair, exportJWK, importJWK, CompactEncrypt, compactDecrypt } from 'jose';
|
|
27
27
|
import { readFile } from 'fs/promises';
|
|
28
|
+
import { fileURLToPath } from 'url';
|
|
28
29
|
|
|
29
30
|
var __defProp = Object.defineProperty;
|
|
30
31
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -154,14 +155,14 @@ function getZodConverter() {
|
|
|
154
155
|
}
|
|
155
156
|
return zodConverter;
|
|
156
157
|
}
|
|
157
|
-
function
|
|
158
|
+
function isRecord2(value) {
|
|
158
159
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
159
160
|
}
|
|
160
161
|
function hasJsonSchema(value) {
|
|
161
|
-
return
|
|
162
|
+
return isRecord2(value) && typeof value.jsonSchema === "function";
|
|
162
163
|
}
|
|
163
164
|
function hasParse(value) {
|
|
164
|
-
return
|
|
165
|
+
return isRecord2(value) && typeof value.parse === "function";
|
|
165
166
|
}
|
|
166
167
|
function estimateTokens(text2) {
|
|
167
168
|
return Math.floor(text2.length / 4);
|
|
@@ -190,7 +191,7 @@ function getSchemaPath(cwd) {
|
|
|
190
191
|
return path2__default.join(cwd, SCHEMA_FILENAME);
|
|
191
192
|
}
|
|
192
193
|
function schemaToJsonSchema(schema) {
|
|
193
|
-
if (
|
|
194
|
+
if (isRecord2(schema)) {
|
|
194
195
|
if ("type" in schema || "properties" in schema || "$schema" in schema) {
|
|
195
196
|
return schema;
|
|
196
197
|
}
|
|
@@ -338,7 +339,7 @@ var claude_exports = {};
|
|
|
338
339
|
__export(claude_exports, {
|
|
339
340
|
ClaudeCodeProvider: () => ClaudeCodeProvider
|
|
340
341
|
});
|
|
341
|
-
function
|
|
342
|
+
function isRecord3(value) {
|
|
342
343
|
return typeof value === "object" && value !== null;
|
|
343
344
|
}
|
|
344
345
|
function getString(record, key) {
|
|
@@ -382,10 +383,16 @@ var init_claude = __esm({
|
|
|
382
383
|
let totalCost;
|
|
383
384
|
let numTurns = 0;
|
|
384
385
|
let sessionId = "";
|
|
386
|
+
let usageObj;
|
|
387
|
+
let inputTokens;
|
|
388
|
+
let outputTokens;
|
|
389
|
+
let cacheReadTokens;
|
|
390
|
+
let cacheCreationTokens;
|
|
391
|
+
let modelName;
|
|
385
392
|
const startApi = Date.now();
|
|
386
393
|
try {
|
|
387
394
|
for await (const msg of sdk.query({ prompt, options: agentOptions })) {
|
|
388
|
-
const msgObj =
|
|
395
|
+
const msgObj = isRecord3(msg) ? msg : { raw: String(msg) };
|
|
389
396
|
messages.push(msgObj);
|
|
390
397
|
const msgType = getString(msgObj, "type") ?? "";
|
|
391
398
|
if (msgType === "result") {
|
|
@@ -404,16 +411,28 @@ var init_claude = __esm({
|
|
|
404
411
|
}
|
|
405
412
|
const turns = getNumber(msgObj, "num_turns");
|
|
406
413
|
numTurns = turns === void 0 ? messages.length : Math.trunc(turns);
|
|
407
|
-
|
|
414
|
+
if (isRecord3(msgObj.usage)) {
|
|
415
|
+
usageObj = msgObj.usage;
|
|
416
|
+
inputTokens = getNumber(usageObj, "input_tokens");
|
|
417
|
+
outputTokens = getNumber(usageObj, "output_tokens");
|
|
418
|
+
cacheReadTokens = getNumber(usageObj, "cache_read_input_tokens");
|
|
419
|
+
cacheCreationTokens = getNumber(usageObj, "cache_creation_input_tokens");
|
|
420
|
+
}
|
|
421
|
+
modelName = getString(msgObj, "model") ?? modelName;
|
|
422
|
+
} else if (msgType === "assistant") {
|
|
423
|
+
if (modelName === void 0 && isRecord3(msgObj.message)) {
|
|
424
|
+
modelName = getString(msgObj.message, "model");
|
|
425
|
+
}
|
|
426
|
+
if (resultText !== void 0) continue;
|
|
408
427
|
let content = msgObj.content;
|
|
409
|
-
if (content === void 0 &&
|
|
428
|
+
if (content === void 0 && isRecord3(msgObj.message)) {
|
|
410
429
|
content = msgObj.message.content;
|
|
411
430
|
}
|
|
412
431
|
if (typeof content === "string") {
|
|
413
432
|
resultText = content;
|
|
414
433
|
} else if (Array.isArray(content)) {
|
|
415
434
|
for (const block of content) {
|
|
416
|
-
if (
|
|
435
|
+
if (isRecord3(block) && block.type === "text" && typeof block.text === "string") {
|
|
417
436
|
resultText = block.text;
|
|
418
437
|
}
|
|
419
438
|
}
|
|
@@ -427,7 +446,13 @@ var init_claude = __esm({
|
|
|
427
446
|
durationApiMs: Date.now() - startApi,
|
|
428
447
|
numTurns,
|
|
429
448
|
totalCostUsd: totalCost,
|
|
430
|
-
sessionId
|
|
449
|
+
sessionId,
|
|
450
|
+
usage: usageObj,
|
|
451
|
+
inputTokens,
|
|
452
|
+
outputTokens,
|
|
453
|
+
cacheReadTokens,
|
|
454
|
+
cacheCreationTokens,
|
|
455
|
+
model: modelName
|
|
431
456
|
}),
|
|
432
457
|
isError: false
|
|
433
458
|
});
|
|
@@ -454,7 +479,7 @@ function resolveIdleMs(idleSeconds) {
|
|
|
454
479
|
return seconds > 0 ? seconds * 1e3 : void 0;
|
|
455
480
|
}
|
|
456
481
|
function runCli(cmd, options) {
|
|
457
|
-
return new Promise((
|
|
482
|
+
return new Promise((resolve3, reject) => {
|
|
458
483
|
const [bin, ...args] = cmd;
|
|
459
484
|
const env = { ...process.env, ...options?.env };
|
|
460
485
|
applyOpenRouterAttributionEnv(env);
|
|
@@ -514,7 +539,7 @@ function runCli(cmd, options) {
|
|
|
514
539
|
}
|
|
515
540
|
settled = true;
|
|
516
541
|
cleanup();
|
|
517
|
-
|
|
542
|
+
resolve3({ stdout, stderr, exitCode: code ?? 0 });
|
|
518
543
|
});
|
|
519
544
|
proc.on("error", (err) => {
|
|
520
545
|
if (settled) {
|
|
@@ -841,6 +866,10 @@ var runner_exports = {};
|
|
|
841
866
|
__export(runner_exports, {
|
|
842
867
|
HarnessRunner: () => HarnessRunner
|
|
843
868
|
});
|
|
869
|
+
function tokenMetrics(raw) {
|
|
870
|
+
const { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, model } = raw.metrics;
|
|
871
|
+
return { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, model };
|
|
872
|
+
}
|
|
844
873
|
var TRANSIENT_PATTERNS, HarnessRunner;
|
|
845
874
|
var init_runner = __esm({
|
|
846
875
|
"src/harness/runner.ts"() {
|
|
@@ -891,7 +920,8 @@ var init_runner = __esm({
|
|
|
891
920
|
numTurns: raw.metrics.numTurns,
|
|
892
921
|
durationMs: Date.now() - startTime,
|
|
893
922
|
sessionId: raw.metrics.sessionId,
|
|
894
|
-
messages: raw.messages
|
|
923
|
+
messages: raw.messages,
|
|
924
|
+
...tokenMetrics(raw)
|
|
895
925
|
});
|
|
896
926
|
} finally {
|
|
897
927
|
if (schema !== void 0) {
|
|
@@ -984,7 +1014,8 @@ var init_runner = __esm({
|
|
|
984
1014
|
numTurns: raw.metrics.numTurns,
|
|
985
1015
|
durationMs: Date.now() - startTime,
|
|
986
1016
|
sessionId: raw.metrics.sessionId,
|
|
987
|
-
messages: raw.messages
|
|
1017
|
+
messages: raw.messages,
|
|
1018
|
+
...tokenMetrics(raw)
|
|
988
1019
|
});
|
|
989
1020
|
}
|
|
990
1021
|
return createHarnessResult({
|
|
@@ -995,7 +1026,8 @@ var init_runner = __esm({
|
|
|
995
1026
|
numTurns: raw.metrics.numTurns,
|
|
996
1027
|
durationMs: Date.now() - startTime,
|
|
997
1028
|
sessionId: raw.metrics.sessionId,
|
|
998
|
-
messages: raw.messages
|
|
1029
|
+
messages: raw.messages,
|
|
1030
|
+
...tokenMetrics(raw)
|
|
999
1031
|
});
|
|
1000
1032
|
}
|
|
1001
1033
|
async buildProvider(providerName, options) {
|
|
@@ -1008,8 +1040,8 @@ var init_runner = __esm({
|
|
|
1008
1040
|
return base + jitter;
|
|
1009
1041
|
}
|
|
1010
1042
|
sleep(delaySeconds) {
|
|
1011
|
-
return new Promise((
|
|
1012
|
-
setTimeout(
|
|
1043
|
+
return new Promise((resolve3) => {
|
|
1044
|
+
setTimeout(resolve3, Math.max(0, delaySeconds) * 1e3);
|
|
1013
1045
|
});
|
|
1014
1046
|
}
|
|
1015
1047
|
};
|
|
@@ -1200,8 +1232,8 @@ var PauseManager = class {
|
|
|
1200
1232
|
return existing.promise;
|
|
1201
1233
|
}
|
|
1202
1234
|
let resolveFn;
|
|
1203
|
-
const promise = new Promise((
|
|
1204
|
-
resolveFn =
|
|
1235
|
+
const promise = new Promise((resolve3) => {
|
|
1236
|
+
resolveFn = resolve3;
|
|
1205
1237
|
});
|
|
1206
1238
|
this.pending.set(approvalRequestId, { resolve: resolveFn, promise });
|
|
1207
1239
|
if (executionId) {
|
|
@@ -1423,7 +1455,7 @@ var ApprovalClient = class {
|
|
|
1423
1455
|
}
|
|
1424
1456
|
};
|
|
1425
1457
|
function sleep(ms) {
|
|
1426
|
-
return new Promise((
|
|
1458
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
1427
1459
|
}
|
|
1428
1460
|
|
|
1429
1461
|
// src/triggers/factories.ts
|
|
@@ -1481,6 +1513,205 @@ function triggerToPayload(trigger) {
|
|
|
1481
1513
|
const _exhaustive = trigger;
|
|
1482
1514
|
throw new TypeError(`Unknown trigger kind: ${_exhaustive}`);
|
|
1483
1515
|
}
|
|
1516
|
+
|
|
1517
|
+
// src/triggers/dispatch.ts
|
|
1518
|
+
function isTriggerEnvelope(body) {
|
|
1519
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return false;
|
|
1520
|
+
const obj = body;
|
|
1521
|
+
if (!("event" in obj && "_meta" in obj)) return false;
|
|
1522
|
+
const meta = obj._meta;
|
|
1523
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return false;
|
|
1524
|
+
return "trigger_id" in meta;
|
|
1525
|
+
}
|
|
1526
|
+
function unwrapEnvelope(body) {
|
|
1527
|
+
if (!isTriggerEnvelope(body)) {
|
|
1528
|
+
return { input: body };
|
|
1529
|
+
}
|
|
1530
|
+
const meta = body._meta;
|
|
1531
|
+
let receivedAt;
|
|
1532
|
+
try {
|
|
1533
|
+
receivedAt = new Date(meta.received_at);
|
|
1534
|
+
if (isNaN(receivedAt.getTime())) {
|
|
1535
|
+
receivedAt = /* @__PURE__ */ new Date();
|
|
1536
|
+
}
|
|
1537
|
+
} catch {
|
|
1538
|
+
receivedAt = /* @__PURE__ */ new Date();
|
|
1539
|
+
}
|
|
1540
|
+
const triggerContext = {
|
|
1541
|
+
triggerId: meta.trigger_id,
|
|
1542
|
+
source: meta.source,
|
|
1543
|
+
eventType: meta.event_type,
|
|
1544
|
+
eventId: meta.event_id,
|
|
1545
|
+
idempotencyKey: meta.idempotency_key,
|
|
1546
|
+
receivedAt,
|
|
1547
|
+
vcId: meta.vc_id
|
|
1548
|
+
};
|
|
1549
|
+
return {
|
|
1550
|
+
input: body.event,
|
|
1551
|
+
triggerContext
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
function applyTriggerTransform(triggerContext, bindings, input) {
|
|
1555
|
+
if (!bindings || bindings.length === 0) return input;
|
|
1556
|
+
let bestMatch;
|
|
1557
|
+
let bestSpecificity = -1;
|
|
1558
|
+
for (const binding of bindings) {
|
|
1559
|
+
if (binding.kind !== "event") continue;
|
|
1560
|
+
if (binding.spec.source !== triggerContext.source) continue;
|
|
1561
|
+
const types = binding.spec.types ?? [];
|
|
1562
|
+
if (types.length > 0) {
|
|
1563
|
+
const matched = types.some(
|
|
1564
|
+
(t) => triggerContext.eventType === t || triggerContext.eventType.startsWith(t + ".")
|
|
1565
|
+
);
|
|
1566
|
+
if (!matched) continue;
|
|
1567
|
+
if (1 > bestSpecificity) {
|
|
1568
|
+
bestMatch = binding;
|
|
1569
|
+
bestSpecificity = 1;
|
|
1570
|
+
}
|
|
1571
|
+
} else {
|
|
1572
|
+
if (0 > bestSpecificity) {
|
|
1573
|
+
bestMatch = binding;
|
|
1574
|
+
bestSpecificity = 0;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
if (bestMatch?.spec.transform) {
|
|
1579
|
+
try {
|
|
1580
|
+
return bestMatch.spec.transform(input);
|
|
1581
|
+
} catch {
|
|
1582
|
+
return input;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
return input;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// src/usage/costTracker.ts
|
|
1589
|
+
var USAGE_ENVELOPE_KEY = "__agentfield_usage__";
|
|
1590
|
+
function deriveProvider(model) {
|
|
1591
|
+
if (!model) return null;
|
|
1592
|
+
const slug = String(model).trim();
|
|
1593
|
+
const slash = slug.indexOf("/");
|
|
1594
|
+
if (slash < 0) return null;
|
|
1595
|
+
return slug.slice(0, slash).toLowerCase() || null;
|
|
1596
|
+
}
|
|
1597
|
+
function toCount(value) {
|
|
1598
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
1599
|
+
return Math.trunc(value);
|
|
1600
|
+
}
|
|
1601
|
+
function toCost(value) {
|
|
1602
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
1603
|
+
return value;
|
|
1604
|
+
}
|
|
1605
|
+
function round6(value) {
|
|
1606
|
+
return Math.round(value * 1e6) / 1e6;
|
|
1607
|
+
}
|
|
1608
|
+
var CostTracker = class {
|
|
1609
|
+
entries = [];
|
|
1610
|
+
/**
|
|
1611
|
+
* Record a single call's usage.
|
|
1612
|
+
*
|
|
1613
|
+
* Cost is optional: a call with known token counts but unknown price is
|
|
1614
|
+
* still recorded (`costUsd: null`) so tokens are never discarded.
|
|
1615
|
+
*/
|
|
1616
|
+
record(init) {
|
|
1617
|
+
this.entries.push({
|
|
1618
|
+
model: init.model,
|
|
1619
|
+
inputTokens: toCount(init.inputTokens),
|
|
1620
|
+
outputTokens: toCount(init.outputTokens),
|
|
1621
|
+
totalTokens: toCount(init.totalTokens),
|
|
1622
|
+
costUsd: toCost(init.costUsd),
|
|
1623
|
+
reasonerName: init.reasonerName ?? null,
|
|
1624
|
+
source: init.source ?? "llm",
|
|
1625
|
+
provider: init.provider ?? deriveProvider(init.model),
|
|
1626
|
+
harness: init.harness ?? null,
|
|
1627
|
+
cacheReadTokens: toCount(init.cacheReadTokens),
|
|
1628
|
+
cacheCreationTokens: toCount(init.cacheCreationTokens),
|
|
1629
|
+
costSource: init.costSource ?? null
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
/** Total accumulated cost in USD (unknown costs count as zero). */
|
|
1633
|
+
get totalCostUsd() {
|
|
1634
|
+
return this.entries.reduce((sum, e) => sum + (e.costUsd ?? 0), 0);
|
|
1635
|
+
}
|
|
1636
|
+
/** Total tokens used across all calls (per-entry total, no fallback). */
|
|
1637
|
+
get totalTokens() {
|
|
1638
|
+
return this.entries.reduce((sum, e) => sum + e.totalTokens, 0);
|
|
1639
|
+
}
|
|
1640
|
+
/** Number of calls tracked. */
|
|
1641
|
+
get callCount() {
|
|
1642
|
+
return this.entries.length;
|
|
1643
|
+
}
|
|
1644
|
+
get hasEntries() {
|
|
1645
|
+
return this.entries.length > 0;
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Return the transport contract form attached to execution envelopes.
|
|
1649
|
+
*
|
|
1650
|
+
* Matches the Python SDK's `CostTracker.serialize()` byte-for-byte in shape:
|
|
1651
|
+
* unset string fields serialize as null, `total_cost_usd` is null when no
|
|
1652
|
+
* entry had a known cost, and a per-entry `total_tokens` of zero falls back
|
|
1653
|
+
* to input + output.
|
|
1654
|
+
*/
|
|
1655
|
+
serialize() {
|
|
1656
|
+
const entries = [];
|
|
1657
|
+
let totalInput = 0;
|
|
1658
|
+
let totalOutput = 0;
|
|
1659
|
+
let totalTokens = 0;
|
|
1660
|
+
let totalCost = 0;
|
|
1661
|
+
let anyCost = false;
|
|
1662
|
+
for (const e of this.entries) {
|
|
1663
|
+
const entryTotal = e.totalTokens || e.inputTokens + e.outputTokens;
|
|
1664
|
+
entries.push({
|
|
1665
|
+
source: e.source,
|
|
1666
|
+
provider: e.provider,
|
|
1667
|
+
model: e.model,
|
|
1668
|
+
harness: e.harness,
|
|
1669
|
+
reasoner: e.reasonerName,
|
|
1670
|
+
input_tokens: e.inputTokens,
|
|
1671
|
+
output_tokens: e.outputTokens,
|
|
1672
|
+
cache_read_tokens: e.cacheReadTokens,
|
|
1673
|
+
cache_creation_tokens: e.cacheCreationTokens,
|
|
1674
|
+
total_tokens: entryTotal,
|
|
1675
|
+
cost_usd: e.costUsd,
|
|
1676
|
+
cost_source: e.costSource
|
|
1677
|
+
});
|
|
1678
|
+
totalInput += e.inputTokens;
|
|
1679
|
+
totalOutput += e.outputTokens;
|
|
1680
|
+
totalTokens += entryTotal;
|
|
1681
|
+
if (e.costUsd !== null) {
|
|
1682
|
+
totalCost += e.costUsd;
|
|
1683
|
+
anyCost = true;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
return {
|
|
1687
|
+
total_cost_usd: anyCost ? round6(totalCost) : null,
|
|
1688
|
+
total_input_tokens: totalInput,
|
|
1689
|
+
total_output_tokens: totalOutput,
|
|
1690
|
+
total_tokens: totalTokens,
|
|
1691
|
+
entries
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
/** Clear all tracked entries. */
|
|
1695
|
+
reset() {
|
|
1696
|
+
this.entries = [];
|
|
1697
|
+
}
|
|
1698
|
+
};
|
|
1699
|
+
function usageSummaryOrNull(tracker) {
|
|
1700
|
+
if (!tracker || !tracker.hasEntries) return null;
|
|
1701
|
+
return tracker.serialize();
|
|
1702
|
+
}
|
|
1703
|
+
function isPlainObject(value) {
|
|
1704
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
1705
|
+
const proto = Object.getPrototypeOf(value);
|
|
1706
|
+
return proto === Object.prototype || proto === null;
|
|
1707
|
+
}
|
|
1708
|
+
function attachUsageToSyncResult(result, tracker) {
|
|
1709
|
+
const usage = usageSummaryOrNull(tracker);
|
|
1710
|
+
if (usage === null || !isPlainObject(result)) return result;
|
|
1711
|
+
return { ...result, [USAGE_ENVELOPE_KEY]: usage };
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// src/context/ExecutionContext.ts
|
|
1484
1715
|
var store = new AsyncLocalStorage();
|
|
1485
1716
|
var ExecutionContext = class {
|
|
1486
1717
|
input;
|
|
@@ -1488,12 +1719,21 @@ var ExecutionContext = class {
|
|
|
1488
1719
|
req;
|
|
1489
1720
|
res;
|
|
1490
1721
|
agent;
|
|
1722
|
+
/**
|
|
1723
|
+
* Per-execution LLM/harness usage accumulator. Each top-level execution
|
|
1724
|
+
* binds a fresh tracker (isolated across concurrent executions via the
|
|
1725
|
+
* AsyncLocalStorage this context lives in); nested local `agent.call()`
|
|
1726
|
+
* executions inherit the parent's tracker so their usage rolls up into the
|
|
1727
|
+
* parent's report.
|
|
1728
|
+
*/
|
|
1729
|
+
costTracker;
|
|
1491
1730
|
constructor(params) {
|
|
1492
1731
|
this.input = params.input;
|
|
1493
1732
|
this.metadata = params.metadata;
|
|
1494
1733
|
this.req = params.req;
|
|
1495
1734
|
this.res = params.res;
|
|
1496
1735
|
this.agent = params.agent;
|
|
1736
|
+
this.costTracker = params.costTracker ?? new CostTracker();
|
|
1497
1737
|
}
|
|
1498
1738
|
get logger() {
|
|
1499
1739
|
return this.agent.getExecutionLogger();
|
|
@@ -1505,6 +1745,79 @@ var ExecutionContext = class {
|
|
|
1505
1745
|
return store.getStore();
|
|
1506
1746
|
}
|
|
1507
1747
|
};
|
|
1748
|
+
|
|
1749
|
+
// src/usage/aiUsage.ts
|
|
1750
|
+
function isRecord(value) {
|
|
1751
|
+
return typeof value === "object" && value !== null;
|
|
1752
|
+
}
|
|
1753
|
+
function num(value) {
|
|
1754
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1755
|
+
}
|
|
1756
|
+
function extractTokenUsage(usage) {
|
|
1757
|
+
if (!isRecord(usage)) {
|
|
1758
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0 };
|
|
1759
|
+
}
|
|
1760
|
+
const inputDetails = isRecord(usage.inputTokenDetails) ? usage.inputTokenDetails : void 0;
|
|
1761
|
+
return {
|
|
1762
|
+
inputTokens: num(usage.inputTokens),
|
|
1763
|
+
outputTokens: num(usage.outputTokens),
|
|
1764
|
+
totalTokens: num(usage.totalTokens),
|
|
1765
|
+
// `cachedInputTokens` is the deprecated pre-details alias — keep it as a
|
|
1766
|
+
// fallback so older provider adapters still report cache reads.
|
|
1767
|
+
cacheReadTokens: num(inputDetails?.cacheReadTokens) || num(usage.cachedInputTokens),
|
|
1768
|
+
cacheCreationTokens: num(inputDetails?.cacheWriteTokens)
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
function costFromUsageRaw(usage) {
|
|
1772
|
+
if (!isRecord(usage) || !isRecord(usage.raw)) return null;
|
|
1773
|
+
const cost = usage.raw.cost;
|
|
1774
|
+
return typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null;
|
|
1775
|
+
}
|
|
1776
|
+
function costFromProviderMetadata(providerMetadata) {
|
|
1777
|
+
if (!isRecord(providerMetadata)) return null;
|
|
1778
|
+
const openrouter = providerMetadata.openrouter;
|
|
1779
|
+
if (!isRecord(openrouter) || !isRecord(openrouter.usage)) return null;
|
|
1780
|
+
const cost = openrouter.usage.cost;
|
|
1781
|
+
return typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null;
|
|
1782
|
+
}
|
|
1783
|
+
function extractProviderCostUsd(source) {
|
|
1784
|
+
if (Array.isArray(source.steps) && source.steps.length > 0) {
|
|
1785
|
+
let sum = 0;
|
|
1786
|
+
let any = false;
|
|
1787
|
+
for (const step of source.steps) {
|
|
1788
|
+
const cost = costFromUsageRaw(isRecord(step) ? step.usage : void 0);
|
|
1789
|
+
if (cost !== null) {
|
|
1790
|
+
sum += cost;
|
|
1791
|
+
any = true;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
if (any) return sum;
|
|
1795
|
+
}
|
|
1796
|
+
return costFromUsageRaw(source.totalUsage) ?? costFromUsageRaw(source.usage) ?? costFromProviderMetadata(source.providerMetadata);
|
|
1797
|
+
}
|
|
1798
|
+
function recordAiSdkUsage(params) {
|
|
1799
|
+
try {
|
|
1800
|
+
const current = ExecutionContext.getCurrent();
|
|
1801
|
+
const tracker = params.tracker ?? current?.costTracker;
|
|
1802
|
+
if (!tracker) return;
|
|
1803
|
+
const tokens = extractTokenUsage(params.source.totalUsage ?? params.source.usage);
|
|
1804
|
+
const cost = extractProviderCostUsd(params.source);
|
|
1805
|
+
const hasTokens = tokens.inputTokens > 0 || tokens.outputTokens > 0 || tokens.totalTokens > 0 || tokens.cacheReadTokens > 0 || tokens.cacheCreationTokens > 0;
|
|
1806
|
+
if (!hasTokens && cost === null) return;
|
|
1807
|
+
tracker.record({
|
|
1808
|
+
model: params.model,
|
|
1809
|
+
...tokens,
|
|
1810
|
+
costUsd: cost,
|
|
1811
|
+
costSource: cost !== null ? "provider" : null,
|
|
1812
|
+
reasonerName: params.reasonerName !== void 0 ? params.reasonerName : current?.metadata.reasonerId ?? null,
|
|
1813
|
+
source: "llm",
|
|
1814
|
+
provider: params.provider ?? void 0
|
|
1815
|
+
});
|
|
1816
|
+
} catch {
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// src/ai/ToolCalling.ts
|
|
1508
1821
|
var DEFAULT_MAX_TURNS = 10;
|
|
1509
1822
|
var DEFAULT_MAX_TOOL_CALLS = 25;
|
|
1510
1823
|
var DEFAULT_HEALTH_STATUS = void 0;
|
|
@@ -1722,9 +2035,15 @@ function wrapToolsWithObservability(toolMap, agent, trace, maxToolCalls, getCurr
|
|
|
1722
2035
|
}
|
|
1723
2036
|
return { tools: observableTools, getTotalCalls: () => totalCalls };
|
|
1724
2037
|
}
|
|
1725
|
-
async function executeToolCallLoop(agent, prompt, toolMap, config, needsLazyHydration, buildModel, options = {}) {
|
|
2038
|
+
async function executeToolCallLoop(agent, prompt, toolMap, config, needsLazyHydration, buildModel, options = {}, modelChoice) {
|
|
1726
2039
|
const maxTurns = config.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
1727
2040
|
const maxToolCalls = config.maxToolCalls ?? DEFAULT_MAX_TOOL_CALLS;
|
|
2041
|
+
const usageModel = modelChoice?.modelName ?? options.model;
|
|
2042
|
+
const recordLoopUsage = (result2) => {
|
|
2043
|
+
if (usageModel) {
|
|
2044
|
+
recordAiSdkUsage({ source: result2, model: usageModel, provider: modelChoice?.provider });
|
|
2045
|
+
}
|
|
2046
|
+
};
|
|
1728
2047
|
const trace = {
|
|
1729
2048
|
calls: [],
|
|
1730
2049
|
totalTurns: 0,
|
|
@@ -1751,6 +2070,7 @@ async function executeToolCallLoop(agent, prompt, toolMap, config, needsLazyHydr
|
|
|
1751
2070
|
stopWhen: stepCountIs(1)
|
|
1752
2071
|
// Stop after LLM's first response (tool selection)
|
|
1753
2072
|
});
|
|
2073
|
+
recordLoopUsage(selectionResult);
|
|
1754
2074
|
const selectedNames = /* @__PURE__ */ new Set();
|
|
1755
2075
|
for (const step of selectionResult.steps) {
|
|
1756
2076
|
for (const tc of step.toolCalls) {
|
|
@@ -1794,6 +2114,7 @@ async function executeToolCallLoop(agent, prompt, toolMap, config, needsLazyHydr
|
|
|
1794
2114
|
trace.totalTurns = currentTurn;
|
|
1795
2115
|
}
|
|
1796
2116
|
});
|
|
2117
|
+
recordLoopUsage(result);
|
|
1797
2118
|
trace.finalResponse = result.text;
|
|
1798
2119
|
trace.totalTurns = result.steps.length;
|
|
1799
2120
|
return { text: result.text, trace };
|
|
@@ -1821,6 +2142,13 @@ var ReasonerContext = class {
|
|
|
1821
2142
|
memory;
|
|
1822
2143
|
workflow;
|
|
1823
2144
|
did;
|
|
2145
|
+
/**
|
|
2146
|
+
* Per-execution token/cost usage accumulator. LLM calls made through
|
|
2147
|
+
* `ctx.ai()` / `ctx.aiWithTools()` and harness runs record into it
|
|
2148
|
+
* automatically; reasoner authors may also `record()` custom entries. Its
|
|
2149
|
+
* serialized summary is attached to the execution's terminal report.
|
|
2150
|
+
*/
|
|
2151
|
+
costTracker;
|
|
1824
2152
|
/**
|
|
1825
2153
|
* AbortSignal that fires when the control plane cancels this execution
|
|
1826
2154
|
* (per-execution cancel, the bottom-up cancel-tree endpoint, or any
|
|
@@ -1830,6 +2158,12 @@ var ReasonerContext = class {
|
|
|
1830
2158
|
* CPU loops, check `ctx.signal.aborted` periodically and throw.
|
|
1831
2159
|
*/
|
|
1832
2160
|
signal;
|
|
2161
|
+
/**
|
|
2162
|
+
* Trigger context populated when the reasoner was invoked by an inbound
|
|
2163
|
+
* webhook event or cron schedule. `undefined` for direct calls via
|
|
2164
|
+
* `app.call(...)` or HTTP POST without a dispatcher envelope.
|
|
2165
|
+
*/
|
|
2166
|
+
trigger;
|
|
1833
2167
|
constructor(params) {
|
|
1834
2168
|
this.input = params.input;
|
|
1835
2169
|
this.executionId = params.executionId;
|
|
@@ -1852,6 +2186,8 @@ var ReasonerContext = class {
|
|
|
1852
2186
|
this.workflow = params.workflow;
|
|
1853
2187
|
this.did = params.did;
|
|
1854
2188
|
this.signal = params.signal ?? new AbortController().signal;
|
|
2189
|
+
this.costTracker = params.costTracker ?? ExecutionContext.getCurrent()?.costTracker ?? new CostTracker();
|
|
2190
|
+
this.trigger = params.trigger;
|
|
1855
2191
|
}
|
|
1856
2192
|
ai(prompt, options) {
|
|
1857
2193
|
if (options?.tools) {
|
|
@@ -1875,6 +2211,7 @@ var ReasonerContext = class {
|
|
|
1875
2211
|
maxTurns: options.maxTurns ?? config.maxTurns ?? 10,
|
|
1876
2212
|
maxToolCalls: options.maxToolCalls ?? config.maxToolCalls ?? 25
|
|
1877
2213
|
};
|
|
2214
|
+
const modelChoice = typeof this.aiClient.resolveModelChoice === "function" ? this.aiClient.resolveModelChoice(options) : void 0;
|
|
1878
2215
|
return executeToolCallLoop(
|
|
1879
2216
|
this.agent,
|
|
1880
2217
|
prompt,
|
|
@@ -1882,7 +2219,8 @@ var ReasonerContext = class {
|
|
|
1882
2219
|
mergedConfig,
|
|
1883
2220
|
needsLazyHydration,
|
|
1884
2221
|
() => this.aiClient.getModel(options),
|
|
1885
|
-
options
|
|
2222
|
+
options,
|
|
2223
|
+
modelChoice
|
|
1886
2224
|
);
|
|
1887
2225
|
}
|
|
1888
2226
|
aiStream(prompt, options) {
|
|
@@ -1947,7 +2285,8 @@ function getCurrentContext() {
|
|
|
1947
2285
|
aiClient: agent.getAIClient(),
|
|
1948
2286
|
memory: agent.getMemoryInterface(metadata),
|
|
1949
2287
|
workflow: agent.getWorkflowReporter(metadata),
|
|
1950
|
-
did: agent.getDidInterface(metadata, input)
|
|
2288
|
+
did: agent.getDidInterface(metadata, input),
|
|
2289
|
+
costTracker: execution.costTracker
|
|
1951
2290
|
});
|
|
1952
2291
|
}
|
|
1953
2292
|
|
|
@@ -2165,7 +2504,7 @@ var StatelessRateLimiter = class {
|
|
|
2165
2504
|
}
|
|
2166
2505
|
}
|
|
2167
2506
|
async _sleep(delaySeconds) {
|
|
2168
|
-
await new Promise((
|
|
2507
|
+
await new Promise((resolve3) => setTimeout(resolve3, delaySeconds * 1e3));
|
|
2169
2508
|
}
|
|
2170
2509
|
_now() {
|
|
2171
2510
|
return Date.now() / 1e3;
|
|
@@ -2209,6 +2548,26 @@ function toError(error) {
|
|
|
2209
2548
|
|
|
2210
2549
|
// src/ai/AIClient.ts
|
|
2211
2550
|
init_openrouterAttribution();
|
|
2551
|
+
|
|
2552
|
+
// src/ai/openrouterUsage.ts
|
|
2553
|
+
function withOpenRouterUsageInclude(baseFetch) {
|
|
2554
|
+
return (input, init) => {
|
|
2555
|
+
const impl = ((...args) => globalThis.fetch(...args));
|
|
2556
|
+
try {
|
|
2557
|
+
if (init && typeof init.body === "string") {
|
|
2558
|
+
const parsed = JSON.parse(init.body);
|
|
2559
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && parsed.usage === void 0) {
|
|
2560
|
+
parsed.usage = { include: true };
|
|
2561
|
+
return impl(input, { ...init, body: JSON.stringify(parsed) });
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
} catch {
|
|
2565
|
+
}
|
|
2566
|
+
return impl(input, init);
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// src/ai/AIClient.ts
|
|
2212
2571
|
function repairJsonText(text2) {
|
|
2213
2572
|
let cleaned = text2.trim();
|
|
2214
2573
|
const codeBlockMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
@@ -2243,6 +2602,7 @@ var AIClient = class {
|
|
|
2243
2602
|
};
|
|
2244
2603
|
}
|
|
2245
2604
|
async generate(prompt, options = {}) {
|
|
2605
|
+
const { provider, modelName } = this.resolveModelChoice(options);
|
|
2246
2606
|
const model = this.buildModel(options);
|
|
2247
2607
|
if (options.schema) {
|
|
2248
2608
|
const schema = options.schema;
|
|
@@ -2257,6 +2617,7 @@ var AIClient = class {
|
|
|
2257
2617
|
experimental_repairText: async ({ text: text2 }) => repairJsonText(text2)
|
|
2258
2618
|
});
|
|
2259
2619
|
const response2 = await this.withRateLimitRetry(call2);
|
|
2620
|
+
recordAiSdkUsage({ source: response2, model: modelName, provider });
|
|
2260
2621
|
return response2.object;
|
|
2261
2622
|
}
|
|
2262
2623
|
const call = async () => generateText({
|
|
@@ -2267,8 +2628,13 @@ var AIClient = class {
|
|
|
2267
2628
|
maxOutputTokens: options.maxTokens ?? this.config.maxTokens
|
|
2268
2629
|
});
|
|
2269
2630
|
const response = await this.withRateLimitRetry(call);
|
|
2631
|
+
recordAiSdkUsage({ source: response, model: modelName, provider });
|
|
2270
2632
|
return response.text;
|
|
2271
2633
|
}
|
|
2634
|
+
// NOTE: stream() usage is deliberately NOT captured. The AI SDK's
|
|
2635
|
+
// streamResult.usage/.totalUsage promises "automatically consume the
|
|
2636
|
+
// stream": attaching to them would force full background consumption of a
|
|
2637
|
+
// stream the caller may abandon early, changing stream semantics.
|
|
2272
2638
|
async stream(prompt, options = {}) {
|
|
2273
2639
|
const model = this.buildModel(options);
|
|
2274
2640
|
const streamResult = streamText({
|
|
@@ -2307,9 +2673,19 @@ var AIClient = class {
|
|
|
2307
2673
|
getModel(options = {}) {
|
|
2308
2674
|
return this.buildModel(options);
|
|
2309
2675
|
}
|
|
2676
|
+
/**
|
|
2677
|
+
* Resolve the effective provider/model pair for a request without building
|
|
2678
|
+
* the model. Used by usage tracking to attribute token/cost entries to the
|
|
2679
|
+
* model actually called.
|
|
2680
|
+
*/
|
|
2681
|
+
resolveModelChoice(options = {}) {
|
|
2682
|
+
return {
|
|
2683
|
+
provider: options.provider ?? this.config.provider ?? "openai",
|
|
2684
|
+
modelName: options.model ?? this.config.model ?? "gpt-4o"
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2310
2687
|
buildModel(options) {
|
|
2311
|
-
const provider
|
|
2312
|
-
const modelName = options.model ?? this.config.model ?? "gpt-4o";
|
|
2688
|
+
const { provider, modelName } = this.resolveModelChoice(options);
|
|
2313
2689
|
const openRouterHeaders = this.openRouterHeaders(provider, modelName);
|
|
2314
2690
|
switch (provider) {
|
|
2315
2691
|
case "anthropic": {
|
|
@@ -2365,7 +2741,8 @@ var AIClient = class {
|
|
|
2365
2741
|
const openrouter = createOpenAI({
|
|
2366
2742
|
apiKey: this.config.apiKey,
|
|
2367
2743
|
baseURL: this.config.baseUrl ?? "https://openrouter.ai/api/v1",
|
|
2368
|
-
headers: openRouterHeaders
|
|
2744
|
+
headers: openRouterHeaders,
|
|
2745
|
+
fetch: withOpenRouterUsageInclude()
|
|
2369
2746
|
});
|
|
2370
2747
|
return openrouter.chat(modelName);
|
|
2371
2748
|
}
|
|
@@ -2382,7 +2759,7 @@ var AIClient = class {
|
|
|
2382
2759
|
const openai = createOpenAI({
|
|
2383
2760
|
apiKey: this.config.apiKey,
|
|
2384
2761
|
baseURL: this.config.baseUrl,
|
|
2385
|
-
...openRouterHeaders ? { headers: openRouterHeaders } : {}
|
|
2762
|
+
...openRouterHeaders ? { headers: openRouterHeaders, fetch: withOpenRouterUsageInclude() } : {}
|
|
2386
2763
|
});
|
|
2387
2764
|
return openai(modelName);
|
|
2388
2765
|
}
|
|
@@ -3088,6 +3465,7 @@ var AgentFieldClient = class {
|
|
|
3088
3465
|
if (payload.result !== void 0) body.result = wrapResult(payload.result);
|
|
3089
3466
|
if (payload.error !== void 0) body.error = payload.error;
|
|
3090
3467
|
if (payload.errorDetails !== void 0) body.error_details = payload.errorDetails;
|
|
3468
|
+
if (payload.usage !== void 0) body.usage = payload.usage;
|
|
3091
3469
|
const bodyStr = JSON.stringify(body);
|
|
3092
3470
|
const authHeaders = this.didAuthenticator.signRequest(Buffer.from(bodyStr));
|
|
3093
3471
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
@@ -3289,7 +3667,7 @@ var AgentFieldClient = class {
|
|
|
3289
3667
|
}
|
|
3290
3668
|
};
|
|
3291
3669
|
function sleep2(ms) {
|
|
3292
|
-
return new Promise((
|
|
3670
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3293
3671
|
}
|
|
3294
3672
|
async function safePauseCallback(cb) {
|
|
3295
3673
|
if (!cb) return;
|
|
@@ -4748,6 +5126,44 @@ var Agent = class {
|
|
|
4748
5126
|
}
|
|
4749
5127
|
return this;
|
|
4750
5128
|
}
|
|
5129
|
+
/**
|
|
5130
|
+
* Sugar for registering an event-triggered reasoner.
|
|
5131
|
+
*
|
|
5132
|
+
* Equivalent to:
|
|
5133
|
+
* ```ts
|
|
5134
|
+
* app.reasoner(name, handler, { triggers: [eventTrigger(spec)] });
|
|
5135
|
+
* ```
|
|
5136
|
+
*
|
|
5137
|
+
* The reasoner name defaults to `handler.name` when not provided.
|
|
5138
|
+
*/
|
|
5139
|
+
onEvent(spec, handler, options) {
|
|
5140
|
+
const name = spec.name || handler.name || `on_${spec.source}`;
|
|
5141
|
+
const { name: _discarded, ...triggerSpec } = spec;
|
|
5142
|
+
const binding = eventTrigger(triggerSpec);
|
|
5143
|
+
return this.reasoner(name, handler, {
|
|
5144
|
+
...options,
|
|
5145
|
+
triggers: [...options?.triggers ?? [], binding]
|
|
5146
|
+
});
|
|
5147
|
+
}
|
|
5148
|
+
/**
|
|
5149
|
+
* Sugar for registering a schedule-triggered (cron) reasoner.
|
|
5150
|
+
*
|
|
5151
|
+
* Equivalent to:
|
|
5152
|
+
* ```ts
|
|
5153
|
+
* app.reasoner(name, handler, { triggers: [scheduleTrigger({ cron })] });
|
|
5154
|
+
* ```
|
|
5155
|
+
*
|
|
5156
|
+
* The reasoner name defaults to `handler.name` when not provided.
|
|
5157
|
+
*/
|
|
5158
|
+
onSchedule(cron, handler, options) {
|
|
5159
|
+
const name = options?.name || handler.name || "on_schedule";
|
|
5160
|
+
const binding = scheduleTrigger({ cron, timezone: options?.timezone });
|
|
5161
|
+
const { name: _discarded, timezone: _tz, ...restOptions } = options ?? {};
|
|
5162
|
+
return this.reasoner(name, handler, {
|
|
5163
|
+
...restOptions,
|
|
5164
|
+
triggers: [...restOptions?.triggers ?? [], binding]
|
|
5165
|
+
});
|
|
5166
|
+
}
|
|
4751
5167
|
skill(name, handler, options) {
|
|
4752
5168
|
this.skills.register(name, handler, options);
|
|
4753
5169
|
if (options?.requireRealtimeValidation) {
|
|
@@ -4801,7 +5217,53 @@ var Agent = class {
|
|
|
4801
5217
|
}
|
|
4802
5218
|
async harness(prompt, options) {
|
|
4803
5219
|
const runner = await this.getHarnessRunner();
|
|
4804
|
-
|
|
5220
|
+
const result = await runner.run(prompt, options ?? {});
|
|
5221
|
+
this.recordHarnessUsage(result, options);
|
|
5222
|
+
return result;
|
|
5223
|
+
}
|
|
5224
|
+
/**
|
|
5225
|
+
* Record a harness run's token/cost usage into the current execution's
|
|
5226
|
+
* tracker. Mirrors the Python SDK's `_record_harness_usage`: a no-op when
|
|
5227
|
+
* the harness reported neither tokens nor cost (the common case for
|
|
5228
|
+
* providers that don't expose usage) so empty entries are never emitted;
|
|
5229
|
+
* cost is threaded even when tokens are unknown, and vice versa. Never
|
|
5230
|
+
* throws — usage capture is best-effort.
|
|
5231
|
+
*/
|
|
5232
|
+
recordHarnessUsage(result, options) {
|
|
5233
|
+
try {
|
|
5234
|
+
const current = ExecutionContext.getCurrent();
|
|
5235
|
+
const tracker = current?.costTracker;
|
|
5236
|
+
if (!tracker) return;
|
|
5237
|
+
const inputTokens = result.inputTokens ?? 0;
|
|
5238
|
+
const outputTokens = result.outputTokens ?? 0;
|
|
5239
|
+
const cacheRead = result.cacheReadTokens ?? 0;
|
|
5240
|
+
const cacheCreation = result.cacheCreationTokens ?? 0;
|
|
5241
|
+
const reportedTotal = result.totalTokens ?? 0;
|
|
5242
|
+
const cost = typeof result.costUsd === "number" && Number.isFinite(result.costUsd) ? result.costUsd : null;
|
|
5243
|
+
if (!inputTokens && !outputTokens && !cacheRead && !cacheCreation && !reportedTotal && cost === null) {
|
|
5244
|
+
return;
|
|
5245
|
+
}
|
|
5246
|
+
const providerName = options?.provider ?? this.config.harnessConfig?.provider;
|
|
5247
|
+
const harnessName = providerName ? String(providerName).replace(/-/g, "_") : null;
|
|
5248
|
+
const modelName = String(
|
|
5249
|
+
result.model ?? options?.model ?? this.config.harnessConfig?.model ?? providerName ?? "harness"
|
|
5250
|
+
);
|
|
5251
|
+
tracker.record({
|
|
5252
|
+
model: modelName,
|
|
5253
|
+
inputTokens,
|
|
5254
|
+
outputTokens,
|
|
5255
|
+
totalTokens: reportedTotal || inputTokens + outputTokens,
|
|
5256
|
+
costUsd: cost,
|
|
5257
|
+
reasonerName: current.metadata.reasonerId ?? null,
|
|
5258
|
+
source: "harness",
|
|
5259
|
+
provider: deriveProvider(modelName),
|
|
5260
|
+
harness: harnessName,
|
|
5261
|
+
cacheReadTokens: cacheRead,
|
|
5262
|
+
cacheCreationTokens: cacheCreation,
|
|
5263
|
+
costSource: cost !== null ? "provider" : null
|
|
5264
|
+
});
|
|
5265
|
+
} catch {
|
|
5266
|
+
}
|
|
4805
5267
|
}
|
|
4806
5268
|
getMemoryInterface(metadata) {
|
|
4807
5269
|
const defaultScope = this.config.memoryConfig?.defaultScope ?? "workflow";
|
|
@@ -4918,9 +5380,9 @@ var Agent = class {
|
|
|
4918
5380
|
try {
|
|
4919
5381
|
const result = await Promise.race([
|
|
4920
5382
|
future,
|
|
4921
|
-
new Promise((
|
|
5383
|
+
new Promise((resolve3) => {
|
|
4922
5384
|
timer = setTimeout(() => {
|
|
4923
|
-
|
|
5385
|
+
resolve3(
|
|
4924
5386
|
new ApprovalResult({
|
|
4925
5387
|
decision: "expired",
|
|
4926
5388
|
feedback: "timed out waiting for approval",
|
|
@@ -4973,8 +5435,8 @@ var Agent = class {
|
|
|
4973
5435
|
const port = this.config.port ?? 8001;
|
|
4974
5436
|
const host = this.config.host ?? "0.0.0.0";
|
|
4975
5437
|
await this.agentFieldClient.heartbeat("starting");
|
|
4976
|
-
await new Promise((
|
|
4977
|
-
this.server = this.app.listen(port, host, () =>
|
|
5438
|
+
await new Promise((resolve3, reject) => {
|
|
5439
|
+
this.server = this.app.listen(port, host, () => resolve3()).on("error", reject);
|
|
4978
5440
|
});
|
|
4979
5441
|
this.memoryEventClient.start();
|
|
4980
5442
|
this.startHeartbeat();
|
|
@@ -4984,20 +5446,27 @@ var Agent = class {
|
|
|
4984
5446
|
clearInterval(this.heartbeatTimer);
|
|
4985
5447
|
}
|
|
4986
5448
|
this.pauseManager.cancelAll();
|
|
4987
|
-
await new Promise((
|
|
5449
|
+
await new Promise((resolve3, reject) => {
|
|
4988
5450
|
this.server?.close((err) => {
|
|
4989
5451
|
if (err) reject(err);
|
|
4990
|
-
else
|
|
5452
|
+
else resolve3();
|
|
4991
5453
|
});
|
|
4992
5454
|
});
|
|
4993
5455
|
this.memoryEventClient.stop();
|
|
4994
5456
|
}
|
|
4995
5457
|
async call(target, input) {
|
|
4996
5458
|
const { agentId, name } = this.parseTarget(target);
|
|
4997
|
-
const
|
|
5459
|
+
const parentContext = ExecutionContext.getCurrent();
|
|
5460
|
+
const parentMetadata = parentContext?.metadata;
|
|
4998
5461
|
if (!agentId || agentId === this.config.nodeId) {
|
|
4999
5462
|
const local = this.reasoners.get(name);
|
|
5000
5463
|
if (!local) throw new Error(`Reasoner not found: ${name}`);
|
|
5464
|
+
const { input: unwrappedInput, triggerContext } = unwrapEnvelope(input);
|
|
5465
|
+
let resolvedInput = unwrappedInput;
|
|
5466
|
+
if (triggerContext) {
|
|
5467
|
+
const bindings = local.options?.triggers ?? [];
|
|
5468
|
+
resolvedInput = applyTriggerTransform(triggerContext, bindings, unwrappedInput);
|
|
5469
|
+
}
|
|
5001
5470
|
const runId2 = parentMetadata?.runId ?? parentMetadata?.executionId ?? randomUUID();
|
|
5002
5471
|
const rootWorkflowId2 = parentMetadata?.rootWorkflowId ?? parentMetadata?.workflowId ?? runId2;
|
|
5003
5472
|
const metadata = {
|
|
@@ -5012,14 +5481,18 @@ var Agent = class {
|
|
|
5012
5481
|
const dummyReq = {};
|
|
5013
5482
|
const dummyRes = {};
|
|
5014
5483
|
const execCtx = new ExecutionContext({
|
|
5015
|
-
input,
|
|
5484
|
+
input: resolvedInput,
|
|
5016
5485
|
metadata: {
|
|
5017
5486
|
...metadata,
|
|
5018
5487
|
executionId: metadata.executionId ?? randomUUID()
|
|
5019
5488
|
},
|
|
5020
5489
|
req: dummyReq,
|
|
5021
5490
|
res: dummyRes,
|
|
5022
|
-
agent: this
|
|
5491
|
+
agent: this,
|
|
5492
|
+
// Nested local calls inherit the parent's cost tracker so their LLM /
|
|
5493
|
+
// harness usage rolls up into the parent execution's usage report
|
|
5494
|
+
// (entries still carry the child reasoner's name).
|
|
5495
|
+
costTracker: parentContext?.costTracker
|
|
5023
5496
|
});
|
|
5024
5497
|
const startTime = Date.now();
|
|
5025
5498
|
this.executionLogger.system("agent.call.started", "Local agent call started", {
|
|
@@ -5069,7 +5542,7 @@ var Agent = class {
|
|
|
5069
5542
|
try {
|
|
5070
5543
|
const result = await local.handler(
|
|
5071
5544
|
new ReasonerContext({
|
|
5072
|
-
input,
|
|
5545
|
+
input: resolvedInput,
|
|
5073
5546
|
executionId: execCtx.metadata.executionId,
|
|
5074
5547
|
runId: execCtx.metadata.runId,
|
|
5075
5548
|
sessionId: execCtx.metadata.sessionId,
|
|
@@ -5088,7 +5561,9 @@ var Agent = class {
|
|
|
5088
5561
|
aiClient: this.aiClient,
|
|
5089
5562
|
memory: this.getMemoryInterface(execCtx.metadata),
|
|
5090
5563
|
workflow: this.getWorkflowReporter(execCtx.metadata),
|
|
5091
|
-
did: this.getDidInterface(execCtx.metadata,
|
|
5564
|
+
did: this.getDidInterface(execCtx.metadata, resolvedInput, name),
|
|
5565
|
+
trigger: triggerContext,
|
|
5566
|
+
costTracker: execCtx.costTracker
|
|
5092
5567
|
})
|
|
5093
5568
|
);
|
|
5094
5569
|
this.executionLogger.system("reasoner.completed", "Reasoner execution completed", {
|
|
@@ -5790,6 +6265,8 @@ var Agent = class {
|
|
|
5790
6265
|
watchdog.catch(() => {
|
|
5791
6266
|
});
|
|
5792
6267
|
const completedAt = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
6268
|
+
const usageTracker = new CostTracker();
|
|
6269
|
+
const usage = () => usageSummaryOrNull(usageTracker) ?? void 0;
|
|
5793
6270
|
try {
|
|
5794
6271
|
const result = await Promise.race([
|
|
5795
6272
|
this.runReasoner(reasoner, {
|
|
@@ -5797,7 +6274,8 @@ var Agent = class {
|
|
|
5797
6274
|
input: params.input,
|
|
5798
6275
|
metadata: params.metadata,
|
|
5799
6276
|
respond: false,
|
|
5800
|
-
controller
|
|
6277
|
+
controller,
|
|
6278
|
+
costTracker: usageTracker
|
|
5801
6279
|
}),
|
|
5802
6280
|
watchdog
|
|
5803
6281
|
]);
|
|
@@ -5806,7 +6284,8 @@ var Agent = class {
|
|
|
5806
6284
|
result,
|
|
5807
6285
|
durationMs: Date.now() - start,
|
|
5808
6286
|
completedAt: completedAt(),
|
|
5809
|
-
reasoner: reasonerName
|
|
6287
|
+
reasoner: reasonerName,
|
|
6288
|
+
usage: usage()
|
|
5810
6289
|
});
|
|
5811
6290
|
} catch (err) {
|
|
5812
6291
|
const durationMs = Date.now() - start;
|
|
@@ -5817,7 +6296,8 @@ var Agent = class {
|
|
|
5817
6296
|
errorDetails: { reason: "reasoner_timeout" },
|
|
5818
6297
|
durationMs,
|
|
5819
6298
|
completedAt: completedAt(),
|
|
5820
|
-
reasoner: reasonerName
|
|
6299
|
+
reasoner: reasonerName,
|
|
6300
|
+
usage: usage()
|
|
5821
6301
|
});
|
|
5822
6302
|
} else if (controller.signal.aborted) {
|
|
5823
6303
|
await this.agentFieldClient.reportExecutionResult(executionId, {
|
|
@@ -5826,7 +6306,8 @@ var Agent = class {
|
|
|
5826
6306
|
errorDetails: { reason: "cancelled" },
|
|
5827
6307
|
durationMs,
|
|
5828
6308
|
completedAt: completedAt(),
|
|
5829
|
-
reasoner: reasonerName
|
|
6309
|
+
reasoner: reasonerName,
|
|
6310
|
+
usage: usage()
|
|
5830
6311
|
});
|
|
5831
6312
|
} else {
|
|
5832
6313
|
await this.agentFieldClient.reportExecutionResult(executionId, {
|
|
@@ -5835,7 +6316,8 @@ var Agent = class {
|
|
|
5835
6316
|
errorDetails: err?.responseData,
|
|
5836
6317
|
durationMs,
|
|
5837
6318
|
completedAt: completedAt(),
|
|
5838
|
-
reasoner: reasonerName
|
|
6319
|
+
reasoner: reasonerName,
|
|
6320
|
+
usage: usage()
|
|
5839
6321
|
});
|
|
5840
6322
|
}
|
|
5841
6323
|
} finally {
|
|
@@ -5851,12 +6333,20 @@ var Agent = class {
|
|
|
5851
6333
|
rootWorkflowId: params.metadata.rootWorkflowId ?? params.metadata.workflowId ?? params.metadata.runId ?? params.metadata.executionId,
|
|
5852
6334
|
reasonerId: params.metadata.reasonerId ?? params.targetName
|
|
5853
6335
|
};
|
|
6336
|
+
const costTracker = params.costTracker ?? new CostTracker();
|
|
6337
|
+
const { input: unwrappedInput, triggerContext } = unwrapEnvelope(params.input);
|
|
6338
|
+
let resolvedInput = unwrappedInput;
|
|
6339
|
+
if (triggerContext) {
|
|
6340
|
+
const bindings = reasoner.options?.triggers ?? [];
|
|
6341
|
+
resolvedInput = applyTriggerTransform(triggerContext, bindings, unwrappedInput);
|
|
6342
|
+
}
|
|
5854
6343
|
const execCtx = new ExecutionContext({
|
|
5855
|
-
input:
|
|
6344
|
+
input: resolvedInput,
|
|
5856
6345
|
metadata: executionMetadata,
|
|
5857
6346
|
req,
|
|
5858
6347
|
res,
|
|
5859
|
-
agent: this
|
|
6348
|
+
agent: this,
|
|
6349
|
+
costTracker
|
|
5860
6350
|
});
|
|
5861
6351
|
const { controller, release } = this.cancelRegistry.register(
|
|
5862
6352
|
executionMetadata.executionId,
|
|
@@ -5881,7 +6371,7 @@ var Agent = class {
|
|
|
5881
6371
|
});
|
|
5882
6372
|
try {
|
|
5883
6373
|
const ctx = new ReasonerContext({
|
|
5884
|
-
input:
|
|
6374
|
+
input: resolvedInput,
|
|
5885
6375
|
executionId: executionMetadata.executionId,
|
|
5886
6376
|
runId: executionMetadata.runId,
|
|
5887
6377
|
sessionId: executionMetadata.sessionId,
|
|
@@ -5900,8 +6390,10 @@ var Agent = class {
|
|
|
5900
6390
|
aiClient: this.aiClient,
|
|
5901
6391
|
memory: this.getMemoryInterface(executionMetadata),
|
|
5902
6392
|
workflow: this.getWorkflowReporter(executionMetadata),
|
|
5903
|
-
did: this.getDidInterface(executionMetadata,
|
|
5904
|
-
signal: controller.signal
|
|
6393
|
+
did: this.getDidInterface(executionMetadata, resolvedInput, params.targetName),
|
|
6394
|
+
signal: controller.signal,
|
|
6395
|
+
trigger: triggerContext,
|
|
6396
|
+
costTracker
|
|
5905
6397
|
});
|
|
5906
6398
|
const result = await reasoner.handler(ctx);
|
|
5907
6399
|
this.executionLogger.system("reasoner.completed", "Reasoner execution completed", {
|
|
@@ -5920,7 +6412,7 @@ var Agent = class {
|
|
|
5920
6412
|
rootWorkflowId: executionMetadata.rootWorkflowId
|
|
5921
6413
|
});
|
|
5922
6414
|
if (params.respond && params.res) {
|
|
5923
|
-
params.res.json(result);
|
|
6415
|
+
params.res.json(attachUsageToSyncResult(result, costTracker));
|
|
5924
6416
|
return;
|
|
5925
6417
|
}
|
|
5926
6418
|
return result;
|
|
@@ -6146,7 +6638,7 @@ var Agent = class {
|
|
|
6146
6638
|
const timeoutMs = 5 * 60 * 1e3;
|
|
6147
6639
|
const deadline = Date.now() + timeoutMs;
|
|
6148
6640
|
while (Date.now() < deadline) {
|
|
6149
|
-
await new Promise((
|
|
6641
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
6150
6642
|
try {
|
|
6151
6643
|
const node = await this.agentFieldClient.getNode(this.config.nodeId);
|
|
6152
6644
|
const status = node?.lifecycle_status;
|
|
@@ -7575,14 +8067,62 @@ var OpenRouterMediaProvider = class {
|
|
|
7575
8067
|
}
|
|
7576
8068
|
};
|
|
7577
8069
|
function sleep3(ms) {
|
|
7578
|
-
return new Promise((
|
|
8070
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
7579
8071
|
}
|
|
7580
8072
|
|
|
7581
8073
|
// src/harness/index.ts
|
|
7582
8074
|
init_types();
|
|
7583
8075
|
init_factory();
|
|
7584
8076
|
init_runner();
|
|
8077
|
+
async function simulateTrigger(handler, options) {
|
|
8078
|
+
const body = options.body ?? {};
|
|
8079
|
+
const triggerContext = {
|
|
8080
|
+
triggerId: options.triggerId ?? `trg_sim_${randomUUID().slice(0, 12)}`,
|
|
8081
|
+
source: options.source,
|
|
8082
|
+
eventType: options.eventType ?? "",
|
|
8083
|
+
eventId: options.eventId ?? `evt_sim_${randomUUID().slice(0, 12)}`,
|
|
8084
|
+
idempotencyKey: options.idempotencyKey ?? `idem_sim_${randomUUID().slice(0, 12)}`,
|
|
8085
|
+
receivedAt: options.receivedAt ?? /* @__PURE__ */ new Date(),
|
|
8086
|
+
vcId: options.vcId
|
|
8087
|
+
};
|
|
8088
|
+
let resolvedInput = body;
|
|
8089
|
+
if (options.bindings && options.bindings.length > 0) {
|
|
8090
|
+
resolvedInput = applyTriggerTransform(triggerContext, options.bindings, body);
|
|
8091
|
+
}
|
|
8092
|
+
const ctx = {
|
|
8093
|
+
input: resolvedInput,
|
|
8094
|
+
trigger: triggerContext,
|
|
8095
|
+
executionId: `exec_sim_${randomUUID().slice(0, 12)}`
|
|
8096
|
+
};
|
|
8097
|
+
return handler(ctx);
|
|
8098
|
+
}
|
|
8099
|
+
async function simulateSchedule(handler, options) {
|
|
8100
|
+
return simulateTrigger(handler, {
|
|
8101
|
+
source: "cron",
|
|
8102
|
+
body: options?.cron ? { cron: options.cron } : {},
|
|
8103
|
+
eventType: "tick",
|
|
8104
|
+
receivedAt: options?.receivedAt,
|
|
8105
|
+
bindings: options?.bindings
|
|
8106
|
+
});
|
|
8107
|
+
}
|
|
8108
|
+
function loadFixture(source) {
|
|
8109
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
8110
|
+
const candidates = [
|
|
8111
|
+
resolve(dir, "fixtures", `${source}.json`),
|
|
8112
|
+
resolve(dir, "..", "src", "triggers", "fixtures", `${source}.json`)
|
|
8113
|
+
];
|
|
8114
|
+
for (const fixturePath of candidates) {
|
|
8115
|
+
try {
|
|
8116
|
+
const content = readFileSync(fixturePath, "utf-8");
|
|
8117
|
+
return JSON.parse(content);
|
|
8118
|
+
} catch {
|
|
8119
|
+
}
|
|
8120
|
+
}
|
|
8121
|
+
throw new Error(
|
|
8122
|
+
`No fixture for source="${source}". Looked at ${candidates.join(", ")}`
|
|
8123
|
+
);
|
|
8124
|
+
}
|
|
7585
8125
|
|
|
7586
|
-
export { ACTIVE_STATUSES, AIClient, Agent, AgentRouter, ApprovalClient, ApprovalResult, Audio, CANONICAL_STATUSES, DIDAuthenticator, DidClient, DidInterface, DidManager, ExecutionContext, ExecutionLogger, ExecutionStatus, File, HEADER_CALLER_DID, HEADER_DID_NONCE, HEADER_DID_SIGNATURE, HEADER_DID_TIMESTAMP, HarnessRunner, Image, JWE_ALG, JWE_ENC, KEY_AGREEMENT_TYPE, MediaProviderError, MediaRouter, MemoryClient, MemoryClientBase, MemoryEventClient, MemoryInterface, MultimodalResponse, OpenRouterMediaProvider, PauseClock, PauseManager, PayloadEncryptionError, RateLimitError, RealtimeSession, ReasonerContext, SUPPORTED_PROVIDERS, SUPPORTED_SESSION_TRANSPORTS, SessionTransportError, SkillContext, StatelessRateLimiter, TERMINAL_STATUSES, Text, Video, WorkflowReporter, audioFromBase64, audioFromBuffer, audioFromFile, audioFromUrl, buildProvider, buildSessionDefinition, buildToolConfig, capabilitiesToTools, capabilityToMetadataTool, capabilityToTool, createExecutionLogger, createHarnessResult, createMetrics, createMultimodalResponse, createRawResult, decrypt, decryptToString, encryptForDid, encryptToJwk, eventTrigger, executeToolCallLoop, extractKeyAgreementJwk, fileFromBase64, fileFromBuffer, fileFromPath, fileFromUrl, generateX25519KeyPair, getCurrentContext, getCurrentSkillContext, imageFromBase64, imageFromBuffer, imageFromFile, imageFromUrl, installApprovalWebhookRoute, isActive, isExecutionLogBatchPayload, isTerminal, normalizeExecutionLogEntry, normalizeSessionTransportValue, normalizeStatus, scheduleTrigger, serializeExecutionLogEntry, text, triggerToPayload, validateSessionTransport, videoFromBase64, videoFromBuffer, videoFromFile, videoFromUrl };
|
|
8126
|
+
export { ACTIVE_STATUSES, AIClient, Agent, AgentRouter, ApprovalClient, ApprovalResult, Audio, CANONICAL_STATUSES, CostTracker, DIDAuthenticator, DidClient, DidInterface, DidManager, ExecutionContext, ExecutionLogger, ExecutionStatus, File, HEADER_CALLER_DID, HEADER_DID_NONCE, HEADER_DID_SIGNATURE, HEADER_DID_TIMESTAMP, HarnessRunner, Image, JWE_ALG, JWE_ENC, KEY_AGREEMENT_TYPE, MediaProviderError, MediaRouter, MemoryClient, MemoryClientBase, MemoryEventClient, MemoryInterface, MultimodalResponse, OpenRouterMediaProvider, PauseClock, PauseManager, PayloadEncryptionError, RateLimitError, RealtimeSession, ReasonerContext, SUPPORTED_PROVIDERS, SUPPORTED_SESSION_TRANSPORTS, SessionTransportError, SkillContext, StatelessRateLimiter, TERMINAL_STATUSES, Text, USAGE_ENVELOPE_KEY, Video, WorkflowReporter, applyTriggerTransform, attachUsageToSyncResult, audioFromBase64, audioFromBuffer, audioFromFile, audioFromUrl, buildProvider, buildSessionDefinition, buildToolConfig, capabilitiesToTools, capabilityToMetadataTool, capabilityToTool, createExecutionLogger, createHarnessResult, createMetrics, createMultimodalResponse, createRawResult, decrypt, decryptToString, deriveProvider, encryptForDid, encryptToJwk, eventTrigger, executeToolCallLoop, extractKeyAgreementJwk, fileFromBase64, fileFromBuffer, fileFromPath, fileFromUrl, generateX25519KeyPair, getCurrentContext, getCurrentSkillContext, imageFromBase64, imageFromBuffer, imageFromFile, imageFromUrl, installApprovalWebhookRoute, isActive, isExecutionLogBatchPayload, isTerminal, isTriggerEnvelope, loadFixture, normalizeExecutionLogEntry, normalizeSessionTransportValue, normalizeStatus, scheduleTrigger, serializeExecutionLogEntry, simulateSchedule, simulateTrigger, text, triggerToPayload, unwrapEnvelope, usageSummaryOrNull, validateSessionTransport, videoFromBase64, videoFromBuffer, videoFromFile, videoFromUrl };
|
|
7587
8127
|
//# sourceMappingURL=index.js.map
|
|
7588
8128
|
//# sourceMappingURL=index.js.map
|