@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.
@@ -1,6 +1,6 @@
1
1
  import { AgentDepsFactory, ChatQueueService, AgentEngine } from '@dudousxd/nestjs-agent';
2
2
  import { ConflictException, Logger, OnApplicationShutdown, Type, InjectionToken, Provider } from '@nestjs/common';
3
- import { ElicitationRequest, AgentRunInput, SinkWriter, AgentStore, ApprovalRequirement, AgentUiComponent, HumanReply, Actor, StoredMessage, ToolRegistry, TokenStreamSink, SkillsConfig, MemoryConfig, RolesPolicy, RememberToolInput, AiToolCtx } from '@dudousxd/nestjs-agent-core';
3
+ import { ElicitationRequest, CostSource, AgentRunInput, SinkWriter, AgentStore, ApprovalRequirement, MessageUsage, AgentUiComponent, HumanReply, Actor, StoredMessage, ToolRegistry, TokenStreamSink, SkillsConfig, MemoryConfig, AgentPricingStore, PriceCatalogOptions, RolesPolicy, RememberToolInput, AiToolCtx } from '@dudousxd/nestjs-agent-core';
4
4
  import { IncomingMessage, ServerResponse } from 'node:http';
5
5
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
6
 
@@ -40,6 +40,9 @@ interface OpenCodeClient {
40
40
  metadata?: {
41
41
  readonly [key: string]: OpenCodeJson;
42
42
  };
43
+ /** What the session spent so far, every model call OpenCode made for it (the title too). */
44
+ cost?: number;
45
+ tokens?: OpenCodeTokens;
43
46
  }>;
44
47
  /** Resolves when the session goes idle. Optional: a safety net for a missed terminal event. */
45
48
  wait?(args: {
@@ -204,6 +207,16 @@ interface OpenCodeFormField {
204
207
  /** The user may type their own answer instead of picking one of `options`. */
205
208
  custom?: boolean;
206
209
  }
210
+ /** A token count as OpenCode reports it: `input` is the uncached input, the cache beside it. */
211
+ interface OpenCodeTokens {
212
+ input: number;
213
+ output: number;
214
+ reasoning?: number;
215
+ cache?: {
216
+ read: number;
217
+ write: number;
218
+ };
219
+ }
207
220
 
208
221
  /** How an OpenCode execution ended. */
209
222
  type TurnOutcome = {
@@ -234,14 +247,46 @@ type PendingAsk = {
234
247
  request: ElicitationRequest;
235
248
  messageId?: string;
236
249
  };
237
- /** Where a turn stands: it needs a person, or it is over. */
250
+ /**
251
+ * What a turn spent. `inputTokens` is the whole input side, cached input included (as the library's
252
+ * `MessageUsage` counts it — OpenCode's own `tokens.input` leaves the cache out); `cacheReadTokens`,
253
+ * `cacheWriteTokens` and `reasoningTokens` are subsets. `costUsd` sums every priced call.
254
+ */
255
+ interface OpenCodeUsage {
256
+ inputTokens: number;
257
+ outputTokens: number;
258
+ cacheReadTokens: number;
259
+ cacheWriteTokens: number;
260
+ reasoningTokens: number;
261
+ costUsd: number;
262
+ }
263
+ declare function emptyUsage(): OpenCodeUsage;
264
+ /** `a + b`; a missing side (a milestone journaled before usage travelled) counts as nothing. */
265
+ declare function addUsage(a: OpenCodeUsage | undefined, b: OpenCodeUsage | undefined): OpenCodeUsage;
266
+ /** The model a step ran on, as OpenCode names it (`session.step.started`). */
267
+ interface StepModel {
268
+ providerID: string;
269
+ id: string;
270
+ }
271
+ /** What a model call cost, and where the figure came from. */
272
+ interface StepCost {
273
+ costUsd?: number;
274
+ costSource?: CostSource;
275
+ }
276
+ /**
277
+ * Where a turn stands: it needs a person, or it is over. `usage` is what the turn spent since its
278
+ * previous milestone — journaled with the milestone, so a run resumed in another process still adds
279
+ * up what every process saw.
280
+ */
238
281
  type Milestone = {
239
282
  kind: 'ask';
240
283
  ask: PendingAsk;
241
284
  timeoutMs?: number;
285
+ usage?: OpenCodeUsage;
242
286
  } | {
243
287
  kind: 'finished';
244
288
  outcome: TurnOutcome;
289
+ usage?: OpenCodeUsage;
245
290
  };
246
291
  interface OpenCodeTurnArgs {
247
292
  runId: string;
@@ -252,8 +297,19 @@ interface OpenCodeTurnArgs {
252
297
  store: AgentStore;
253
298
  /** What an action needs before it runs (the module's `ApprovalPolicy`). */
254
299
  approvalFor: (action: string) => Promise<ApprovalRequirement>;
255
- /** The model label usage is recorded under. */
300
+ /** The model label usage is recorded under until OpenCode names the step's model. */
256
301
  modelLabel: string;
302
+ /**
303
+ * What a model call cost: OpenCode's reported figure, or the library's estimate. Omit → OpenCode's
304
+ * figure as reported.
305
+ */
306
+ priceStep?: (step: {
307
+ model: StepModel | undefined;
308
+ usage: MessageUsage;
309
+ reportedCostUsd: number | undefined;
310
+ }) => Promise<StepCost>;
311
+ /** What the run spent before this turn object existed (an earlier process, an earlier session). */
312
+ carried?: OpenCodeUsage;
257
313
  logger: Logger;
258
314
  /**
259
315
  * An action OpenCode was allowed to run (a person approved it, or the policy did): the approval
@@ -296,8 +352,15 @@ declare class OpenCodeTurn {
296
352
  private readonly hidden;
297
353
  private readonly milestones;
298
354
  private waiting;
299
- private readonly usage;
300
- private costUsd;
355
+ /** What this turn object saw spent, and how much of it a milestone already reported. */
356
+ private spent;
357
+ private reported;
358
+ private priced;
359
+ private model;
360
+ /** What the session had spent when this turn prompted it; what OpenCode reported since. */
361
+ private baseline;
362
+ private readonly seen;
363
+ private compacted;
301
364
  private stepOpen;
302
365
  private sawText;
303
366
  private separator;
@@ -306,6 +369,18 @@ declare class OpenCodeTurn {
306
369
  private ended;
307
370
  private stepError;
308
371
  constructor(a: OpenCodeTurnArgs);
372
+ /**
373
+ * What the session had spent before this turn prompted it (`session.get`). When the execution
374
+ * ends, what the session spent since that this turn's events did not report — the title OpenCode
375
+ * generates, a compaction: OpenCode records those calls on the session without streaming them — is
376
+ * recorded as a usage row of its own.
377
+ */
378
+ setBaseline(info: {
379
+ cost?: number;
380
+ tokens?: OpenCodeTokens;
381
+ } | undefined): void;
382
+ /** What the run spent so far: carried from before this turn object, plus what it saw. */
383
+ totalUsage(): OpenCodeUsage;
309
384
  get sessionId(): string;
310
385
  get finished(): boolean;
311
386
  /** Feed one of the session's events. */
@@ -348,12 +423,19 @@ declare class OpenCodeTurn {
348
423
  private innerCalls;
349
424
  private onEvent;
350
425
  private settleTool;
426
+ private modelId;
427
+ /** One model call's usage and cost, into the ledger and the run's total. */
428
+ private spend;
429
+ /** `session.usage.recorded`: a call OpenCode made besides the steps (the title, a compaction). */
430
+ private usageRecorded;
351
431
  private stepEnded;
352
432
  /**
353
433
  * Persist what the turn produced since the last flush as one assistant message — the message a
354
434
  * call put to a person hangs on, or the turn's last one. Returns its id.
355
435
  */
356
436
  private flush;
437
+ /** The run's usage as its last message carries it: the whole run, cache tokens included. */
438
+ private messageUsage;
357
439
  /** A request recorded by an earlier process (a turn resumed after a restart). */
358
440
  private alreadyRecorded;
359
441
  /** The persisted assistant message carrying call `id`, so its result lands on the same message. */
@@ -363,8 +445,16 @@ declare class OpenCodeTurn {
363
445
  private onForm;
364
446
  private answered;
365
447
  private addResult;
448
+ /** What the session spent since the baseline that no event of this turn reported. */
449
+ private unreported;
366
450
  private end;
367
451
  }
452
+ /**
453
+ * OpenCode's token counts as the library's `MessageUsage`. OpenCode reports the uncached input in
454
+ * `input` and the cache reads and writes beside it; the library counts the whole input side in
455
+ * `inputTokens`, the cache counts being subsets of it.
456
+ */
457
+ declare function usageOf(tokens: any): MessageUsage;
368
458
 
369
459
  /**
370
460
  * Where a turn runs and how its OpenCode session is set up — the part only the host knows. The
@@ -471,11 +561,12 @@ interface OpenCodeRunResult {
471
561
  text: string;
472
562
  /** The run's assistant messages, as stored. */
473
563
  messages: StoredMessage[];
474
- usage: {
475
- inputTokens: number;
476
- outputTokens: number;
477
- costUsd: number;
478
- };
564
+ /**
565
+ * What the run spent, over every model call OpenCode made for it (its steps, the title, a
566
+ * compaction) and every process that ran a part of it. `inputTokens` counts cached input too;
567
+ * `cacheReadTokens`/`cacheWriteTokens` are subsets of it.
568
+ */
569
+ usage: OpenCodeUsage;
479
570
  durationMs: number;
480
571
  }
481
572
  /** See {@link OpenCodeHost.beforeSettle}. */
@@ -648,6 +739,19 @@ interface OpenCodeEngineSettings {
648
739
  * parked on a person is not observing: this bounds one stretch of OpenCode working on its own.
649
740
  */
650
741
  turnTimeoutMs?: number;
742
+ /**
743
+ * Whose figure a model call is recorded at. OpenCode prices each call itself, off its model
744
+ * catalog (the `cost` its config declares, else models.dev's list price):
745
+ *
746
+ * - `'reported'` (default) — OpenCode's figure when it has one (`cost_source` `'provider'`); a call
747
+ * OpenCode priced at 0 (no price for the model) is estimated from the library's price instead
748
+ * (`priceCatalog`, the pricing store, the built-in GovCloud Bedrock table, models.dev;
749
+ * `cost_source` `'estimate'`).
750
+ * - `'estimate'` — the library's price whenever it has one, OpenCode's figure otherwise. For
751
+ * models OpenCode prices at a list price that is not yours (Bedrock in GovCloud, a negotiated
752
+ * rate) when you would rather not declare `cost` in OpenCode's config.
753
+ */
754
+ cost?: 'reported' | 'estimate';
651
755
  }
652
756
  /** A thread's session, as the steps of a turn pass it along (and a durable run journals it). */
653
757
  interface SessionHandle {
@@ -699,7 +803,20 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
699
803
  private readonly spent;
700
804
  /** When the tools endpoint was last registered for a location (`server|boot|directory|actor`). */
701
805
  private readonly toolsIssued;
702
- constructor(host: OpenCodeHost, sessions: OpenCodeSessionStore, settings: OpenCodeEngineSettings, store: AgentStore, sink: TokenStreamSink, deps: AgentDepsFactory, registry: ToolRegistry, skills?: SkillsConfig | undefined, memory?: MemoryConfig | undefined, queue?: ChatQueueService | undefined, toolsTokens?: (OpenCodeToolsTokens | null) | undefined);
806
+ constructor(host: OpenCodeHost, sessions: OpenCodeSessionStore, settings: OpenCodeEngineSettings, store: AgentStore, sink: TokenStreamSink, deps: AgentDepsFactory, registry: ToolRegistry, skills?: SkillsConfig | undefined, memory?: MemoryConfig | undefined, queue?: ChatQueueService | undefined, toolsTokens?: (OpenCodeToolsTokens | null) | undefined, pricingStore?: AgentPricingStore, agentOptions?: {
807
+ priceCatalog?: PriceCatalogOptions | false;
808
+ });
809
+ private readonly prices;
810
+ /**
811
+ * What a model call OpenCode made cost, by the same rule the library's loop records its own steps
812
+ * at: a reported figure wins, else an estimate off the model's price — see
813
+ * {@link OpenCodeEngineSettings.cost}.
814
+ */
815
+ priceStep(step: {
816
+ model: StepModel | undefined;
817
+ usage: MessageUsage;
818
+ reportedCostUsd: number | undefined;
819
+ }): Promise<StepCost>;
703
820
  /** The tools server's name in OpenCode. */
704
821
  get toolsServer(): string;
705
822
  /** The roles policy a turn's tools are gated by (the agent's own). */
@@ -784,20 +901,25 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
784
901
  */
785
902
  private addTools;
786
903
  /** Start listening to the session (so nothing it says is missed), then send the user message. */
787
- prompt(runId: string, input: AgentRunInput, handle: SessionHandle): Promise<void>;
904
+ prompt(runId: string, input: AgentRunInput, handle: SessionHandle, spent?: OpenCodeUsage): Promise<void>;
788
905
  /**
789
906
  * Wait for the turn's next milestone. In a process that was not following the turn (a resumed
790
907
  * durable run), the turn is rebuilt and first catches up on requests OpenCode raised meanwhile;
791
908
  * `session.wait` is the safety net for a terminal event that was missed.
909
+ *
910
+ * `spent` is what the run spent before (the sum of the `usage` of its earlier milestones): a turn
911
+ * rebuilt here starts from it, so the answer it persists carries the whole run's usage.
792
912
  */
793
- observe(runId: string, input: AgentRunInput, handle: SessionHandle): Promise<Milestone>;
913
+ observe(runId: string, input: AgentRunInput, handle: SessionHandle, spent?: OpenCodeUsage): Promise<Milestone>;
794
914
  /**
795
915
  * Hand a person's answer back to OpenCode. When the server restarted while the turn waited, the
796
916
  * request is gone with it: a new session is opened, told what the person decided, and prompted
797
917
  * to go on.
798
918
  */
799
- reply(runId: string, input: AgentRunInput, handle: SessionHandle, ask: PendingAsk, reply: HumanReply): Promise<SessionHandle>;
919
+ reply(runId: string, input: AgentRunInput, handle: SessionHandle, ask: PendingAsk, reply: HumanReply, spent?: OpenCodeUsage): Promise<SessionHandle>;
800
920
  private continueAfterRestart;
921
+ /** What a session spent so far (`session.get`), or nothing when OpenCode does not say. */
922
+ private sessionUsage;
801
923
  /** Interrupt the thread's session (cancel). */
802
924
  interrupt(input: AgentRunInput): Promise<void>;
803
925
  private ensureLive;
@@ -807,7 +929,11 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
807
929
  private forget;
808
930
  /** A person was asked something: tell the host (cards in other channels, wake-ups). */
809
931
  private reached;
810
- /** What the run produced, as the host's settle hooks see it. */
932
+ /**
933
+ * What the run produced, as the host's settle hooks see it. `usage` is what the run spent — the
934
+ * runner's sum of its milestones' usage, which a resumed run reads back from its journal — else
935
+ * what the turn this process follows saw.
936
+ */
811
937
  private runResult;
812
938
  /**
813
939
  * The host's last word on the answer before the stream ends: components to append (a guardrail
@@ -816,9 +942,18 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
816
942
  private amend;
817
943
  /** Delivery, telemetry, spend: after the run settled. Errors are the host's, never the run's. */
818
944
  private settled;
819
- /** End the run's stream and bookkeeping for how its execution ended. */
820
- settle(runId: string, input: AgentRunInput, outcome: TurnOutcome, durationMs: number): Promise<void>;
821
- settleFailed(runId: string, input: AgentRunInput, error: string, durationMs?: number): Promise<void>;
945
+ /**
946
+ * What the run spent: the runner's sum of its milestones, or what the turn this process follows
947
+ * saw (which starts from that sum) when it saw more — a run that failed or was cancelled between
948
+ * two milestones.
949
+ */
950
+ private spentBy;
951
+ /**
952
+ * End the run's stream and bookkeeping for how its execution ended. `spent` is what the run spent
953
+ * (the sum of its milestones' `usage`); omit → what the turn this process follows saw.
954
+ */
955
+ settle(runId: string, input: AgentRunInput, outcome: TurnOutcome, durationMs: number, spent?: OpenCodeUsage): Promise<void>;
956
+ settleFailed(runId: string, input: AgentRunInput, error: string, durationMs?: number, spent?: OpenCodeUsage): Promise<void>;
822
957
  /** Starts the next queued message of the thread; set by the runner (it owns `start`). */
823
958
  startNext: ((next: AgentRunInput, runId: string) => Promise<unknown>) | undefined;
824
959
  /** As `InlineAgentRunner`: hand the thread to its next queued message, and say so on the stream. */
@@ -846,4 +981,4 @@ declare function openCodeControllers(options: OpenCodeEngineSettings): Type<obje
846
981
  /** The providers every OpenCode engine needs: host, session store, settings and the turn steps. */
847
982
  declare function openCodeProviders(options: OpenCodeEngineOptions): Provider[];
848
983
 
849
- export { OpenCodeTurn as A, type OpenCodeTurnContext as B, keyValueOpenCodeSessionStore as C, openCode as D, openCodeControllers as E, openCodeProviders as F, InMemoryOpenCodeSessionStore as I, type Milestone as M, OpenCodeTurns as O, type PendingAsk as P, type SessionHandle as S, type TurnOutcome as T, type OpenCodeEngineSettings as a, type OpenCodeHost as b, type OpenCodeEngineOptions as c, type OpenCodeClient as d, type OpenCodeEvent as e, type OpenCodeForm as f, type OpenCodeFormValue as g, type OpenCodeFormField as h, OpenCodeMcpEndpoint as i, type OpenCodeAmendment as j, type OpenCodeCallContext as k, type OpenCodeKeyValue as l, type OpenCodeModelRef as m, type OpenCodePermissionRequest as n, type OpenCodePermissionRule as o, OpenCodeReplyMismatchError as p, type OpenCodeRunResult as q, type OpenCodeServer as r, type OpenCodeSessionCreate as s, type OpenCodeSessionRef as t, type OpenCodeSessionStore as u, type OpenCodeToolCall as v, OpenCodeToolRefusedError as w, type OpenCodeToolsClaims as x, type OpenCodeToolsOptions as y, OpenCodeToolsTokens as z };
984
+ export { OpenCodeTurn as A, type OpenCodeTurnContext as B, type OpenCodeUsage as C, type StepCost as D, type StepModel as E, addUsage as F, emptyUsage as G, keyValueOpenCodeSessionStore as H, InMemoryOpenCodeSessionStore as I, openCode as J, openCodeControllers as K, openCodeProviders as L, type Milestone as M, usageOf as N, OpenCodeTurns as O, type PendingAsk as P, type SessionHandle as S, type TurnOutcome as T, type OpenCodeEngineSettings as a, type OpenCodeHost as b, type OpenCodeEngineOptions as c, type OpenCodeClient as d, type OpenCodeEvent as e, type OpenCodeForm as f, type OpenCodeFormValue as g, type OpenCodeFormField as h, OpenCodeMcpEndpoint as i, type OpenCodeAmendment as j, type OpenCodeCallContext as k, type OpenCodeKeyValue as l, type OpenCodeModelRef as m, type OpenCodePermissionRequest as n, type OpenCodePermissionRule as o, OpenCodeReplyMismatchError as p, type OpenCodeRunResult as q, type OpenCodeServer as r, type OpenCodeSessionCreate as s, type OpenCodeSessionRef as t, type OpenCodeSessionStore as u, type OpenCodeToolCall as v, OpenCodeToolRefusedError as w, type OpenCodeToolsClaims as x, type OpenCodeToolsOptions as y, OpenCodeToolsTokens as z };
@@ -1,6 +1,6 @@
1
1
  import { AgentDepsFactory, ChatQueueService, AgentEngine } from '@dudousxd/nestjs-agent';
2
2
  import { ConflictException, Logger, OnApplicationShutdown, Type, InjectionToken, Provider } from '@nestjs/common';
3
- import { ElicitationRequest, AgentRunInput, SinkWriter, AgentStore, ApprovalRequirement, AgentUiComponent, HumanReply, Actor, StoredMessage, ToolRegistry, TokenStreamSink, SkillsConfig, MemoryConfig, RolesPolicy, RememberToolInput, AiToolCtx } from '@dudousxd/nestjs-agent-core';
3
+ import { ElicitationRequest, CostSource, AgentRunInput, SinkWriter, AgentStore, ApprovalRequirement, MessageUsage, AgentUiComponent, HumanReply, Actor, StoredMessage, ToolRegistry, TokenStreamSink, SkillsConfig, MemoryConfig, AgentPricingStore, PriceCatalogOptions, RolesPolicy, RememberToolInput, AiToolCtx } from '@dudousxd/nestjs-agent-core';
4
4
  import { IncomingMessage, ServerResponse } from 'node:http';
5
5
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
6
 
@@ -40,6 +40,9 @@ interface OpenCodeClient {
40
40
  metadata?: {
41
41
  readonly [key: string]: OpenCodeJson;
42
42
  };
43
+ /** What the session spent so far, every model call OpenCode made for it (the title too). */
44
+ cost?: number;
45
+ tokens?: OpenCodeTokens;
43
46
  }>;
44
47
  /** Resolves when the session goes idle. Optional: a safety net for a missed terminal event. */
45
48
  wait?(args: {
@@ -204,6 +207,16 @@ interface OpenCodeFormField {
204
207
  /** The user may type their own answer instead of picking one of `options`. */
205
208
  custom?: boolean;
206
209
  }
210
+ /** A token count as OpenCode reports it: `input` is the uncached input, the cache beside it. */
211
+ interface OpenCodeTokens {
212
+ input: number;
213
+ output: number;
214
+ reasoning?: number;
215
+ cache?: {
216
+ read: number;
217
+ write: number;
218
+ };
219
+ }
207
220
 
208
221
  /** How an OpenCode execution ended. */
209
222
  type TurnOutcome = {
@@ -234,14 +247,46 @@ type PendingAsk = {
234
247
  request: ElicitationRequest;
235
248
  messageId?: string;
236
249
  };
237
- /** Where a turn stands: it needs a person, or it is over. */
250
+ /**
251
+ * What a turn spent. `inputTokens` is the whole input side, cached input included (as the library's
252
+ * `MessageUsage` counts it — OpenCode's own `tokens.input` leaves the cache out); `cacheReadTokens`,
253
+ * `cacheWriteTokens` and `reasoningTokens` are subsets. `costUsd` sums every priced call.
254
+ */
255
+ interface OpenCodeUsage {
256
+ inputTokens: number;
257
+ outputTokens: number;
258
+ cacheReadTokens: number;
259
+ cacheWriteTokens: number;
260
+ reasoningTokens: number;
261
+ costUsd: number;
262
+ }
263
+ declare function emptyUsage(): OpenCodeUsage;
264
+ /** `a + b`; a missing side (a milestone journaled before usage travelled) counts as nothing. */
265
+ declare function addUsage(a: OpenCodeUsage | undefined, b: OpenCodeUsage | undefined): OpenCodeUsage;
266
+ /** The model a step ran on, as OpenCode names it (`session.step.started`). */
267
+ interface StepModel {
268
+ providerID: string;
269
+ id: string;
270
+ }
271
+ /** What a model call cost, and where the figure came from. */
272
+ interface StepCost {
273
+ costUsd?: number;
274
+ costSource?: CostSource;
275
+ }
276
+ /**
277
+ * Where a turn stands: it needs a person, or it is over. `usage` is what the turn spent since its
278
+ * previous milestone — journaled with the milestone, so a run resumed in another process still adds
279
+ * up what every process saw.
280
+ */
238
281
  type Milestone = {
239
282
  kind: 'ask';
240
283
  ask: PendingAsk;
241
284
  timeoutMs?: number;
285
+ usage?: OpenCodeUsage;
242
286
  } | {
243
287
  kind: 'finished';
244
288
  outcome: TurnOutcome;
289
+ usage?: OpenCodeUsage;
245
290
  };
246
291
  interface OpenCodeTurnArgs {
247
292
  runId: string;
@@ -252,8 +297,19 @@ interface OpenCodeTurnArgs {
252
297
  store: AgentStore;
253
298
  /** What an action needs before it runs (the module's `ApprovalPolicy`). */
254
299
  approvalFor: (action: string) => Promise<ApprovalRequirement>;
255
- /** The model label usage is recorded under. */
300
+ /** The model label usage is recorded under until OpenCode names the step's model. */
256
301
  modelLabel: string;
302
+ /**
303
+ * What a model call cost: OpenCode's reported figure, or the library's estimate. Omit → OpenCode's
304
+ * figure as reported.
305
+ */
306
+ priceStep?: (step: {
307
+ model: StepModel | undefined;
308
+ usage: MessageUsage;
309
+ reportedCostUsd: number | undefined;
310
+ }) => Promise<StepCost>;
311
+ /** What the run spent before this turn object existed (an earlier process, an earlier session). */
312
+ carried?: OpenCodeUsage;
257
313
  logger: Logger;
258
314
  /**
259
315
  * An action OpenCode was allowed to run (a person approved it, or the policy did): the approval
@@ -296,8 +352,15 @@ declare class OpenCodeTurn {
296
352
  private readonly hidden;
297
353
  private readonly milestones;
298
354
  private waiting;
299
- private readonly usage;
300
- private costUsd;
355
+ /** What this turn object saw spent, and how much of it a milestone already reported. */
356
+ private spent;
357
+ private reported;
358
+ private priced;
359
+ private model;
360
+ /** What the session had spent when this turn prompted it; what OpenCode reported since. */
361
+ private baseline;
362
+ private readonly seen;
363
+ private compacted;
301
364
  private stepOpen;
302
365
  private sawText;
303
366
  private separator;
@@ -306,6 +369,18 @@ declare class OpenCodeTurn {
306
369
  private ended;
307
370
  private stepError;
308
371
  constructor(a: OpenCodeTurnArgs);
372
+ /**
373
+ * What the session had spent before this turn prompted it (`session.get`). When the execution
374
+ * ends, what the session spent since that this turn's events did not report — the title OpenCode
375
+ * generates, a compaction: OpenCode records those calls on the session without streaming them — is
376
+ * recorded as a usage row of its own.
377
+ */
378
+ setBaseline(info: {
379
+ cost?: number;
380
+ tokens?: OpenCodeTokens;
381
+ } | undefined): void;
382
+ /** What the run spent so far: carried from before this turn object, plus what it saw. */
383
+ totalUsage(): OpenCodeUsage;
309
384
  get sessionId(): string;
310
385
  get finished(): boolean;
311
386
  /** Feed one of the session's events. */
@@ -348,12 +423,19 @@ declare class OpenCodeTurn {
348
423
  private innerCalls;
349
424
  private onEvent;
350
425
  private settleTool;
426
+ private modelId;
427
+ /** One model call's usage and cost, into the ledger and the run's total. */
428
+ private spend;
429
+ /** `session.usage.recorded`: a call OpenCode made besides the steps (the title, a compaction). */
430
+ private usageRecorded;
351
431
  private stepEnded;
352
432
  /**
353
433
  * Persist what the turn produced since the last flush as one assistant message — the message a
354
434
  * call put to a person hangs on, or the turn's last one. Returns its id.
355
435
  */
356
436
  private flush;
437
+ /** The run's usage as its last message carries it: the whole run, cache tokens included. */
438
+ private messageUsage;
357
439
  /** A request recorded by an earlier process (a turn resumed after a restart). */
358
440
  private alreadyRecorded;
359
441
  /** The persisted assistant message carrying call `id`, so its result lands on the same message. */
@@ -363,8 +445,16 @@ declare class OpenCodeTurn {
363
445
  private onForm;
364
446
  private answered;
365
447
  private addResult;
448
+ /** What the session spent since the baseline that no event of this turn reported. */
449
+ private unreported;
366
450
  private end;
367
451
  }
452
+ /**
453
+ * OpenCode's token counts as the library's `MessageUsage`. OpenCode reports the uncached input in
454
+ * `input` and the cache reads and writes beside it; the library counts the whole input side in
455
+ * `inputTokens`, the cache counts being subsets of it.
456
+ */
457
+ declare function usageOf(tokens: any): MessageUsage;
368
458
 
369
459
  /**
370
460
  * Where a turn runs and how its OpenCode session is set up — the part only the host knows. The
@@ -471,11 +561,12 @@ interface OpenCodeRunResult {
471
561
  text: string;
472
562
  /** The run's assistant messages, as stored. */
473
563
  messages: StoredMessage[];
474
- usage: {
475
- inputTokens: number;
476
- outputTokens: number;
477
- costUsd: number;
478
- };
564
+ /**
565
+ * What the run spent, over every model call OpenCode made for it (its steps, the title, a
566
+ * compaction) and every process that ran a part of it. `inputTokens` counts cached input too;
567
+ * `cacheReadTokens`/`cacheWriteTokens` are subsets of it.
568
+ */
569
+ usage: OpenCodeUsage;
479
570
  durationMs: number;
480
571
  }
481
572
  /** See {@link OpenCodeHost.beforeSettle}. */
@@ -648,6 +739,19 @@ interface OpenCodeEngineSettings {
648
739
  * parked on a person is not observing: this bounds one stretch of OpenCode working on its own.
649
740
  */
650
741
  turnTimeoutMs?: number;
742
+ /**
743
+ * Whose figure a model call is recorded at. OpenCode prices each call itself, off its model
744
+ * catalog (the `cost` its config declares, else models.dev's list price):
745
+ *
746
+ * - `'reported'` (default) — OpenCode's figure when it has one (`cost_source` `'provider'`); a call
747
+ * OpenCode priced at 0 (no price for the model) is estimated from the library's price instead
748
+ * (`priceCatalog`, the pricing store, the built-in GovCloud Bedrock table, models.dev;
749
+ * `cost_source` `'estimate'`).
750
+ * - `'estimate'` — the library's price whenever it has one, OpenCode's figure otherwise. For
751
+ * models OpenCode prices at a list price that is not yours (Bedrock in GovCloud, a negotiated
752
+ * rate) when you would rather not declare `cost` in OpenCode's config.
753
+ */
754
+ cost?: 'reported' | 'estimate';
651
755
  }
652
756
  /** A thread's session, as the steps of a turn pass it along (and a durable run journals it). */
653
757
  interface SessionHandle {
@@ -699,7 +803,20 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
699
803
  private readonly spent;
700
804
  /** When the tools endpoint was last registered for a location (`server|boot|directory|actor`). */
701
805
  private readonly toolsIssued;
702
- constructor(host: OpenCodeHost, sessions: OpenCodeSessionStore, settings: OpenCodeEngineSettings, store: AgentStore, sink: TokenStreamSink, deps: AgentDepsFactory, registry: ToolRegistry, skills?: SkillsConfig | undefined, memory?: MemoryConfig | undefined, queue?: ChatQueueService | undefined, toolsTokens?: (OpenCodeToolsTokens | null) | undefined);
806
+ constructor(host: OpenCodeHost, sessions: OpenCodeSessionStore, settings: OpenCodeEngineSettings, store: AgentStore, sink: TokenStreamSink, deps: AgentDepsFactory, registry: ToolRegistry, skills?: SkillsConfig | undefined, memory?: MemoryConfig | undefined, queue?: ChatQueueService | undefined, toolsTokens?: (OpenCodeToolsTokens | null) | undefined, pricingStore?: AgentPricingStore, agentOptions?: {
807
+ priceCatalog?: PriceCatalogOptions | false;
808
+ });
809
+ private readonly prices;
810
+ /**
811
+ * What a model call OpenCode made cost, by the same rule the library's loop records its own steps
812
+ * at: a reported figure wins, else an estimate off the model's price — see
813
+ * {@link OpenCodeEngineSettings.cost}.
814
+ */
815
+ priceStep(step: {
816
+ model: StepModel | undefined;
817
+ usage: MessageUsage;
818
+ reportedCostUsd: number | undefined;
819
+ }): Promise<StepCost>;
703
820
  /** The tools server's name in OpenCode. */
704
821
  get toolsServer(): string;
705
822
  /** The roles policy a turn's tools are gated by (the agent's own). */
@@ -784,20 +901,25 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
784
901
  */
785
902
  private addTools;
786
903
  /** Start listening to the session (so nothing it says is missed), then send the user message. */
787
- prompt(runId: string, input: AgentRunInput, handle: SessionHandle): Promise<void>;
904
+ prompt(runId: string, input: AgentRunInput, handle: SessionHandle, spent?: OpenCodeUsage): Promise<void>;
788
905
  /**
789
906
  * Wait for the turn's next milestone. In a process that was not following the turn (a resumed
790
907
  * durable run), the turn is rebuilt and first catches up on requests OpenCode raised meanwhile;
791
908
  * `session.wait` is the safety net for a terminal event that was missed.
909
+ *
910
+ * `spent` is what the run spent before (the sum of the `usage` of its earlier milestones): a turn
911
+ * rebuilt here starts from it, so the answer it persists carries the whole run's usage.
792
912
  */
793
- observe(runId: string, input: AgentRunInput, handle: SessionHandle): Promise<Milestone>;
913
+ observe(runId: string, input: AgentRunInput, handle: SessionHandle, spent?: OpenCodeUsage): Promise<Milestone>;
794
914
  /**
795
915
  * Hand a person's answer back to OpenCode. When the server restarted while the turn waited, the
796
916
  * request is gone with it: a new session is opened, told what the person decided, and prompted
797
917
  * to go on.
798
918
  */
799
- reply(runId: string, input: AgentRunInput, handle: SessionHandle, ask: PendingAsk, reply: HumanReply): Promise<SessionHandle>;
919
+ reply(runId: string, input: AgentRunInput, handle: SessionHandle, ask: PendingAsk, reply: HumanReply, spent?: OpenCodeUsage): Promise<SessionHandle>;
800
920
  private continueAfterRestart;
921
+ /** What a session spent so far (`session.get`), or nothing when OpenCode does not say. */
922
+ private sessionUsage;
801
923
  /** Interrupt the thread's session (cancel). */
802
924
  interrupt(input: AgentRunInput): Promise<void>;
803
925
  private ensureLive;
@@ -807,7 +929,11 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
807
929
  private forget;
808
930
  /** A person was asked something: tell the host (cards in other channels, wake-ups). */
809
931
  private reached;
810
- /** What the run produced, as the host's settle hooks see it. */
932
+ /**
933
+ * What the run produced, as the host's settle hooks see it. `usage` is what the run spent — the
934
+ * runner's sum of its milestones' usage, which a resumed run reads back from its journal — else
935
+ * what the turn this process follows saw.
936
+ */
811
937
  private runResult;
812
938
  /**
813
939
  * The host's last word on the answer before the stream ends: components to append (a guardrail
@@ -816,9 +942,18 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
816
942
  private amend;
817
943
  /** Delivery, telemetry, spend: after the run settled. Errors are the host's, never the run's. */
818
944
  private settled;
819
- /** End the run's stream and bookkeeping for how its execution ended. */
820
- settle(runId: string, input: AgentRunInput, outcome: TurnOutcome, durationMs: number): Promise<void>;
821
- settleFailed(runId: string, input: AgentRunInput, error: string, durationMs?: number): Promise<void>;
945
+ /**
946
+ * What the run spent: the runner's sum of its milestones, or what the turn this process follows
947
+ * saw (which starts from that sum) when it saw more — a run that failed or was cancelled between
948
+ * two milestones.
949
+ */
950
+ private spentBy;
951
+ /**
952
+ * End the run's stream and bookkeeping for how its execution ended. `spent` is what the run spent
953
+ * (the sum of its milestones' `usage`); omit → what the turn this process follows saw.
954
+ */
955
+ settle(runId: string, input: AgentRunInput, outcome: TurnOutcome, durationMs: number, spent?: OpenCodeUsage): Promise<void>;
956
+ settleFailed(runId: string, input: AgentRunInput, error: string, durationMs?: number, spent?: OpenCodeUsage): Promise<void>;
822
957
  /** Starts the next queued message of the thread; set by the runner (it owns `start`). */
823
958
  startNext: ((next: AgentRunInput, runId: string) => Promise<unknown>) | undefined;
824
959
  /** As `InlineAgentRunner`: hand the thread to its next queued message, and say so on the stream. */
@@ -846,4 +981,4 @@ declare function openCodeControllers(options: OpenCodeEngineSettings): Type<obje
846
981
  /** The providers every OpenCode engine needs: host, session store, settings and the turn steps. */
847
982
  declare function openCodeProviders(options: OpenCodeEngineOptions): Provider[];
848
983
 
849
- export { OpenCodeTurn as A, type OpenCodeTurnContext as B, keyValueOpenCodeSessionStore as C, openCode as D, openCodeControllers as E, openCodeProviders as F, InMemoryOpenCodeSessionStore as I, type Milestone as M, OpenCodeTurns as O, type PendingAsk as P, type SessionHandle as S, type TurnOutcome as T, type OpenCodeEngineSettings as a, type OpenCodeHost as b, type OpenCodeEngineOptions as c, type OpenCodeClient as d, type OpenCodeEvent as e, type OpenCodeForm as f, type OpenCodeFormValue as g, type OpenCodeFormField as h, OpenCodeMcpEndpoint as i, type OpenCodeAmendment as j, type OpenCodeCallContext as k, type OpenCodeKeyValue as l, type OpenCodeModelRef as m, type OpenCodePermissionRequest as n, type OpenCodePermissionRule as o, OpenCodeReplyMismatchError as p, type OpenCodeRunResult as q, type OpenCodeServer as r, type OpenCodeSessionCreate as s, type OpenCodeSessionRef as t, type OpenCodeSessionStore as u, type OpenCodeToolCall as v, OpenCodeToolRefusedError as w, type OpenCodeToolsClaims as x, type OpenCodeToolsOptions as y, OpenCodeToolsTokens as z };
984
+ export { OpenCodeTurn as A, type OpenCodeTurnContext as B, type OpenCodeUsage as C, type StepCost as D, type StepModel as E, addUsage as F, emptyUsage as G, keyValueOpenCodeSessionStore as H, InMemoryOpenCodeSessionStore as I, openCode as J, openCodeControllers as K, openCodeProviders as L, type Milestone as M, usageOf as N, OpenCodeTurns as O, type PendingAsk as P, type SessionHandle as S, type TurnOutcome as T, type OpenCodeEngineSettings as a, type OpenCodeHost as b, type OpenCodeEngineOptions as c, type OpenCodeClient as d, type OpenCodeEvent as e, type OpenCodeForm as f, type OpenCodeFormValue as g, type OpenCodeFormField as h, OpenCodeMcpEndpoint as i, type OpenCodeAmendment as j, type OpenCodeCallContext as k, type OpenCodeKeyValue as l, type OpenCodeModelRef as m, type OpenCodePermissionRequest as n, type OpenCodePermissionRule as o, OpenCodeReplyMismatchError as p, type OpenCodeRunResult as q, type OpenCodeServer as r, type OpenCodeSessionCreate as s, type OpenCodeSessionRef as t, type OpenCodeSessionStore as u, type OpenCodeToolCall as v, OpenCodeToolRefusedError as w, type OpenCodeToolsClaims as x, type OpenCodeToolsOptions as y, OpenCodeToolsTokens as z };