@dudousxd/nestjs-agent-opencode 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { d as OpenCodeClient, e as OpenCodeEvent, f as OpenCodeForm, g as OpenCodeFormValue, h as OpenCodeFormField, i as OpenCodeMcpEndpoint, O as OpenCodeTurns, a as OpenCodeEngineSettings } from './engine-Bij6AdoF.cjs';
2
- export { I as InMemoryOpenCodeSessionStore, M as Milestone, j as OpenCodeAmendment, k as OpenCodeCallContext, c as OpenCodeEngineOptions, b as OpenCodeHost, l as OpenCodeKeyValue, m as OpenCodeModelRef, n as OpenCodePermissionRequest, o as OpenCodePermissionRule, p as OpenCodeReplyMismatchError, q as OpenCodeRunResult, r as OpenCodeServer, s as OpenCodeSessionCreate, t as OpenCodeSessionRef, u as OpenCodeSessionStore, v as OpenCodeToolCall, w as OpenCodeToolRefusedError, x as OpenCodeToolsClaims, y as OpenCodeToolsOptions, z as OpenCodeToolsTokens, A as OpenCodeTurn, B as OpenCodeTurnContext, P as PendingAsk, S as SessionHandle, T as TurnOutcome, C as keyValueOpenCodeSessionStore, D as openCode, E as openCodeControllers, F as openCodeProviders } from './engine-Bij6AdoF.cjs';
1
+ import { d as OpenCodeClient, e as OpenCodeEvent, f as OpenCodeForm, g as OpenCodeFormValue, h as OpenCodeFormField, i as OpenCodeMcpEndpoint, O as OpenCodeTurns, a as OpenCodeEngineSettings } from './engine-JnBbcRxh.cjs';
2
+ export { I as InMemoryOpenCodeSessionStore, M as Milestone, j as OpenCodeAmendment, k as OpenCodeCallContext, c as OpenCodeEngineOptions, b as OpenCodeHost, l as OpenCodeKeyValue, m as OpenCodeModelRef, n as OpenCodePermissionRequest, o as OpenCodePermissionRule, p as OpenCodeReplyMismatchError, q as OpenCodeRunResult, r as OpenCodeServer, s as OpenCodeSessionCreate, t as OpenCodeSessionRef, u as OpenCodeSessionStore, v as OpenCodeToolCall, w as OpenCodeToolRefusedError, x as OpenCodeToolsClaims, y as OpenCodeToolsOptions, z as OpenCodeToolsTokens, A as OpenCodeTurn, B as OpenCodeTurnContext, C as OpenCodeUsage, P as PendingAsk, S as SessionHandle, D as StepCost, E as StepModel, T as TurnOutcome, F as addUsage, G as emptyUsage, H as keyValueOpenCodeSessionStore, J as openCode, K as openCodeControllers, L as openCodeProviders, N as usageOf } from './engine-JnBbcRxh.cjs';
3
3
  import { ElicitationRequest, ElicitationQuestion, AgentRunner, AgentRunInput, AgentRunStartOptions, HumanReply } from '@dudousxd/nestjs-agent-core';
4
4
  import { IncomingMessage, ServerResponse } from 'node:http';
5
5
  import '@dudousxd/nestjs-agent';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { d as OpenCodeClient, e as OpenCodeEvent, f as OpenCodeForm, g as OpenCodeFormValue, h as OpenCodeFormField, i as OpenCodeMcpEndpoint, O as OpenCodeTurns, a as OpenCodeEngineSettings } from './engine-Bij6AdoF.js';
2
- export { I as InMemoryOpenCodeSessionStore, M as Milestone, j as OpenCodeAmendment, k as OpenCodeCallContext, c as OpenCodeEngineOptions, b as OpenCodeHost, l as OpenCodeKeyValue, m as OpenCodeModelRef, n as OpenCodePermissionRequest, o as OpenCodePermissionRule, p as OpenCodeReplyMismatchError, q as OpenCodeRunResult, r as OpenCodeServer, s as OpenCodeSessionCreate, t as OpenCodeSessionRef, u as OpenCodeSessionStore, v as OpenCodeToolCall, w as OpenCodeToolRefusedError, x as OpenCodeToolsClaims, y as OpenCodeToolsOptions, z as OpenCodeToolsTokens, A as OpenCodeTurn, B as OpenCodeTurnContext, P as PendingAsk, S as SessionHandle, T as TurnOutcome, C as keyValueOpenCodeSessionStore, D as openCode, E as openCodeControllers, F as openCodeProviders } from './engine-Bij6AdoF.js';
1
+ import { d as OpenCodeClient, e as OpenCodeEvent, f as OpenCodeForm, g as OpenCodeFormValue, h as OpenCodeFormField, i as OpenCodeMcpEndpoint, O as OpenCodeTurns, a as OpenCodeEngineSettings } from './engine-JnBbcRxh.js';
2
+ export { I as InMemoryOpenCodeSessionStore, M as Milestone, j as OpenCodeAmendment, k as OpenCodeCallContext, c as OpenCodeEngineOptions, b as OpenCodeHost, l as OpenCodeKeyValue, m as OpenCodeModelRef, n as OpenCodePermissionRequest, o as OpenCodePermissionRule, p as OpenCodeReplyMismatchError, q as OpenCodeRunResult, r as OpenCodeServer, s as OpenCodeSessionCreate, t as OpenCodeSessionRef, u as OpenCodeSessionStore, v as OpenCodeToolCall, w as OpenCodeToolRefusedError, x as OpenCodeToolsClaims, y as OpenCodeToolsOptions, z as OpenCodeToolsTokens, A as OpenCodeTurn, B as OpenCodeTurnContext, C as OpenCodeUsage, P as PendingAsk, S as SessionHandle, D as StepCost, E as StepModel, T as TurnOutcome, F as addUsage, G as emptyUsage, H as keyValueOpenCodeSessionStore, J as openCode, K as openCodeControllers, L as openCodeProviders, N as usageOf } from './engine-JnBbcRxh.js';
3
3
  import { ElicitationRequest, ElicitationQuestion, AgentRunner, AgentRunInput, AgentRunStartOptions, HumanReply } from '@dudousxd/nestjs-agent-core';
4
4
  import { IncomingMessage, ServerResponse } from 'node:http';
5
5
  import '@dudousxd/nestjs-agent';
package/dist/index.js CHANGED
@@ -239,6 +239,41 @@ function toFormAnswer(form, answers = {}) {
239
239
  __name(toFormAnswer, "toFormAnswer");
240
240
 
241
241
  // src/turn.ts
242
+ function emptyUsage() {
243
+ return {
244
+ inputTokens: 0,
245
+ outputTokens: 0,
246
+ cacheReadTokens: 0,
247
+ cacheWriteTokens: 0,
248
+ reasoningTokens: 0,
249
+ costUsd: 0
250
+ };
251
+ }
252
+ __name(emptyUsage, "emptyUsage");
253
+ function addUsage(a, b) {
254
+ const x = a ?? emptyUsage();
255
+ const y = b ?? emptyUsage();
256
+ return {
257
+ inputTokens: x.inputTokens + (y.inputTokens ?? 0),
258
+ outputTokens: x.outputTokens + (y.outputTokens ?? 0),
259
+ cacheReadTokens: (x.cacheReadTokens ?? 0) + (y.cacheReadTokens ?? 0),
260
+ cacheWriteTokens: (x.cacheWriteTokens ?? 0) + (y.cacheWriteTokens ?? 0),
261
+ reasoningTokens: (x.reasoningTokens ?? 0) + (y.reasoningTokens ?? 0),
262
+ costUsd: x.costUsd + (y.costUsd ?? 0)
263
+ };
264
+ }
265
+ __name(addUsage, "addUsage");
266
+ function subUsage(a, b) {
267
+ return {
268
+ inputTokens: a.inputTokens - b.inputTokens,
269
+ outputTokens: a.outputTokens - b.outputTokens,
270
+ cacheReadTokens: a.cacheReadTokens - b.cacheReadTokens,
271
+ cacheWriteTokens: a.cacheWriteTokens - b.cacheWriteTokens,
272
+ reasoningTokens: a.reasoningTokens - b.reasoningTokens,
273
+ costUsd: a.costUsd - b.costUsd
274
+ };
275
+ }
276
+ __name(subUsage, "subUsage");
242
277
  var ASK_TOOL_NAME = "ask";
243
278
  var FAILED_STATUSES = /* @__PURE__ */ new Set([
244
279
  "error",
@@ -304,11 +339,11 @@ var OpenCodeTurn = class {
304
339
  hidden = /* @__PURE__ */ new Set();
305
340
  milestones = [];
306
341
  waiting;
307
- usage = {
308
- inputTokens: 0,
309
- outputTokens: 0
310
- };
311
- costUsd;
342
+ /** What this turn object saw spent, and how much of it a milestone already reported. */
343
+ spent = emptyUsage();
344
+ reported = emptyUsage();
345
+ priced = false;
346
+ model;
312
347
  stepOpen = false;
313
348
  sawText = false;
314
349
  separator = false;
@@ -318,6 +353,11 @@ var OpenCodeTurn = class {
318
353
  stepError;
319
354
  constructor(a) {
320
355
  this.a = a;
356
+ this.model = modelOfLabel(a.input.model);
357
+ }
358
+ /** What the run spent so far: carried from before this turn object, plus what it saw. */
359
+ totalUsage() {
360
+ return addUsage(this.a.carried, this.spent);
321
361
  }
322
362
  get sessionId() {
323
363
  return this.a.sessionId;
@@ -400,7 +440,15 @@ var OpenCodeTurn = class {
400
440
  this.chain = this.chain.then(() => ask.kind === "approval" ? this.decided(ask, reply, tell) : this.answered(ask, reply, tell)).then(resolve, reject);
401
441
  });
402
442
  }
403
- reach(milestone) {
443
+ reach(reached) {
444
+ const usage = subUsage(this.spent, this.reported);
445
+ this.reported = {
446
+ ...this.spent
447
+ };
448
+ const milestone = {
449
+ ...reached,
450
+ usage
451
+ };
404
452
  const waiting = this.waiting;
405
453
  if (waiting !== void 0) {
406
454
  this.waiting = void 0;
@@ -556,9 +604,22 @@ var OpenCodeTurn = class {
556
604
  }
557
605
  }
558
606
  async onEvent(e) {
559
- if (this.ended) return;
560
607
  const d = e.data ?? {};
608
+ if (e.type === "session.usage.recorded") {
609
+ await this.usageRecorded(d);
610
+ return;
611
+ }
612
+ if (this.ended) return;
561
613
  switch (e.type) {
614
+ case "session.step.started":
615
+ case "session.model.selected": {
616
+ const model = d.model;
617
+ if (typeof model?.id === "string" && typeof model.providerID === "string") this.model = {
618
+ providerID: model.providerID,
619
+ id: model.id
620
+ };
621
+ return;
622
+ }
562
623
  case "session.text.delta": {
563
624
  const delta = typeof d.delta === "string" ? d.delta : "";
564
625
  if (!delta) return;
@@ -712,41 +773,63 @@ ${delta}` : delta;
712
773
  });
713
774
  }
714
775
  }
715
- async stepEnded(d) {
716
- const usage = {
717
- inputTokens: Number(d.tokens?.input) || 0,
718
- outputTokens: Number(d.tokens?.output) || 0,
719
- ...Number(d.tokens?.cache?.read) ? {
720
- cacheReadTokens: Number(d.tokens.cache.read)
721
- } : {},
722
- ...Number(d.tokens?.cache?.write) ? {
723
- cacheWriteTokens: Number(d.tokens.cache.write)
724
- } : {}
725
- };
726
- const costUsd = typeof d.cost === "number" ? d.cost : void 0;
727
- this.usage.inputTokens += usage.inputTokens;
728
- this.usage.outputTokens += usage.outputTokens;
729
- if (costUsd !== void 0) this.costUsd = (this.costUsd ?? 0) + costUsd;
776
+ modelId() {
777
+ return this.model !== void 0 ? `${this.model.providerID}/${this.model.id}` : this.a.modelLabel;
778
+ }
779
+ /** One model call's usage and cost, into the ledger and the run's total. */
780
+ async spend(d, purpose) {
781
+ const usage = usageOf(d.tokens);
782
+ const reportedCostUsd = typeof d.cost === "number" && Number.isFinite(d.cost) && d.cost >= 0 ? d.cost : void 0;
783
+ const fallback = reportedCostUsd !== void 0 ? {
784
+ costUsd: reportedCostUsd,
785
+ costSource: "provider"
786
+ } : {};
787
+ let cost = fallback;
788
+ if (this.a.priceStep !== void 0) {
789
+ cost = await this.a.priceStep({
790
+ model: this.model,
791
+ usage,
792
+ reportedCostUsd
793
+ }).catch(() => fallback);
794
+ }
795
+ this.spent = addUsage(this.spent, {
796
+ inputTokens: usage.inputTokens,
797
+ outputTokens: usage.outputTokens,
798
+ cacheReadTokens: usage.cacheReadTokens ?? 0,
799
+ cacheWriteTokens: usage.cacheWriteTokens ?? 0,
800
+ reasoningTokens: usage.reasoningTokens ?? 0,
801
+ costUsd: cost.costUsd ?? 0
802
+ });
803
+ if (cost.costUsd !== void 0) this.priced = true;
730
804
  await this.a.store.recordUsage({
731
805
  threadId: this.a.input.threadId,
732
806
  actorRef: this.a.input.actor.id,
733
- modelId: this.a.modelLabel,
734
- purpose: "chat",
807
+ modelId: this.modelId(),
808
+ purpose,
735
809
  usage,
736
- ...costUsd !== void 0 ? {
737
- costUsd
738
- } : {}
810
+ ...cost
739
811
  });
812
+ return {
813
+ usage,
814
+ cost
815
+ };
816
+ }
817
+ /** `session.usage.recorded`: a call OpenCode made besides the steps (the title, a compaction). */
818
+ async usageRecorded(d) {
819
+ await this.spend(d, d.source === "title" ? "title" : "history_summary");
820
+ }
821
+ async stepEnded(d) {
822
+ const { usage, cost } = await this.spend(d, "chat");
740
823
  if (!this.stepOpen) return;
741
824
  this.stepOpen = false;
742
825
  await this.write({
743
826
  kind: "step-finish",
744
827
  usage,
745
- costUsd: costUsd ?? null,
828
+ costUsd: cost.costUsd ?? null,
746
829
  ...this.segment.reasoningMs > 0 ? {
747
830
  reasoningMs: this.segment.reasoningMs
748
831
  } : {},
749
- model: this.a.modelLabel
832
+ model: this.modelId()
750
833
  });
751
834
  }
752
835
  /**
@@ -765,12 +848,7 @@ ${delta}` : delta;
765
848
  content: s.text,
766
849
  runId: this.a.runId,
767
850
  ...final ? {
768
- usage: {
769
- ...this.usage,
770
- ...this.costUsd !== void 0 ? {
771
- costUsd: this.costUsd
772
- } : {}
773
- }
851
+ usage: this.messageUsage()
774
852
  } : {},
775
853
  ...this.a.input.agentName !== void 0 ? {
776
854
  agentName: this.a.input.agentName
@@ -798,6 +876,26 @@ ${delta}` : delta;
798
876
  ]);
799
877
  return message.id;
800
878
  }
879
+ /** The run's usage as its last message carries it: the whole run, cache tokens included. */
880
+ messageUsage() {
881
+ const total = this.totalUsage();
882
+ return {
883
+ inputTokens: total.inputTokens,
884
+ outputTokens: total.outputTokens,
885
+ ...total.cacheReadTokens > 0 ? {
886
+ cacheReadTokens: total.cacheReadTokens
887
+ } : {},
888
+ ...total.cacheWriteTokens > 0 ? {
889
+ cacheWriteTokens: total.cacheWriteTokens
890
+ } : {},
891
+ ...total.reasoningTokens > 0 ? {
892
+ reasoningTokens: total.reasoningTokens
893
+ } : {},
894
+ ...this.priced || (this.a.carried?.costUsd ?? 0) > 0 ? {
895
+ costUsd: total.costUsd
896
+ } : {}
897
+ };
898
+ }
801
899
  /** A request recorded by an earlier process (a turn resumed after a restart). */
802
900
  async alreadyRecorded(id) {
803
901
  return (await this.a.store.toolCallApproval?.(id) ?? null) !== null;
@@ -1167,6 +1265,35 @@ ${delta}` : delta;
1167
1265
  });
1168
1266
  }
1169
1267
  };
1268
+ function modelOfLabel(label) {
1269
+ const slash = label?.indexOf("/") ?? -1;
1270
+ if (label === void 0 || slash <= 0 || slash === label.length - 1) return void 0;
1271
+ return {
1272
+ providerID: label.slice(0, slash),
1273
+ id: label.slice(slash + 1)
1274
+ };
1275
+ }
1276
+ __name(modelOfLabel, "modelOfLabel");
1277
+ function usageOf(tokens) {
1278
+ const n = /* @__PURE__ */ __name((v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0, "n");
1279
+ const read = n(tokens?.cache?.read);
1280
+ const write = n(tokens?.cache?.write);
1281
+ const reasoning = n(tokens?.reasoning);
1282
+ return {
1283
+ inputTokens: n(tokens?.input) + read + write,
1284
+ outputTokens: n(tokens?.output),
1285
+ ...read > 0 ? {
1286
+ cacheReadTokens: read
1287
+ } : {},
1288
+ ...write > 0 ? {
1289
+ cacheWriteTokens: write
1290
+ } : {},
1291
+ ...reasoning > 0 ? {
1292
+ reasoningTokens: reasoning
1293
+ } : {}
1294
+ };
1295
+ }
1296
+ __name(usageOf, "usageOf");
1170
1297
 
1171
1298
  // src/mcp.ts
1172
1299
  var LOOP_SERVED_KINDS = /* @__PURE__ */ new Set([
@@ -1431,7 +1558,7 @@ import { Inject as Inject3, Injectable as Injectable2 } from "@nestjs/common";
1431
1558
  // src/turns.ts
1432
1559
  import { randomUUID } from "crypto";
1433
1560
  import { AGENT_CHAT_QUEUE } from "@dudousxd/nestjs-agent";
1434
- import { AGENT_DEPS_FACTORY, AGENT_MEMORY, AGENT_SINK, AGENT_SKILLS, AGENT_STORE, AGENT_TOOL_REGISTRY, DEFAULT_MAX_FACT_CHARS, DefaultApprovalPolicy, RUN_ENDED_BEFORE_TOOL_CALL, actorScope, buildMemoryBlock, defaultScopeResolver, encodeStreamEvent as encodeStreamEvent2, memoryWriteVerdict, publishAgentRunFailed, releaseThreadRun, resolveMemoryDigest, resolveSkillCatalog, streamFailure } from "@dudousxd/nestjs-agent-core";
1561
+ import { AGENT_DEPS_FACTORY, AGENT_MEMORY, AGENT_OPTIONS, AGENT_PRICING_STORE, AGENT_SINK, AGENT_SKILLS, AGENT_STORE, AGENT_TOOL_REGISTRY, DEFAULT_MAX_FACT_CHARS, DefaultApprovalPolicy, ModelPriceResolver, RUN_ENDED_BEFORE_TOOL_CALL, actorScope, buildMemoryBlock, defaultScopeResolver, encodeStreamEvent as encodeStreamEvent2, memoryWriteVerdict, parseBedrockModelId, publishAgentRunFailed, releaseThreadRun, resolveMemoryDigest, resolveSkillCatalog, resolveUsageCost, streamFailure } from "@dudousxd/nestjs-agent-core";
1435
1562
  import { Inject as Inject2, Injectable, Logger as Logger2, Optional } from "@nestjs/common";
1436
1563
 
1437
1564
  // src/event-hub.ts
@@ -1598,7 +1725,7 @@ var OpenCodeTurns = class {
1598
1725
  spent = /* @__PURE__ */ new Set();
1599
1726
  /** When the tools endpoint was last registered for a location (`server|boot|directory|actor`). */
1600
1727
  toolsIssued = /* @__PURE__ */ new Map();
1601
- constructor(host, sessions, settings, store, sink, deps, registry, skills, memory, queue, toolsTokens2) {
1728
+ constructor(host, sessions, settings, store, sink, deps, registry, skills, memory, queue, toolsTokens2, pricingStore, agentOptions) {
1602
1729
  this.host = host;
1603
1730
  this.sessions = sessions;
1604
1731
  this.settings = settings;
@@ -1610,6 +1737,30 @@ var OpenCodeTurns = class {
1610
1737
  this.memory = memory;
1611
1738
  this.queue = queue;
1612
1739
  this.toolsTokens = toolsTokens2;
1740
+ this.prices = new ModelPriceResolver({
1741
+ pricingStore,
1742
+ catalog: agentOptions?.priceCatalog,
1743
+ log: {
1744
+ info: /* @__PURE__ */ __name((message) => this.logger.log(message), "info"),
1745
+ warn: /* @__PURE__ */ __name((message) => this.logger.warn(message), "warn")
1746
+ }
1747
+ });
1748
+ }
1749
+ prices;
1750
+ /**
1751
+ * What a model call OpenCode made cost, by the same rule the library's loop records its own steps
1752
+ * at: a reported figure wins, else an estimate off the model's price — see
1753
+ * {@link OpenCodeEngineSettings.cost}.
1754
+ */
1755
+ async priceStep(step) {
1756
+ const { model, usage, reportedCostUsd } = step;
1757
+ const tokens = usage.inputTokens + usage.outputTokens;
1758
+ const reported = reportedCostUsd !== void 0 && (reportedCostUsd > 0 || tokens === 0);
1759
+ if (reported && this.settings.cost !== "estimate") {
1760
+ return resolveUsageCost(usage, reportedCostUsd, void 0);
1761
+ }
1762
+ const price = model !== void 0 && tokens > 0 ? await this.prices.priceFor(priceRef(model)) : void 0;
1763
+ return resolveUsageCost(usage, price === void 0 ? reportedCostUsd : void 0, price);
1613
1764
  }
1614
1765
  /** The tools server's name in OpenCode. */
1615
1766
  get toolsServer() {
@@ -2199,8 +2350,8 @@ ${body}
2199
2350
  }
2200
2351
  // ---- prompt / observe / reply --------------------------------------------------------------
2201
2352
  /** Start listening to the session (so nothing it says is missed), then send the user message. */
2202
- async prompt(runId, input, handle) {
2203
- const live = await this.ensureLive(runId, input, handle, false);
2353
+ async prompt(runId, input, handle, spent) {
2354
+ const live = await this.ensureLive(runId, input, handle, false, spent);
2204
2355
  try {
2205
2356
  const prompt = await this.host.promptFor?.({
2206
2357
  input,
@@ -2230,9 +2381,12 @@ ${body}
2230
2381
  * Wait for the turn's next milestone. In a process that was not following the turn (a resumed
2231
2382
  * durable run), the turn is rebuilt and first catches up on requests OpenCode raised meanwhile;
2232
2383
  * `session.wait` is the safety net for a terminal event that was missed.
2384
+ *
2385
+ * `spent` is what the run spent before (the sum of the `usage` of its earlier milestones): a turn
2386
+ * rebuilt here starts from it, so the answer it persists carries the whole run's usage.
2233
2387
  */
2234
- async observe(runId, input, handle) {
2235
- const live = await this.ensureLive(runId, input, handle, true);
2388
+ async observe(runId, input, handle, spent) {
2389
+ const live = await this.ensureLive(runId, input, handle, true, spent);
2236
2390
  const { turn, client } = live;
2237
2391
  if (turn.hasMilestone()) return this.reached(runId, input, handle, await turn.next());
2238
2392
  let done = false;
@@ -2271,23 +2425,23 @@ ${body}
2271
2425
  * request is gone with it: a new session is opened, told what the person decided, and prompted
2272
2426
  * to go on.
2273
2427
  */
2274
- async reply(runId, input, handle, ask, reply) {
2428
+ async reply(runId, input, handle, ask, reply, spent) {
2275
2429
  const server = await this.host.server(input.actor);
2276
2430
  if (server.key !== handle.serverKey || server.bootId !== handle.bootId) {
2277
- return this.continueAfterRestart(runId, input, server, ask, reply);
2431
+ return this.continueAfterRestart(runId, input, server, ask, reply, spent);
2278
2432
  }
2279
- const live = await this.ensureLive(runId, input, handle, false);
2433
+ const live = await this.ensureLive(runId, input, handle, false, spent);
2280
2434
  await live.turn.decide(ask, reply);
2281
2435
  return handle;
2282
2436
  }
2283
- async continueAfterRestart(runId, input, server, ask, reply) {
2437
+ async continueAfterRestart(runId, input, server, ask, reply, spent) {
2284
2438
  const approved = "approved" in reply && reply.approved === true;
2285
2439
  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.`;
2286
2440
  const thread = await this.store.getThread(input.threadId);
2287
2441
  const handle = await this.createSession(runId, input, server, thread?.messages ?? [], note);
2288
2442
  await this.refreshInstructions(runId, input, server.client, handle.sessionId);
2289
2443
  this.drop(runId);
2290
- const live = await this.ensureLive(runId, input, handle, false);
2444
+ const live = await this.ensureLive(runId, input, handle, false, spent);
2291
2445
  await live.turn.decide(ask, reply, {
2292
2446
  tellOpenCode: false
2293
2447
  });
@@ -2307,7 +2461,7 @@ ${body}
2307
2461
  sessionID: known.sessionId
2308
2462
  });
2309
2463
  }
2310
- async ensureLive(runId, input, handle, catchUp) {
2464
+ async ensureLive(runId, input, handle, catchUp, spent) {
2311
2465
  const current = this.live.get(runId);
2312
2466
  if (current !== void 0 && current.handle.sessionId === handle.sessionId) return current;
2313
2467
  current?.stop();
@@ -2333,6 +2487,10 @@ ${body}
2333
2487
  } : {}
2334
2488
  }), "approvalFor"),
2335
2489
  modelLabel: input.model ?? "opencode",
2490
+ priceStep: /* @__PURE__ */ __name((step) => this.priceStep(step), "priceStep"),
2491
+ ...spent !== void 0 ? {
2492
+ carried: spent
2493
+ } : {},
2336
2494
  logger: this.logger,
2337
2495
  onGranted: /* @__PURE__ */ __name((action, id) => {
2338
2496
  const grants = this.grants.get(runId) ?? [];
@@ -2379,20 +2537,14 @@ ${body}
2379
2537
  }
2380
2538
  return milestone;
2381
2539
  }
2382
- /** What the run produced, as the host's settle hooks see it. */
2383
- async runResult(runId, input, outcome, durationMs) {
2540
+ /**
2541
+ * What the run produced, as the host's settle hooks see it. `usage` is what the run spent — the
2542
+ * runner's sum of its milestones' usage, which a resumed run reads back from its journal — else
2543
+ * what the turn this process follows saw.
2544
+ */
2545
+ async runResult(runId, input, outcome, durationMs, usage) {
2384
2546
  const thread = await this.store.getThread(input.threadId);
2385
2547
  const messages = (thread?.messages ?? []).filter((m) => m.role === "assistant" && m.runId === runId);
2386
- const usage = {
2387
- inputTokens: 0,
2388
- outputTokens: 0,
2389
- costUsd: 0
2390
- };
2391
- for (const m of messages) {
2392
- usage.inputTokens += m.usage?.inputTokens ?? 0;
2393
- usage.outputTokens += m.usage?.outputTokens ?? 0;
2394
- usage.costUsd += m.usage?.costUsd ?? 0;
2395
- }
2396
2548
  return {
2397
2549
  runId,
2398
2550
  input,
@@ -2437,15 +2589,31 @@ ${body}
2437
2589
  this.logger.warn(`onSettled failed: ${errorText(error, "error")}`);
2438
2590
  }
2439
2591
  }
2440
- /** End the run's stream and bookkeeping for how its execution ended. */
2441
- async settle(runId, input, outcome, durationMs) {
2592
+ /**
2593
+ * What the run spent: the runner's sum of its milestones, or what the turn this process follows
2594
+ * saw (which starts from that sum) when it saw more — a run that failed or was cancelled between
2595
+ * two milestones.
2596
+ */
2597
+ spentBy(runId, spent) {
2598
+ const seen = this.live.get(runId)?.turn.totalUsage();
2599
+ if (spent === void 0) return seen ?? emptyUsage();
2600
+ if (seen === void 0) return spent;
2601
+ const tokens = /* @__PURE__ */ __name((u) => u.inputTokens + u.outputTokens, "tokens");
2602
+ return tokens(seen) > tokens(spent) ? seen : spent;
2603
+ }
2604
+ /**
2605
+ * End the run's stream and bookkeeping for how its execution ended. `spent` is what the run spent
2606
+ * (the sum of its milestones' `usage`); omit → what the turn this process follows saw.
2607
+ */
2608
+ async settle(runId, input, outcome, durationMs, spent) {
2442
2609
  if (outcome.status === "failed") {
2443
- await this.settleFailed(runId, input, outcome.error, durationMs);
2610
+ await this.settleFailed(runId, input, outcome.error, durationMs, spent);
2444
2611
  return;
2445
2612
  }
2613
+ const usage = this.spentBy(runId, spent);
2446
2614
  this.forget(runId);
2447
2615
  const writer = await this.sink.open(runId);
2448
- const result = await this.runResult(runId, input, outcome, durationMs);
2616
+ const result = await this.runResult(runId, input, outcome, durationMs, usage);
2449
2617
  await this.amend(writer, result);
2450
2618
  if (outcome.status === "interrupted") {
2451
2619
  this.logger.log(`agent run ${runId} cancelled`);
@@ -2473,13 +2641,14 @@ ${body}
2473
2641
  }
2474
2642
  await this.settled(result);
2475
2643
  }
2476
- async settleFailed(runId, input, error, durationMs = 0) {
2644
+ async settleFailed(runId, input, error, durationMs = 0, spent) {
2645
+ const usage = this.spentBy(runId, spent);
2477
2646
  this.forget(runId);
2478
2647
  const writer = await this.sink.open(runId);
2479
2648
  const result = await this.runResult(runId, input, {
2480
2649
  status: "failed",
2481
2650
  error
2482
- }, durationMs);
2651
+ }, durationMs, usage);
2483
2652
  const worded = (await this.amend(writer, result)).error;
2484
2653
  const message = worded ?? streamFailure(new Error(error)).message;
2485
2654
  this.logger.error(`agent run ${runId} failed: ${error}`);
@@ -2549,6 +2718,10 @@ OpenCodeTurns = _ts_decorate2([
2549
2718
  _ts_param2(9, Inject2(AGENT_CHAT_QUEUE)),
2550
2719
  _ts_param2(10, Optional()),
2551
2720
  _ts_param2(10, Inject2(OPENCODE_TOOLS_TOKENS)),
2721
+ _ts_param2(11, Optional()),
2722
+ _ts_param2(11, Inject2(AGENT_PRICING_STORE)),
2723
+ _ts_param2(12, Optional()),
2724
+ _ts_param2(12, Inject2(AGENT_OPTIONS)),
2552
2725
  _ts_metadata2("design:type", Function),
2553
2726
  _ts_metadata2("design:paramtypes", [
2554
2727
  typeof OpenCodeHost === "undefined" ? Object : OpenCodeHost,
@@ -2561,6 +2734,8 @@ OpenCodeTurns = _ts_decorate2([
2561
2734
  typeof SkillsConfig === "undefined" ? Object : SkillsConfig,
2562
2735
  typeof MemoryConfig === "undefined" ? Object : MemoryConfig,
2563
2736
  typeof ChatQueueService === "undefined" ? Object : ChatQueueService,
2737
+ Object,
2738
+ typeof AgentPricingStore === "undefined" ? Object : AgentPricingStore,
2564
2739
  Object
2565
2740
  ])
2566
2741
  ], OpenCodeTurns);
@@ -2603,6 +2778,19 @@ function uiEmitter(scope, push) {
2603
2778
  };
2604
2779
  }
2605
2780
  __name(uiEmitter, "uiEmitter");
2781
+ function priceRef(model) {
2782
+ const aliases = [
2783
+ model.id
2784
+ ];
2785
+ const tail = model.id.slice(model.id.lastIndexOf("/") + 1);
2786
+ if (tail !== model.id && parseBedrockModelId(tail) !== void 0) aliases.push(tail);
2787
+ return {
2788
+ modelId: `${model.providerID}/${model.id}`,
2789
+ provider: model.providerID,
2790
+ aliases
2791
+ };
2792
+ }
2793
+ __name(priceRef, "priceRef");
2606
2794
 
2607
2795
  // src/runner.ts
2608
2796
  function _ts_decorate3(decorators, target, key, desc) {
@@ -2704,18 +2892,20 @@ var OpenCodeAgentRunner = class {
2704
2892
  async run(runId, input) {
2705
2893
  const started = Date.now();
2706
2894
  this.inputs.set(runId, input);
2895
+ let spent = emptyUsage();
2707
2896
  try {
2708
2897
  let handle = await this.turns.begin(runId, input);
2709
2898
  if (this.cancelled.has(runId)) throw new RunCancelledError();
2710
- await this.turns.prompt(runId, input, handle);
2899
+ await this.turns.prompt(runId, input, handle, spent);
2711
2900
  for (; ; ) {
2712
- const milestone = await this.turns.observe(runId, input, handle);
2901
+ const milestone = await this.turns.observe(runId, input, handle, spent);
2902
+ spent = addUsage(spent, milestone.usage);
2713
2903
  if (milestone.kind === "finished") {
2714
- await this.turns.settle(runId, input, milestone.outcome, Date.now() - started);
2904
+ await this.turns.settle(runId, input, milestone.outcome, Date.now() - started, spent);
2715
2905
  return;
2716
2906
  }
2717
2907
  const reply = await this.park(runId, milestone.ask.id, milestone.ask.kind === "approval" ? "approval" : "answers", milestone.timeoutMs);
2718
- handle = await this.turns.reply(runId, input, handle, milestone.ask, reply);
2908
+ handle = await this.turns.reply(runId, input, handle, milestone.ask, reply, spent);
2719
2909
  }
2720
2910
  } catch (error) {
2721
2911
  if (error instanceof RunCancelledError) {
@@ -2725,7 +2915,7 @@ var OpenCodeAgentRunner = class {
2725
2915
  }, Date.now() - started);
2726
2916
  return;
2727
2917
  }
2728
- await this.turns.settleFailed(runId, input, errorText(error, "the turn failed"));
2918
+ await this.turns.settleFailed(runId, input, errorText(error, "the turn failed"), Date.now() - started);
2729
2919
  } finally {
2730
2920
  this.inputs.delete(runId);
2731
2921
  }
@@ -2877,6 +3067,8 @@ export {
2877
3067
  OpenCodeToolsTokens,
2878
3068
  OpenCodeTurn,
2879
3069
  OpenCodeTurns,
3070
+ addUsage,
3071
+ emptyUsage,
2880
3072
  keyValueOpenCodeSessionStore,
2881
3073
  openCode,
2882
3074
  openCodeControllers,
@@ -2884,6 +3076,7 @@ export {
2884
3076
  sessionOf,
2885
3077
  toElicitation,
2886
3078
  toFormAnswer,
2887
- toQuestion
3079
+ toQuestion,
3080
+ usageOf
2888
3081
  };
2889
3082
  //# sourceMappingURL=index.js.map