@dudousxd/nestjs-agent-opencode 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -1
- package/dist/durable/index.cjs +344 -74
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +1 -1
- package/dist/durable/index.d.ts +1 -1
- package/dist/durable/index.js +345 -75
- package/dist/durable/index.js.map +1 -1
- package/dist/{engine-Bij6AdoF.d.cts → engine-0YkBLWZR.d.cts} +154 -19
- package/dist/{engine-Bij6AdoF.d.ts → engine-0YkBLWZR.d.ts} +154 -19
- package/dist/index.cjs +343 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +340 -71
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,8 @@ __export(index_exports, {
|
|
|
38
38
|
OpenCodeToolsTokens: () => OpenCodeToolsTokens,
|
|
39
39
|
OpenCodeTurn: () => OpenCodeTurn,
|
|
40
40
|
OpenCodeTurns: () => OpenCodeTurns,
|
|
41
|
+
addUsage: () => addUsage,
|
|
42
|
+
emptyUsage: () => emptyUsage,
|
|
41
43
|
keyValueOpenCodeSessionStore: () => keyValueOpenCodeSessionStore,
|
|
42
44
|
openCode: () => openCode,
|
|
43
45
|
openCodeControllers: () => openCodeControllers,
|
|
@@ -45,7 +47,8 @@ __export(index_exports, {
|
|
|
45
47
|
sessionOf: () => sessionOf,
|
|
46
48
|
toElicitation: () => toElicitation,
|
|
47
49
|
toFormAnswer: () => toFormAnswer,
|
|
48
|
-
toQuestion: () => toQuestion
|
|
50
|
+
toQuestion: () => toQuestion,
|
|
51
|
+
usageOf: () => usageOf
|
|
49
52
|
});
|
|
50
53
|
module.exports = __toCommonJS(index_exports);
|
|
51
54
|
|
|
@@ -287,6 +290,41 @@ function toFormAnswer(form, answers = {}) {
|
|
|
287
290
|
__name(toFormAnswer, "toFormAnswer");
|
|
288
291
|
|
|
289
292
|
// src/turn.ts
|
|
293
|
+
function emptyUsage() {
|
|
294
|
+
return {
|
|
295
|
+
inputTokens: 0,
|
|
296
|
+
outputTokens: 0,
|
|
297
|
+
cacheReadTokens: 0,
|
|
298
|
+
cacheWriteTokens: 0,
|
|
299
|
+
reasoningTokens: 0,
|
|
300
|
+
costUsd: 0
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
__name(emptyUsage, "emptyUsage");
|
|
304
|
+
function addUsage(a, b) {
|
|
305
|
+
const x = a ?? emptyUsage();
|
|
306
|
+
const y = b ?? emptyUsage();
|
|
307
|
+
return {
|
|
308
|
+
inputTokens: x.inputTokens + (y.inputTokens ?? 0),
|
|
309
|
+
outputTokens: x.outputTokens + (y.outputTokens ?? 0),
|
|
310
|
+
cacheReadTokens: (x.cacheReadTokens ?? 0) + (y.cacheReadTokens ?? 0),
|
|
311
|
+
cacheWriteTokens: (x.cacheWriteTokens ?? 0) + (y.cacheWriteTokens ?? 0),
|
|
312
|
+
reasoningTokens: (x.reasoningTokens ?? 0) + (y.reasoningTokens ?? 0),
|
|
313
|
+
costUsd: x.costUsd + (y.costUsd ?? 0)
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
__name(addUsage, "addUsage");
|
|
317
|
+
function subUsage(a, b) {
|
|
318
|
+
return {
|
|
319
|
+
inputTokens: a.inputTokens - b.inputTokens,
|
|
320
|
+
outputTokens: a.outputTokens - b.outputTokens,
|
|
321
|
+
cacheReadTokens: a.cacheReadTokens - b.cacheReadTokens,
|
|
322
|
+
cacheWriteTokens: a.cacheWriteTokens - b.cacheWriteTokens,
|
|
323
|
+
reasoningTokens: a.reasoningTokens - b.reasoningTokens,
|
|
324
|
+
costUsd: a.costUsd - b.costUsd
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
__name(subUsage, "subUsage");
|
|
290
328
|
var ASK_TOOL_NAME = "ask";
|
|
291
329
|
var FAILED_STATUSES = /* @__PURE__ */ new Set([
|
|
292
330
|
"error",
|
|
@@ -352,11 +390,22 @@ var OpenCodeTurn = class {
|
|
|
352
390
|
hidden = /* @__PURE__ */ new Set();
|
|
353
391
|
milestones = [];
|
|
354
392
|
waiting;
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
393
|
+
/** What this turn object saw spent, and how much of it a milestone already reported. */
|
|
394
|
+
spent = emptyUsage();
|
|
395
|
+
reported = emptyUsage();
|
|
396
|
+
priced = false;
|
|
397
|
+
model;
|
|
398
|
+
/** What the session had spent when this turn prompted it; what OpenCode reported since. */
|
|
399
|
+
baseline;
|
|
400
|
+
seen = {
|
|
401
|
+
cost: 0,
|
|
402
|
+
input: 0,
|
|
403
|
+
output: 0,
|
|
404
|
+
reasoning: 0,
|
|
405
|
+
read: 0,
|
|
406
|
+
write: 0
|
|
358
407
|
};
|
|
359
|
-
|
|
408
|
+
compacted = false;
|
|
360
409
|
stepOpen = false;
|
|
361
410
|
sawText = false;
|
|
362
411
|
separator = false;
|
|
@@ -366,6 +415,24 @@ var OpenCodeTurn = class {
|
|
|
366
415
|
stepError;
|
|
367
416
|
constructor(a) {
|
|
368
417
|
this.a = a;
|
|
418
|
+
this.model = modelOfLabel(a.input.model);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* What the session had spent before this turn prompted it (`session.get`). When the execution
|
|
422
|
+
* ends, what the session spent since that this turn's events did not report — the title OpenCode
|
|
423
|
+
* generates, a compaction: OpenCode records those calls on the session without streaming them — is
|
|
424
|
+
* recorded as a usage row of its own.
|
|
425
|
+
*/
|
|
426
|
+
setBaseline(info) {
|
|
427
|
+
if (info?.tokens === void 0) return;
|
|
428
|
+
this.baseline = {
|
|
429
|
+
cost: Number(info.cost) || 0,
|
|
430
|
+
tokens: info.tokens
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
/** What the run spent so far: carried from before this turn object, plus what it saw. */
|
|
434
|
+
totalUsage() {
|
|
435
|
+
return addUsage(this.a.carried, this.spent);
|
|
369
436
|
}
|
|
370
437
|
get sessionId() {
|
|
371
438
|
return this.a.sessionId;
|
|
@@ -448,7 +515,15 @@ var OpenCodeTurn = class {
|
|
|
448
515
|
this.chain = this.chain.then(() => ask.kind === "approval" ? this.decided(ask, reply, tell) : this.answered(ask, reply, tell)).then(resolve, reject);
|
|
449
516
|
});
|
|
450
517
|
}
|
|
451
|
-
reach(
|
|
518
|
+
reach(reached) {
|
|
519
|
+
const usage = subUsage(this.spent, this.reported);
|
|
520
|
+
this.reported = {
|
|
521
|
+
...this.spent
|
|
522
|
+
};
|
|
523
|
+
const milestone = {
|
|
524
|
+
...reached,
|
|
525
|
+
usage
|
|
526
|
+
};
|
|
452
527
|
const waiting = this.waiting;
|
|
453
528
|
if (waiting !== void 0) {
|
|
454
529
|
this.waiting = void 0;
|
|
@@ -604,9 +679,26 @@ var OpenCodeTurn = class {
|
|
|
604
679
|
}
|
|
605
680
|
}
|
|
606
681
|
async onEvent(e) {
|
|
607
|
-
if (this.ended) return;
|
|
608
682
|
const d = e.data ?? {};
|
|
683
|
+
if (e.type === "session.usage.recorded") {
|
|
684
|
+
await this.usageRecorded(d);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (this.ended) return;
|
|
609
688
|
switch (e.type) {
|
|
689
|
+
case "session.compaction.started":
|
|
690
|
+
case "session.compaction.ended":
|
|
691
|
+
this.compacted = true;
|
|
692
|
+
return;
|
|
693
|
+
case "session.step.started":
|
|
694
|
+
case "session.model.selected": {
|
|
695
|
+
const model = d.model;
|
|
696
|
+
if (typeof model?.id === "string" && typeof model.providerID === "string") this.model = {
|
|
697
|
+
providerID: model.providerID,
|
|
698
|
+
id: model.id
|
|
699
|
+
};
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
610
702
|
case "session.text.delta": {
|
|
611
703
|
const delta = typeof d.delta === "string" ? d.delta : "";
|
|
612
704
|
if (!delta) return;
|
|
@@ -760,42 +852,70 @@ ${delta}` : delta;
|
|
|
760
852
|
});
|
|
761
853
|
}
|
|
762
854
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
this.
|
|
776
|
-
this.
|
|
777
|
-
|
|
855
|
+
modelId() {
|
|
856
|
+
return this.model !== void 0 ? `${this.model.providerID}/${this.model.id}` : this.a.modelLabel;
|
|
857
|
+
}
|
|
858
|
+
/** One model call's usage and cost, into the ledger and the run's total. */
|
|
859
|
+
async spend(d, purpose) {
|
|
860
|
+
const usage = usageOf(d.tokens);
|
|
861
|
+
const reportedCostUsd = typeof d.cost === "number" && Number.isFinite(d.cost) && d.cost >= 0 ? d.cost : void 0;
|
|
862
|
+
const n = /* @__PURE__ */ __name((v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0, "n");
|
|
863
|
+
this.seen.cost += reportedCostUsd ?? 0;
|
|
864
|
+
this.seen.input += n(d.tokens?.input);
|
|
865
|
+
this.seen.output += n(d.tokens?.output);
|
|
866
|
+
this.seen.reasoning += n(d.tokens?.reasoning);
|
|
867
|
+
this.seen.read += n(d.tokens?.cache?.read);
|
|
868
|
+
this.seen.write += n(d.tokens?.cache?.write);
|
|
869
|
+
const fallback = reportedCostUsd !== void 0 ? {
|
|
870
|
+
costUsd: reportedCostUsd,
|
|
871
|
+
costSource: "provider"
|
|
872
|
+
} : {};
|
|
873
|
+
let cost = fallback;
|
|
874
|
+
if (this.a.priceStep !== void 0) {
|
|
875
|
+
cost = await this.a.priceStep({
|
|
876
|
+
model: this.model,
|
|
877
|
+
usage,
|
|
878
|
+
reportedCostUsd
|
|
879
|
+
}).catch(() => fallback);
|
|
880
|
+
}
|
|
881
|
+
this.spent = addUsage(this.spent, {
|
|
882
|
+
inputTokens: usage.inputTokens,
|
|
883
|
+
outputTokens: usage.outputTokens,
|
|
884
|
+
cacheReadTokens: usage.cacheReadTokens ?? 0,
|
|
885
|
+
cacheWriteTokens: usage.cacheWriteTokens ?? 0,
|
|
886
|
+
reasoningTokens: usage.reasoningTokens ?? 0,
|
|
887
|
+
costUsd: cost.costUsd ?? 0
|
|
888
|
+
});
|
|
889
|
+
if (cost.costUsd !== void 0) this.priced = true;
|
|
778
890
|
await this.a.store.recordUsage({
|
|
779
891
|
threadId: this.a.input.threadId,
|
|
780
892
|
actorRef: this.a.input.actor.id,
|
|
781
|
-
modelId: this.
|
|
782
|
-
purpose
|
|
893
|
+
modelId: this.modelId(),
|
|
894
|
+
purpose,
|
|
783
895
|
usage,
|
|
784
|
-
...
|
|
785
|
-
costUsd,
|
|
786
|
-
costSource: "provider"
|
|
787
|
-
} : {}
|
|
896
|
+
...cost
|
|
788
897
|
});
|
|
898
|
+
return {
|
|
899
|
+
usage,
|
|
900
|
+
cost
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
/** `session.usage.recorded`: a call OpenCode made besides the steps (the title, a compaction). */
|
|
904
|
+
async usageRecorded(d) {
|
|
905
|
+
await this.spend(d, d.source === "title" ? "title" : "history_summary");
|
|
906
|
+
}
|
|
907
|
+
async stepEnded(d) {
|
|
908
|
+
const { usage, cost } = await this.spend(d, "chat");
|
|
789
909
|
if (!this.stepOpen) return;
|
|
790
910
|
this.stepOpen = false;
|
|
791
911
|
await this.write({
|
|
792
912
|
kind: "step-finish",
|
|
793
913
|
usage,
|
|
794
|
-
costUsd: costUsd ?? null,
|
|
914
|
+
costUsd: cost.costUsd ?? null,
|
|
795
915
|
...this.segment.reasoningMs > 0 ? {
|
|
796
916
|
reasoningMs: this.segment.reasoningMs
|
|
797
917
|
} : {},
|
|
798
|
-
model: this.
|
|
918
|
+
model: this.modelId()
|
|
799
919
|
});
|
|
800
920
|
}
|
|
801
921
|
/**
|
|
@@ -814,12 +934,7 @@ ${delta}` : delta;
|
|
|
814
934
|
content: s.text,
|
|
815
935
|
runId: this.a.runId,
|
|
816
936
|
...final ? {
|
|
817
|
-
usage:
|
|
818
|
-
...this.usage,
|
|
819
|
-
...this.costUsd !== void 0 ? {
|
|
820
|
-
costUsd: this.costUsd
|
|
821
|
-
} : {}
|
|
822
|
-
}
|
|
937
|
+
usage: this.messageUsage()
|
|
823
938
|
} : {},
|
|
824
939
|
...this.a.input.agentName !== void 0 ? {
|
|
825
940
|
agentName: this.a.input.agentName
|
|
@@ -847,6 +962,26 @@ ${delta}` : delta;
|
|
|
847
962
|
]);
|
|
848
963
|
return message.id;
|
|
849
964
|
}
|
|
965
|
+
/** The run's usage as its last message carries it: the whole run, cache tokens included. */
|
|
966
|
+
messageUsage() {
|
|
967
|
+
const total = this.totalUsage();
|
|
968
|
+
return {
|
|
969
|
+
inputTokens: total.inputTokens,
|
|
970
|
+
outputTokens: total.outputTokens,
|
|
971
|
+
...total.cacheReadTokens > 0 ? {
|
|
972
|
+
cacheReadTokens: total.cacheReadTokens
|
|
973
|
+
} : {},
|
|
974
|
+
...total.cacheWriteTokens > 0 ? {
|
|
975
|
+
cacheWriteTokens: total.cacheWriteTokens
|
|
976
|
+
} : {},
|
|
977
|
+
...total.reasoningTokens > 0 ? {
|
|
978
|
+
reasoningTokens: total.reasoningTokens
|
|
979
|
+
} : {},
|
|
980
|
+
...this.priced || (this.a.carried?.costUsd ?? 0) > 0 ? {
|
|
981
|
+
costUsd: total.costUsd
|
|
982
|
+
} : {}
|
|
983
|
+
};
|
|
984
|
+
}
|
|
850
985
|
/** A request recorded by an earlier process (a turn resumed after a restart). */
|
|
851
986
|
async alreadyRecorded(id) {
|
|
852
987
|
return (await this.a.store.toolCallApproval?.(id) ?? null) !== null;
|
|
@@ -1198,9 +1333,39 @@ ${delta}` : delta;
|
|
|
1198
1333
|
this.messageResults.set(messageId, results);
|
|
1199
1334
|
await this.a.store.setMessageToolResults(messageId, results);
|
|
1200
1335
|
}
|
|
1336
|
+
/** What the session spent since the baseline that no event of this turn reported. */
|
|
1337
|
+
async unreported() {
|
|
1338
|
+
const baseline = this.baseline;
|
|
1339
|
+
if (baseline === void 0) return;
|
|
1340
|
+
this.baseline = void 0;
|
|
1341
|
+
const info = await this.a.client.session.get?.({
|
|
1342
|
+
sessionID: this.a.sessionId
|
|
1343
|
+
}).catch(() => void 0);
|
|
1344
|
+
const after = info?.tokens;
|
|
1345
|
+
if (after === void 0) return;
|
|
1346
|
+
const before = baseline.tokens;
|
|
1347
|
+
const left = /* @__PURE__ */ __name((now, then, seen) => Math.max(0, (Number(now) || 0) - (Number(then) || 0) - seen), "left");
|
|
1348
|
+
const tokens = {
|
|
1349
|
+
input: left(after.input, before.input, this.seen.input),
|
|
1350
|
+
output: left(after.output, before.output, this.seen.output),
|
|
1351
|
+
reasoning: left(after.reasoning, before.reasoning, this.seen.reasoning),
|
|
1352
|
+
cache: {
|
|
1353
|
+
read: left(after.cache?.read, before.cache?.read, this.seen.read),
|
|
1354
|
+
write: left(after.cache?.write, before.cache?.write, this.seen.write)
|
|
1355
|
+
}
|
|
1356
|
+
};
|
|
1357
|
+
const total = tokens.input + tokens.output + tokens.reasoning + tokens.cache.read + tokens.cache.write;
|
|
1358
|
+
if (total === 0) return;
|
|
1359
|
+
const cost = left(info?.cost, baseline.cost, this.seen.cost);
|
|
1360
|
+
await this.spend({
|
|
1361
|
+
tokens,
|
|
1362
|
+
cost: cost < 1e-9 ? 0 : cost
|
|
1363
|
+
}, this.compacted ? "history_summary" : "title");
|
|
1364
|
+
}
|
|
1201
1365
|
async end(outcome) {
|
|
1202
1366
|
if (this.ended) return;
|
|
1203
1367
|
this.ended = true;
|
|
1368
|
+
await this.unreported().catch((error) => this.a.logger.warn(`run ${this.a.runId}: ${errorText(error, "could not read the session usage")}`));
|
|
1204
1369
|
if (outcome.status !== "failed" || this.segment.text || this.segment.calls.length > 0 || this.segment.ui.length > 0) {
|
|
1205
1370
|
await this.flush(true);
|
|
1206
1371
|
}
|
|
@@ -1216,6 +1381,35 @@ ${delta}` : delta;
|
|
|
1216
1381
|
});
|
|
1217
1382
|
}
|
|
1218
1383
|
};
|
|
1384
|
+
function modelOfLabel(label) {
|
|
1385
|
+
const slash = label?.indexOf("/") ?? -1;
|
|
1386
|
+
if (label === void 0 || slash <= 0 || slash === label.length - 1) return void 0;
|
|
1387
|
+
return {
|
|
1388
|
+
providerID: label.slice(0, slash),
|
|
1389
|
+
id: label.slice(slash + 1)
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
__name(modelOfLabel, "modelOfLabel");
|
|
1393
|
+
function usageOf(tokens) {
|
|
1394
|
+
const n = /* @__PURE__ */ __name((v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0, "n");
|
|
1395
|
+
const read = n(tokens?.cache?.read);
|
|
1396
|
+
const write = n(tokens?.cache?.write);
|
|
1397
|
+
const reasoning = n(tokens?.reasoning);
|
|
1398
|
+
return {
|
|
1399
|
+
inputTokens: n(tokens?.input) + read + write,
|
|
1400
|
+
outputTokens: n(tokens?.output),
|
|
1401
|
+
...read > 0 ? {
|
|
1402
|
+
cacheReadTokens: read
|
|
1403
|
+
} : {},
|
|
1404
|
+
...write > 0 ? {
|
|
1405
|
+
cacheWriteTokens: write
|
|
1406
|
+
} : {},
|
|
1407
|
+
...reasoning > 0 ? {
|
|
1408
|
+
reasoningTokens: reasoning
|
|
1409
|
+
} : {}
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
__name(usageOf, "usageOf");
|
|
1219
1413
|
|
|
1220
1414
|
// src/mcp.ts
|
|
1221
1415
|
var LOOP_SERVED_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -1647,7 +1841,7 @@ var OpenCodeTurns = class {
|
|
|
1647
1841
|
spent = /* @__PURE__ */ new Set();
|
|
1648
1842
|
/** When the tools endpoint was last registered for a location (`server|boot|directory|actor`). */
|
|
1649
1843
|
toolsIssued = /* @__PURE__ */ new Map();
|
|
1650
|
-
constructor(host, sessions, settings, store, sink, deps, registry, skills, memory, queue, toolsTokens2) {
|
|
1844
|
+
constructor(host, sessions, settings, store, sink, deps, registry, skills, memory, queue, toolsTokens2, pricingStore, agentOptions) {
|
|
1651
1845
|
this.host = host;
|
|
1652
1846
|
this.sessions = sessions;
|
|
1653
1847
|
this.settings = settings;
|
|
@@ -1659,6 +1853,30 @@ var OpenCodeTurns = class {
|
|
|
1659
1853
|
this.memory = memory;
|
|
1660
1854
|
this.queue = queue;
|
|
1661
1855
|
this.toolsTokens = toolsTokens2;
|
|
1856
|
+
this.prices = new import_nestjs_agent_core4.ModelPriceResolver({
|
|
1857
|
+
pricingStore,
|
|
1858
|
+
catalog: agentOptions?.priceCatalog,
|
|
1859
|
+
log: {
|
|
1860
|
+
info: /* @__PURE__ */ __name((message) => this.logger.log(message), "info"),
|
|
1861
|
+
warn: /* @__PURE__ */ __name((message) => this.logger.warn(message), "warn")
|
|
1862
|
+
}
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
prices;
|
|
1866
|
+
/**
|
|
1867
|
+
* What a model call OpenCode made cost, by the same rule the library's loop records its own steps
|
|
1868
|
+
* at: a reported figure wins, else an estimate off the model's price — see
|
|
1869
|
+
* {@link OpenCodeEngineSettings.cost}.
|
|
1870
|
+
*/
|
|
1871
|
+
async priceStep(step) {
|
|
1872
|
+
const { model, usage, reportedCostUsd } = step;
|
|
1873
|
+
const tokens = usage.inputTokens + usage.outputTokens;
|
|
1874
|
+
const reported = reportedCostUsd !== void 0 && (reportedCostUsd > 0 || tokens === 0);
|
|
1875
|
+
if (reported && this.settings.cost !== "estimate") {
|
|
1876
|
+
return (0, import_nestjs_agent_core4.resolveUsageCost)(usage, reportedCostUsd, void 0);
|
|
1877
|
+
}
|
|
1878
|
+
const price = model !== void 0 && tokens > 0 ? await this.prices.priceFor(priceRef(model)) : void 0;
|
|
1879
|
+
return (0, import_nestjs_agent_core4.resolveUsageCost)(usage, price === void 0 ? reportedCostUsd : void 0, price);
|
|
1662
1880
|
}
|
|
1663
1881
|
/** The tools server's name in OpenCode. */
|
|
1664
1882
|
get toolsServer() {
|
|
@@ -2248,8 +2466,8 @@ ${body}
|
|
|
2248
2466
|
}
|
|
2249
2467
|
// ---- prompt / observe / reply --------------------------------------------------------------
|
|
2250
2468
|
/** Start listening to the session (so nothing it says is missed), then send the user message. */
|
|
2251
|
-
async prompt(runId, input, handle) {
|
|
2252
|
-
const live = await this.ensureLive(runId, input, handle, false);
|
|
2469
|
+
async prompt(runId, input, handle, spent) {
|
|
2470
|
+
const live = await this.ensureLive(runId, input, handle, false, spent);
|
|
2253
2471
|
try {
|
|
2254
2472
|
const prompt = await this.host.promptFor?.({
|
|
2255
2473
|
input,
|
|
@@ -2264,6 +2482,7 @@ ${body}
|
|
|
2264
2482
|
})
|
|
2265
2483
|
} : {}
|
|
2266
2484
|
};
|
|
2485
|
+
live.turn.setBaseline(await this.sessionUsage(live.client, handle.sessionId));
|
|
2267
2486
|
await live.client.session.prompt({
|
|
2268
2487
|
sessionID: handle.sessionId,
|
|
2269
2488
|
text: prompt.text,
|
|
@@ -2279,9 +2498,12 @@ ${body}
|
|
|
2279
2498
|
* Wait for the turn's next milestone. In a process that was not following the turn (a resumed
|
|
2280
2499
|
* durable run), the turn is rebuilt and first catches up on requests OpenCode raised meanwhile;
|
|
2281
2500
|
* `session.wait` is the safety net for a terminal event that was missed.
|
|
2501
|
+
*
|
|
2502
|
+
* `spent` is what the run spent before (the sum of the `usage` of its earlier milestones): a turn
|
|
2503
|
+
* rebuilt here starts from it, so the answer it persists carries the whole run's usage.
|
|
2282
2504
|
*/
|
|
2283
|
-
async observe(runId, input, handle) {
|
|
2284
|
-
const live = await this.ensureLive(runId, input, handle, true);
|
|
2505
|
+
async observe(runId, input, handle, spent) {
|
|
2506
|
+
const live = await this.ensureLive(runId, input, handle, true, spent);
|
|
2285
2507
|
const { turn, client } = live;
|
|
2286
2508
|
if (turn.hasMilestone()) return this.reached(runId, input, handle, await turn.next());
|
|
2287
2509
|
let done = false;
|
|
@@ -2320,32 +2542,43 @@ ${body}
|
|
|
2320
2542
|
* request is gone with it: a new session is opened, told what the person decided, and prompted
|
|
2321
2543
|
* to go on.
|
|
2322
2544
|
*/
|
|
2323
|
-
async reply(runId, input, handle, ask, reply) {
|
|
2545
|
+
async reply(runId, input, handle, ask, reply, spent) {
|
|
2324
2546
|
const server = await this.host.server(input.actor);
|
|
2325
2547
|
if (server.key !== handle.serverKey || server.bootId !== handle.bootId) {
|
|
2326
|
-
return this.continueAfterRestart(runId, input, server, ask, reply);
|
|
2548
|
+
return this.continueAfterRestart(runId, input, server, ask, reply, spent);
|
|
2327
2549
|
}
|
|
2328
|
-
const live = await this.ensureLive(runId, input, handle, false);
|
|
2550
|
+
const live = await this.ensureLive(runId, input, handle, false, spent);
|
|
2329
2551
|
await live.turn.decide(ask, reply);
|
|
2330
2552
|
return handle;
|
|
2331
2553
|
}
|
|
2332
|
-
async continueAfterRestart(runId, input, server, ask, reply) {
|
|
2554
|
+
async continueAfterRestart(runId, input, server, ask, reply, spent) {
|
|
2333
2555
|
const approved = "approved" in reply && reply.approved === true;
|
|
2334
2556
|
const note = ask.kind === "approval" ? approved ? `The user approved the action ${ask.action} you asked permission for. Continue the task.` : `The user did not approve the action ${ask.action}. Continue without it and explain.` : `The user answered your question: ${JSON.stringify("answers" in reply ? reply.answers : {})}. Continue.`;
|
|
2335
2557
|
const thread = await this.store.getThread(input.threadId);
|
|
2336
2558
|
const handle = await this.createSession(runId, input, server, thread?.messages ?? [], note);
|
|
2337
2559
|
await this.refreshInstructions(runId, input, server.client, handle.sessionId);
|
|
2338
2560
|
this.drop(runId);
|
|
2339
|
-
const live = await this.ensureLive(runId, input, handle, false);
|
|
2561
|
+
const live = await this.ensureLive(runId, input, handle, false, spent);
|
|
2340
2562
|
await live.turn.decide(ask, reply, {
|
|
2341
2563
|
tellOpenCode: false
|
|
2342
2564
|
});
|
|
2565
|
+
live.turn.setBaseline(await this.sessionUsage(live.client, handle.sessionId));
|
|
2343
2566
|
await live.client.session.prompt({
|
|
2344
2567
|
sessionID: handle.sessionId,
|
|
2345
2568
|
text: note
|
|
2346
2569
|
});
|
|
2347
2570
|
return handle;
|
|
2348
2571
|
}
|
|
2572
|
+
/** What a session spent so far (`session.get`), or nothing when OpenCode does not say. */
|
|
2573
|
+
async sessionUsage(client, sessionId) {
|
|
2574
|
+
try {
|
|
2575
|
+
return await client.session.get?.({
|
|
2576
|
+
sessionID: sessionId
|
|
2577
|
+
});
|
|
2578
|
+
} catch {
|
|
2579
|
+
return void 0;
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2349
2582
|
/** Interrupt the thread's session (cancel). */
|
|
2350
2583
|
async interrupt(input) {
|
|
2351
2584
|
const known = await this.sessions.get(input.threadId);
|
|
@@ -2356,7 +2589,7 @@ ${body}
|
|
|
2356
2589
|
sessionID: known.sessionId
|
|
2357
2590
|
});
|
|
2358
2591
|
}
|
|
2359
|
-
async ensureLive(runId, input, handle, catchUp) {
|
|
2592
|
+
async ensureLive(runId, input, handle, catchUp, spent) {
|
|
2360
2593
|
const current = this.live.get(runId);
|
|
2361
2594
|
if (current !== void 0 && current.handle.sessionId === handle.sessionId) return current;
|
|
2362
2595
|
current?.stop();
|
|
@@ -2382,6 +2615,10 @@ ${body}
|
|
|
2382
2615
|
} : {}
|
|
2383
2616
|
}), "approvalFor"),
|
|
2384
2617
|
modelLabel: input.model ?? "opencode",
|
|
2618
|
+
priceStep: /* @__PURE__ */ __name((step) => this.priceStep(step), "priceStep"),
|
|
2619
|
+
...spent !== void 0 ? {
|
|
2620
|
+
carried: spent
|
|
2621
|
+
} : {},
|
|
2385
2622
|
logger: this.logger,
|
|
2386
2623
|
onGranted: /* @__PURE__ */ __name((action, id) => {
|
|
2387
2624
|
const grants = this.grants.get(runId) ?? [];
|
|
@@ -2428,20 +2665,14 @@ ${body}
|
|
|
2428
2665
|
}
|
|
2429
2666
|
return milestone;
|
|
2430
2667
|
}
|
|
2431
|
-
/**
|
|
2432
|
-
|
|
2668
|
+
/**
|
|
2669
|
+
* What the run produced, as the host's settle hooks see it. `usage` is what the run spent — the
|
|
2670
|
+
* runner's sum of its milestones' usage, which a resumed run reads back from its journal — else
|
|
2671
|
+
* what the turn this process follows saw.
|
|
2672
|
+
*/
|
|
2673
|
+
async runResult(runId, input, outcome, durationMs, usage) {
|
|
2433
2674
|
const thread = await this.store.getThread(input.threadId);
|
|
2434
2675
|
const messages = (thread?.messages ?? []).filter((m) => m.role === "assistant" && m.runId === runId);
|
|
2435
|
-
const usage = {
|
|
2436
|
-
inputTokens: 0,
|
|
2437
|
-
outputTokens: 0,
|
|
2438
|
-
costUsd: 0
|
|
2439
|
-
};
|
|
2440
|
-
for (const m of messages) {
|
|
2441
|
-
usage.inputTokens += m.usage?.inputTokens ?? 0;
|
|
2442
|
-
usage.outputTokens += m.usage?.outputTokens ?? 0;
|
|
2443
|
-
usage.costUsd += m.usage?.costUsd ?? 0;
|
|
2444
|
-
}
|
|
2445
2676
|
return {
|
|
2446
2677
|
runId,
|
|
2447
2678
|
input,
|
|
@@ -2486,15 +2717,31 @@ ${body}
|
|
|
2486
2717
|
this.logger.warn(`onSettled failed: ${errorText(error, "error")}`);
|
|
2487
2718
|
}
|
|
2488
2719
|
}
|
|
2489
|
-
/**
|
|
2490
|
-
|
|
2720
|
+
/**
|
|
2721
|
+
* What the run spent: the runner's sum of its milestones, or what the turn this process follows
|
|
2722
|
+
* saw (which starts from that sum) when it saw more — a run that failed or was cancelled between
|
|
2723
|
+
* two milestones.
|
|
2724
|
+
*/
|
|
2725
|
+
spentBy(runId, spent) {
|
|
2726
|
+
const seen = this.live.get(runId)?.turn.totalUsage();
|
|
2727
|
+
if (spent === void 0) return seen ?? emptyUsage();
|
|
2728
|
+
if (seen === void 0) return spent;
|
|
2729
|
+
const tokens = /* @__PURE__ */ __name((u) => u.inputTokens + u.outputTokens, "tokens");
|
|
2730
|
+
return tokens(seen) > tokens(spent) ? seen : spent;
|
|
2731
|
+
}
|
|
2732
|
+
/**
|
|
2733
|
+
* End the run's stream and bookkeeping for how its execution ended. `spent` is what the run spent
|
|
2734
|
+
* (the sum of its milestones' `usage`); omit → what the turn this process follows saw.
|
|
2735
|
+
*/
|
|
2736
|
+
async settle(runId, input, outcome, durationMs, spent) {
|
|
2491
2737
|
if (outcome.status === "failed") {
|
|
2492
|
-
await this.settleFailed(runId, input, outcome.error, durationMs);
|
|
2738
|
+
await this.settleFailed(runId, input, outcome.error, durationMs, spent);
|
|
2493
2739
|
return;
|
|
2494
2740
|
}
|
|
2741
|
+
const usage = this.spentBy(runId, spent);
|
|
2495
2742
|
this.forget(runId);
|
|
2496
2743
|
const writer = await this.sink.open(runId);
|
|
2497
|
-
const result = await this.runResult(runId, input, outcome, durationMs);
|
|
2744
|
+
const result = await this.runResult(runId, input, outcome, durationMs, usage);
|
|
2498
2745
|
await this.amend(writer, result);
|
|
2499
2746
|
if (outcome.status === "interrupted") {
|
|
2500
2747
|
this.logger.log(`agent run ${runId} cancelled`);
|
|
@@ -2522,13 +2769,14 @@ ${body}
|
|
|
2522
2769
|
}
|
|
2523
2770
|
await this.settled(result);
|
|
2524
2771
|
}
|
|
2525
|
-
async settleFailed(runId, input, error, durationMs = 0) {
|
|
2772
|
+
async settleFailed(runId, input, error, durationMs = 0, spent) {
|
|
2773
|
+
const usage = this.spentBy(runId, spent);
|
|
2526
2774
|
this.forget(runId);
|
|
2527
2775
|
const writer = await this.sink.open(runId);
|
|
2528
2776
|
const result = await this.runResult(runId, input, {
|
|
2529
2777
|
status: "failed",
|
|
2530
2778
|
error
|
|
2531
|
-
}, durationMs);
|
|
2779
|
+
}, durationMs, usage);
|
|
2532
2780
|
const worded = (await this.amend(writer, result)).error;
|
|
2533
2781
|
const message = worded ?? (0, import_nestjs_agent_core4.streamFailure)(new Error(error)).message;
|
|
2534
2782
|
this.logger.error(`agent run ${runId} failed: ${error}`);
|
|
@@ -2598,6 +2846,10 @@ OpenCodeTurns = _ts_decorate2([
|
|
|
2598
2846
|
_ts_param2(9, (0, import_common4.Inject)(import_nestjs_agent.AGENT_CHAT_QUEUE)),
|
|
2599
2847
|
_ts_param2(10, (0, import_common4.Optional)()),
|
|
2600
2848
|
_ts_param2(10, (0, import_common4.Inject)(OPENCODE_TOOLS_TOKENS)),
|
|
2849
|
+
_ts_param2(11, (0, import_common4.Optional)()),
|
|
2850
|
+
_ts_param2(11, (0, import_common4.Inject)(import_nestjs_agent_core4.AGENT_PRICING_STORE)),
|
|
2851
|
+
_ts_param2(12, (0, import_common4.Optional)()),
|
|
2852
|
+
_ts_param2(12, (0, import_common4.Inject)(import_nestjs_agent_core4.AGENT_OPTIONS)),
|
|
2601
2853
|
_ts_metadata2("design:type", Function),
|
|
2602
2854
|
_ts_metadata2("design:paramtypes", [
|
|
2603
2855
|
typeof OpenCodeHost === "undefined" ? Object : OpenCodeHost,
|
|
@@ -2610,6 +2862,8 @@ OpenCodeTurns = _ts_decorate2([
|
|
|
2610
2862
|
typeof SkillsConfig === "undefined" ? Object : SkillsConfig,
|
|
2611
2863
|
typeof MemoryConfig === "undefined" ? Object : MemoryConfig,
|
|
2612
2864
|
typeof ChatQueueService === "undefined" ? Object : ChatQueueService,
|
|
2865
|
+
Object,
|
|
2866
|
+
typeof AgentPricingStore === "undefined" ? Object : AgentPricingStore,
|
|
2613
2867
|
Object
|
|
2614
2868
|
])
|
|
2615
2869
|
], OpenCodeTurns);
|
|
@@ -2652,6 +2906,19 @@ function uiEmitter(scope, push) {
|
|
|
2652
2906
|
};
|
|
2653
2907
|
}
|
|
2654
2908
|
__name(uiEmitter, "uiEmitter");
|
|
2909
|
+
function priceRef(model) {
|
|
2910
|
+
const aliases = [
|
|
2911
|
+
model.id
|
|
2912
|
+
];
|
|
2913
|
+
const tail = model.id.slice(model.id.lastIndexOf("/") + 1);
|
|
2914
|
+
if (tail !== model.id && (0, import_nestjs_agent_core4.parseBedrockModelId)(tail) !== void 0) aliases.push(tail);
|
|
2915
|
+
return {
|
|
2916
|
+
modelId: `${model.providerID}/${model.id}`,
|
|
2917
|
+
provider: model.providerID,
|
|
2918
|
+
aliases
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
__name(priceRef, "priceRef");
|
|
2655
2922
|
|
|
2656
2923
|
// src/runner.ts
|
|
2657
2924
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
@@ -2753,18 +3020,20 @@ var OpenCodeAgentRunner = class {
|
|
|
2753
3020
|
async run(runId, input) {
|
|
2754
3021
|
const started = Date.now();
|
|
2755
3022
|
this.inputs.set(runId, input);
|
|
3023
|
+
let spent = emptyUsage();
|
|
2756
3024
|
try {
|
|
2757
3025
|
let handle = await this.turns.begin(runId, input);
|
|
2758
3026
|
if (this.cancelled.has(runId)) throw new import_nestjs_agent_core5.RunCancelledError();
|
|
2759
|
-
await this.turns.prompt(runId, input, handle);
|
|
3027
|
+
await this.turns.prompt(runId, input, handle, spent);
|
|
2760
3028
|
for (; ; ) {
|
|
2761
|
-
const milestone = await this.turns.observe(runId, input, handle);
|
|
3029
|
+
const milestone = await this.turns.observe(runId, input, handle, spent);
|
|
3030
|
+
spent = addUsage(spent, milestone.usage);
|
|
2762
3031
|
if (milestone.kind === "finished") {
|
|
2763
|
-
await this.turns.settle(runId, input, milestone.outcome, Date.now() - started);
|
|
3032
|
+
await this.turns.settle(runId, input, milestone.outcome, Date.now() - started, spent);
|
|
2764
3033
|
return;
|
|
2765
3034
|
}
|
|
2766
3035
|
const reply = await this.park(runId, milestone.ask.id, milestone.ask.kind === "approval" ? "approval" : "answers", milestone.timeoutMs);
|
|
2767
|
-
handle = await this.turns.reply(runId, input, handle, milestone.ask, reply);
|
|
3036
|
+
handle = await this.turns.reply(runId, input, handle, milestone.ask, reply, spent);
|
|
2768
3037
|
}
|
|
2769
3038
|
} catch (error) {
|
|
2770
3039
|
if (error instanceof import_nestjs_agent_core5.RunCancelledError) {
|
|
@@ -2774,7 +3043,7 @@ var OpenCodeAgentRunner = class {
|
|
|
2774
3043
|
}, Date.now() - started);
|
|
2775
3044
|
return;
|
|
2776
3045
|
}
|
|
2777
|
-
await this.turns.settleFailed(runId, input, errorText(error, "the turn failed"));
|
|
3046
|
+
await this.turns.settleFailed(runId, input, errorText(error, "the turn failed"), Date.now() - started);
|
|
2778
3047
|
} finally {
|
|
2779
3048
|
this.inputs.delete(runId);
|
|
2780
3049
|
}
|
|
@@ -2927,6 +3196,8 @@ __name(openCodeProviders, "openCodeProviders");
|
|
|
2927
3196
|
OpenCodeToolsTokens,
|
|
2928
3197
|
OpenCodeTurn,
|
|
2929
3198
|
OpenCodeTurns,
|
|
3199
|
+
addUsage,
|
|
3200
|
+
emptyUsage,
|
|
2930
3201
|
keyValueOpenCodeSessionStore,
|
|
2931
3202
|
openCode,
|
|
2932
3203
|
openCodeControllers,
|
|
@@ -2934,6 +3205,7 @@ __name(openCodeProviders, "openCodeProviders");
|
|
|
2934
3205
|
sessionOf,
|
|
2935
3206
|
toElicitation,
|
|
2936
3207
|
toFormAnswer,
|
|
2937
|
-
toQuestion
|
|
3208
|
+
toQuestion,
|
|
3209
|
+
usageOf
|
|
2938
3210
|
});
|
|
2939
3211
|
//# sourceMappingURL=index.cjs.map
|