@peakflo/agent-harness 0.2.10-rc.2 → 0.2.10-rc.3

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.
@@ -44,6 +44,25 @@ const permission_mode_1 = require("./permission-mode");
44
44
  const projection_1 = require("./projection");
45
45
  const space_files_1 = require("./space-files");
46
46
  const task_files_1 = require("./task-files");
47
+ /** The virtual key keeps spend out of URLs and logs. */
48
+ async function readGatewaySpend(baseUrl, key) {
49
+ try {
50
+ const response = await fetch(new URL('/key/info', baseUrl), {
51
+ headers: { Authorization: `Bearer ${key}` },
52
+ signal: AbortSignal.timeout(5_000)
53
+ });
54
+ if (!response.ok)
55
+ return null;
56
+ const data = (await response.json());
57
+ const spend = data.info?.spend;
58
+ return typeof spend === 'number' && Number.isFinite(spend) && spend >= 0
59
+ ? spend
60
+ : null;
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
47
66
  /** A missing Pi consumption event must not hold the lease open for its full RPC timeout. */
48
67
  const FINAL_STEER_ACK_WAIT_MS = 5_000;
49
68
  /** How often the runner asks the adapter for new output when nothing pushes. */
@@ -658,6 +677,7 @@ class SessionRunner {
658
677
  let partsProduced = 0;
659
678
  let stopReason = 'completed';
660
679
  let error;
680
+ let gatewaySpendBefore = null;
661
681
  let repositoryStatuses = [];
662
682
  /** True when the backend session was resumed rather than created. */
663
683
  let resumed = false;
@@ -689,6 +709,11 @@ class SessionRunner {
689
709
  const refusal = modelGatewayRefusal(lease);
690
710
  if (refusal)
691
711
  throw new Error(refusal);
712
+ if (session.agent.model.gatewayUrl && lease.modelToken) {
713
+ gatewaySpendBefore = await readGatewaySpend(session.agent.model.gatewayUrl, lease.modelToken);
714
+ if (gatewaySpendBefore === null)
715
+ this.logger.warn(`[coding-agent] Gateway spend start unavailable for turn ${session.turnId}; this turn cannot be debited from gateway usage`);
716
+ }
692
717
  // The catalogue is advisory. Fetch it while the independent workspace
693
718
  // is prepared. Pi can start with the selected lease model while this
694
719
  // request fills the cache for a later turn. OpenCode still waits for
@@ -1331,6 +1356,16 @@ class SessionRunner {
1331
1356
  * that cannot remember the previous sentence.
1332
1357
  */
1333
1358
  }
1359
+ let costUsd;
1360
+ if (gatewaySpendBefore !== null &&
1361
+ session.agent.model.gatewayUrl &&
1362
+ lease.modelToken) {
1363
+ const spendAfter = await readGatewaySpend(session.agent.model.gatewayUrl, lease.modelToken);
1364
+ if (spendAfter === null)
1365
+ this.logger.warn(`[coding-agent] Gateway spend end unavailable for turn ${session.turnId}; this turn cannot be debited from gateway usage`);
1366
+ else
1367
+ costUsd = Math.max(0, spendAfter - gatewaySpendBefore);
1368
+ }
1334
1369
  const turn = {
1335
1370
  turnId: session.turnId,
1336
1371
  startedAt,
@@ -1338,6 +1373,7 @@ class SessionRunner {
1338
1373
  model: session.agent.model.model,
1339
1374
  stopReason,
1340
1375
  error,
1376
+ costUsd,
1341
1377
  repositories: repositoryStatuses.length > 0 ? repositoryStatuses : undefined,
1342
1378
  // The checkpoint travels on the final ingest because the final ingest is
1343
1379
  // the ONE message guaranteed to be sent — it is what releases the lease,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peakflo/agent-harness",
3
- "version": "0.2.10-rc.2",
3
+ "version": "0.2.10-rc.3",
4
4
  "description": "Headless Peakflo coding-agent harness. One codebase, three hosting modes: a Daytona cloud sandbox, a customer VM, and the 20x desktop.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",