maka-agent 0.2.0-dev.75.20261008 → 0.2.0-dev.76.20261009

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.
@@ -2288,6 +2288,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
2288
2288
  'turn.stop': turnControl.handlers['turn.stop'],
2289
2289
  'usage.query': usagePricing.handlers['usage.query'],
2290
2290
  },
2291
+ runSettlementCoverage: (from, to) => usagePricing.runSettlementCoverage(from, to),
2291
2292
  context: {
2292
2293
  hostEpoch: context.hostEpoch,
2293
2294
  connectionId: 'hosted-execution',
@@ -31,6 +31,7 @@ import { buildSessionTitlePrompt, cleanGeneratedSessionTitle, SESSION_TITLE_GENE
31
31
  import { createProxiedFetchTransport, } from '@maka/runtime/network/scoped-fetch-transport';
32
32
  import { generateToolFreeModelCall, generateProviderPrefixModelCall, ProviderPrefixModelCallUnavailableError, } from '@maka/runtime/tool-free-model-call';
33
33
  import { resolveModelRuntime } from '@maka/runtime/model-runtime';
34
+ import { NO_RUN_TURN_ID } from '@maka/storage/model-call-ledger';
34
35
  import { memoryExtractionMaxOutputTokens, } from '@maka/runtime/memory-extraction';
35
36
  import { createHostOAuthModelFetch, OAuthExecutionCredentialError, } from './oauth-execution-authority.js';
36
37
  import { toRuntimePolicyProxy } from './runtime-policy-proxy.js';
@@ -304,6 +305,15 @@ export function createHostGoalEvaluator(input) {
304
305
  },
305
306
  });
306
307
  }
308
+ /**
309
+ * Turn placeholder for canonical rows of calls no run owns: auxiliary calls
310
+ * happen outside any turn, and the ledger's countable rows require the column.
311
+ * The storage side records the rows' no-run ownership from this seam, so a
312
+ * failed auxiliary call cannot flip a hosted run to indeterminate (#5691) —
313
+ * including a hosted execution legally named like this placeholder
314
+ * (#5890 review).
315
+ */
316
+ const AUXILIARY_TURN_ID = NO_RUN_TURN_ID;
307
317
  function createAuxiliaryModelCallAuthority(input) {
308
318
  let drainRequested = false;
309
319
  const requestDrain = () => {
@@ -328,6 +338,17 @@ function createAuxiliaryModelCallAuthority(input) {
328
338
  }
329
339
  },
330
340
  },
341
+ modelCalls: {
342
+ recordUsageUnknownAttempt: async (record) => {
343
+ try {
344
+ await input.usage.modelCalls.recordUsageUnknownAttempt(record);
345
+ }
346
+ catch (error) {
347
+ requestDrain();
348
+ throw error;
349
+ }
350
+ },
351
+ },
331
352
  requestDrain,
332
353
  now: input.now ?? Date.now,
333
354
  newId: input.newId ?? randomUUID,
@@ -373,6 +394,32 @@ async function runHostAuxiliaryModelCall(authority, input) {
373
394
  modelId: target.model,
374
395
  startedAt,
375
396
  };
397
+ // Abort and error paths know no token counts: their canonical row says
398
+ // usage-unknown (`usageBasis: 'missing'`, no tokens) instead of posing as a
399
+ // free call (#5691). Auxiliary calls run outside any AgentRun, so the
400
+ // canonical writer is this store seam, not the run's event stream.
401
+ const recordUsageUnknown = async (status, errorClass) => {
402
+ const completedAt = authority.now();
403
+ try {
404
+ await authority.modelCalls.recordUsageUnknownAttempt({
405
+ attemptId: input.callId,
406
+ completedAt,
407
+ ...(input.telemetrySessionId ? { sessionId: input.telemetrySessionId } : {}),
408
+ logicalCallId: input.callId,
409
+ turnId: AUXILIARY_TURN_ID,
410
+ callKind: input.callKind,
411
+ connectionSlug: target.connection.slug,
412
+ providerId: target.connection.providerType,
413
+ modelId: target.model,
414
+ latencyMs: Math.max(0, completedAt - startedAt),
415
+ status,
416
+ ...(errorClass ? { errorClass } : {}),
417
+ });
418
+ }
419
+ catch (accountingError) {
420
+ throw new AuxiliaryModelCallLocalError('accounting', accountingError);
421
+ }
422
+ };
376
423
  let result;
377
424
  try {
378
425
  result = await readDuringBackendCreation(() => {
@@ -408,24 +455,18 @@ async function runHostAuxiliaryModelCall(authority, input) {
408
455
  }
409
456
  catch (error) {
410
457
  const effectiveError = readDeferredOAuthFailure?.() ?? error;
411
- try {
412
- await recordLlmCallStrict({ repo: authority.telemetry, lookupPricing: pricing }, {
413
- ...baseRecord,
414
- inputTokens: 0,
415
- outputTokens: 0,
416
- latencyMs: Math.max(0, authority.now() - startedAt),
417
- status: input.abortSignal.aborted ? 'aborted' : 'error',
418
- errorClass: evaluatorErrorClass(effectiveError),
419
- });
420
- }
421
- catch (accountingError) {
422
- throw new AuxiliaryModelCallLocalError('accounting', accountingError);
423
- }
458
+ await recordUsageUnknown(input.abortSignal.aborted ? 'aborted' : 'failed', evaluatorErrorClass(effectiveError));
424
459
  throw effectiveError;
425
460
  }
426
461
  try {
427
462
  await recordLlmCallStrict({ repo: authority.telemetry, lookupPricing: pricing }, {
428
463
  ...baseRecord,
464
+ // A completion without reported usage stays on the legacy zero path:
465
+ // hosted execution settlement refuses any range containing
466
+ // usage-missing rows ('missing_attempt_usage'), so routing these
467
+ // rows to the canonical ledger marked every such hosted run
468
+ // indeterminate. The residual zero is known dishonesty, pending a
469
+ // ruling on that settlement coverage semantics (#5691).
429
470
  ...(result.usage
430
471
  ? llmCallUsageFields(result.usage)
431
472
  : { inputTokens: 0, outputTokens: 0 }),
@@ -50,8 +50,10 @@ export class HostHostedExecutionRunner {
50
50
  await (signal.aborted
51
51
  ? this.input.waitForAllResidencies()
52
52
  : this.input.waitForExecutionResidencies());
53
- const usage = await this.#readUsage(startedAt, (this.input.now ?? Date.now)());
54
- const incompleteUsage = incompleteUsageReason(usage);
53
+ const settledAt = (this.input.now ?? Date.now)();
54
+ const usage = await this.#readUsage(startedAt, settledAt);
55
+ const runCoverage = await this.input.runSettlementCoverage(startedAt, settledAt);
56
+ const incompleteUsage = incompleteUsageReason(usage, runCoverage);
55
57
  if (incompleteUsage) {
56
58
  return indeterminate(input.executionId, `Runtime Host usage did not settle: ${incompleteUsage}`);
57
59
  }
@@ -111,15 +113,20 @@ export class HostHostedExecutionRunner {
111
113
  return result;
112
114
  }
113
115
  }
114
- function incompleteUsageReason(result) {
115
- const { coverage, unreadableRecords, pendingRepairs } = result.provenance;
116
+ function incompleteUsageReason(result, runCoverage) {
117
+ // Unreadable and pending-repair rows stay ledger-wide: damage in the window
118
+ // is nobody's settled fact. Partial and missing usage are judged against the
119
+ // same window as the totals this check guards (#5890), so a delegated child
120
+ // Session cannot settle underneath them — the `no_run` sentinel rows stay
121
+ // excluded, and the shared coverage field still reports everything.
122
+ const { unreadableRecords, pendingRepairs } = result.provenance;
116
123
  if (unreadableRecords > 0)
117
124
  return 'unreadable_usage_record';
118
125
  if (pendingRepairs > 0)
119
126
  return 'pending_usage_repair';
120
- if (coverage.usagePartialAttempts > 0)
127
+ if (runCoverage.usagePartialAttempts > 0)
121
128
  return 'partial_attempt_usage';
122
- if (coverage.usageMissingAttempts > 0)
129
+ if (runCoverage.usageMissingAttempts > 0)
123
130
  return 'missing_attempt_usage';
124
131
  return undefined;
125
132
  }
@@ -33,6 +33,16 @@ export class HostUsagePricingCoordinator {
33
33
  'pricing.query': (input) => this.#queryPricing(input),
34
34
  'pricing.mutate': (input) => this.#mutatePricing(input),
35
35
  };
36
+ /**
37
+ * What the settlement window itself left unsettled (#5890) — hosted
38
+ * execution settlement's incompleteness check reads the same window as the
39
+ * totals it guards, minus the `no_run` sentinel rows. Deliberately off the
40
+ * operation handler map: the ledger-wide `usage.query` coverage keeps a
41
+ * different shape, so settlement scopes itself through this in-process seam.
42
+ */
43
+ async runSettlementCoverage(from, to) {
44
+ return this.#stores.modelCalls.modelCallRunSettlementCoverage(from, to);
45
+ }
36
46
  #stores;
37
47
  #requestDrain;
38
48
  #activation;
@@ -19,9 +19,10 @@
19
19
  import { decodeModelCallAttempt, MODEL_CALL_ATTEMPT_EVENT_TYPE, } from '@maka/core/model-call-attempt';
20
20
  import { resolveUsageRange, } from '@maka/core/model-call-usage-projection';
21
21
  import { usageBucketKey } from '@maka/core/usage-stats/bucket-key';
22
- import { bucketGrouping, CACHE_READ_TOKENS, count, countableFilter, COVERAGE_SUMS, PRICED_COST, REQUEST_SUMS, TOKEN_SUMS, unreadableFilter, } from './model-call-usage-sql.js';
22
+ import { bucketGrouping, CACHE_READ_TOKENS, count, countableFilter, COVERAGE_SUMS, NO_RUN_TURN_ID, PRICED_COST, REQUEST_SUMS, RUN_SETTLEMENT_COVERAGE_SUMS, TOKEN_SUMS, unreadableFilter, } from './model-call-usage-sql.js';
23
23
  import { acquireOperationalStateDatabase, } from './operational-state-store.js';
24
24
  import { MODEL_CALL_COLUMNS } from './sqlite-usage-schema.js';
25
+ export { NO_RUN_TURN_ID };
25
26
  export class ModelCallLedgerClosedError extends Error {
26
27
  constructor() {
27
28
  super('Model call ledger is draining or closed');
@@ -69,6 +70,11 @@ class SqliteModelCallLedger {
69
70
  const eventsPerRun = positiveInteger(input.eventsPerRun, 512, 'projection catch-up event limit');
70
71
  return this.write(() => catchUpModelCallProjection(this.#lease.database, input, limit, eventsPerRun));
71
72
  }
73
+ recordUsageUnknownAttempt(record) {
74
+ if (this.#state !== 'open')
75
+ return Promise.reject(new ModelCallLedgerClosedError());
76
+ return this.write(() => writeUsageUnknownModelCallAttempt(this.#lease.database, record));
77
+ }
72
78
  summary(query, now) {
73
79
  const db = this.#open();
74
80
  const range = resolveUsageRange(query.range, now);
@@ -159,6 +165,19 @@ class SqliteModelCallLedger {
159
165
  unreadableRecords: this.#unreadable(query, range),
160
166
  };
161
167
  }
168
+ runSettlementCoverage(from, to) {
169
+ const db = this.#open();
170
+ const row = db
171
+ .prepare(`SELECT ${RUN_SETTLEMENT_COVERAGE_SUMS}
172
+ FROM usage_model_call_attempts
173
+ WHERE cost_basis IS NOT NULL AND no_run = 0
174
+ AND completed_at >= ? AND completed_at <= ?`)
175
+ .get(from, to);
176
+ return {
177
+ usageMissingAttempts: count(row?.usageMissingAttempts),
178
+ usagePartialAttempts: count(row?.usagePartialAttempts),
179
+ };
180
+ }
162
181
  #open() {
163
182
  if (this.#state !== 'open')
164
183
  throw new ModelCallLedgerClosedError();
@@ -285,12 +304,51 @@ function bindModelCallAttempt(attempt) {
285
304
  reasoning_tokens: attempt.reasoningTokens ?? null,
286
305
  cost_basis: attempt.costBasis,
287
306
  cost_usd: attempt.costUsd ?? null,
307
+ // The projection's only source is the AgentRun authority: every row it
308
+ // writes is run-owned, whatever turn the run ran under.
309
+ no_run: 0,
288
310
  };
289
311
  return MODEL_CALL_COLUMNS.map((column) => values[column]);
290
312
  }
291
313
  function writeModelCallAttempt(db, attempt) {
292
314
  db.prepare(MODEL_CALL_UPSERT).run(...bindModelCallAttempt(attempt));
293
315
  }
316
+ /**
317
+ * The usage-unknown binding: no token counts and no cost exist to record, and
318
+ * the table's CHECKs refuse any row that claimed otherwise.
319
+ */
320
+ function bindUsageUnknownModelCallAttempt(record) {
321
+ const values = {
322
+ attempt_id: record.attemptId,
323
+ completed_at: record.completedAt,
324
+ session_id: record.sessionId ?? null,
325
+ logical_call_id: record.logicalCallId,
326
+ turn_id: record.turnId,
327
+ call_kind: record.callKind,
328
+ connection_slug: record.connectionSlug ?? null,
329
+ provider_id: record.providerId,
330
+ model_id: record.modelId,
331
+ latency_ms: record.latencyMs,
332
+ status: record.status,
333
+ error_class: record.errorClass ?? null,
334
+ usage_basis: 'missing',
335
+ input_tokens: null,
336
+ output_tokens: null,
337
+ cache_read_input_tokens: null,
338
+ cache_miss_input_tokens: null,
339
+ cache_write_input_tokens: null,
340
+ reasoning_tokens: null,
341
+ cost_basis: 'unpriced',
342
+ cost_usd: null,
343
+ // The one write outside any run's event stream: the ledger marks the row
344
+ // no-run here rather than inferring ownership from the turn value.
345
+ no_run: 1,
346
+ };
347
+ return MODEL_CALL_COLUMNS.map((column) => values[column]);
348
+ }
349
+ function writeUsageUnknownModelCallAttempt(db, record) {
350
+ db.prepare(MODEL_CALL_UPSERT).run(...bindUsageUnknownModelCallAttempt(record));
351
+ }
294
352
  function catchUpModelCallProjection(db, input, limit, eventsPerRun) {
295
353
  const scope = projectionScope(input);
296
354
  const lagging = db
@@ -36,6 +36,17 @@ export const CACHE_READ_TOKENS = `
36
36
  export const PRICED_COST = `CASE WHEN cost_basis = 'priced' THEN COALESCE(cost_usd, 0) ELSE 0 END`;
37
37
  /** Only a provider failure is an error. */
38
38
  const ERROR_ROW = `status = 'failed'`;
39
+ /**
40
+ * The turn placeholder on rows recorded outside any AgentRun — failed and
41
+ * aborted auxiliary Host model calls (#5691). Such a row is honest accounting
42
+ * (real spend shape, unknown usage), so the ledger-wide coverage counts it.
43
+ * The value is a placeholder for the table's required turn column, not the
44
+ * settlement discriminator: rows carry a `no_run` column written by whichever
45
+ * seam recorded them, because a hosted execution legally named `auxiliary`
46
+ * owns rows under this same turn value (#5890 review). Shared with the writer
47
+ * so the placeholder cannot drift.
48
+ */
49
+ export const NO_RUN_TURN_ID = 'auxiliary';
39
50
  export const TOKEN_SUMS = `
40
51
  SUM(COALESCE(input_tokens, 0)) AS input,
41
52
  SUM(COALESCE(output_tokens, 0)) AS output,
@@ -51,6 +62,18 @@ export const COVERAGE_SUMS = `
51
62
  SUM(usage_basis = 'reported') AS usageReportedAttempts,
52
63
  SUM(usage_basis = 'partial') AS usagePartialAttempts,
53
64
  SUM(usage_basis = 'missing') AS usageMissingAttempts`;
65
+ /**
66
+ * Coverage a hosted execution's settlement may hold a run to (#5691): the
67
+ * rows the run owns — the ones its Session's event-stream projection
68
+ * recorded, outside the `no_run` mark the usage-unknown seam writes. The
69
+ * ownership exclusion is the query's WHERE clause, not an aggregate here, so
70
+ * this stays narrower than the ledger-wide {@link COVERAGE_SUMS} on purpose:
71
+ * one answers "what does the ledger hold unknown-usage rows for", the other
72
+ * "what did this run leave unsettled".
73
+ */
74
+ export const RUN_SETTLEMENT_COVERAGE_SUMS = `
75
+ SUM(usage_basis = 'partial') AS usagePartialAttempts,
76
+ SUM(usage_basis = 'missing') AS usageMissingAttempts`;
54
77
  export const REQUEST_SUMS = `
55
78
  COUNT(*) AS totalRequests,
56
79
  SUM(${PRICED_COST}) AS totalCostUsd,
@@ -16,7 +16,7 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- export const SQLITE_USAGE_SCHEMA_VERSION = 9;
19
+ export const SQLITE_USAGE_SCHEMA_VERSION = 10;
20
20
  /**
21
21
  * The canonical ledger's columns, in the order every statement binds them.
22
22
  *
@@ -45,6 +45,7 @@ export const MODEL_CALL_COLUMNS = [
45
45
  'reasoning_tokens',
46
46
  'cost_basis',
47
47
  'cost_usd',
48
+ 'no_run',
48
49
  ];
49
50
  /**
50
51
  * The JSON path each pricing column was read from before the columns existed,
@@ -131,6 +132,11 @@ const MODEL_CALL_TABLE = `
131
132
  reasoning_tokens INTEGER,
132
133
  cost_basis TEXT,
133
134
  cost_usd REAL,
135
+ -- Which writer recorded the row: the run's event-stream projection (0) or
136
+ -- the usage-unknown seam for calls no AgentRun owns (1). Settlement reads
137
+ -- this, not the turn value — a hosted execution legally named like the
138
+ -- no-run sentinel owns rows under the sentinel turn too (#5890 review).
139
+ no_run INTEGER NOT NULL DEFAULT 0,
134
140
  CHECK (${MODEL_CALL_REQUIRED_COLUMNS.map((column) => `(${column} IS NULL) = (cost_basis IS NULL)`).join(' AND ')}),
135
141
  -- A price that could not be resolved must never surface as an amount, and a
136
142
  -- priced call must carry one. Zero stays legal: it is the only way to say a
@@ -209,6 +215,12 @@ export function migrateSqliteUsageDatabase(db) {
209
215
  // through, and the conversion below reads one.
210
216
  ensureColumn(db, 'usage_model_call_attempts', 'session_id', 'TEXT');
211
217
  spreadModelCallRecordJson(db);
218
+ // no_run is written by the recorder, never inferred from the turn value:
219
+ // a backfill keyed on the auxiliary turn string would flip a legally named
220
+ // execution's own run-owned rows (see the settlement-coverage regression in
221
+ // model-call-usage-query.test.ts). Databases from before this column cannot
222
+ // hold auxiliary rows - recording them is what introduced the column.
223
+ ensureColumn(db, 'usage_model_call_attempts', 'no_run', 'INTEGER NOT NULL DEFAULT 0');
212
224
  db.exec(`
213
225
  CREATE INDEX IF NOT EXISTS usage_model_call_attempts_completed_at
214
226
  ON usage_model_call_attempts(completed_at DESC, attempt_id);
@@ -350,7 +362,10 @@ function spreadModelCallRecordJson(db) {
350
362
  attempt_id,
351
363
  completed_at,
352
364
  session_id,
353
- ${pricing}
365
+ ${pricing},
366
+ -- Pre-column rows all predate the usage-unknown seam: every one of them
367
+ -- was recorded by the run's event-stream projection.
368
+ 0 AS no_run
354
369
  FROM classified;
355
370
 
356
371
  DROP TABLE usage_model_call_attempts_blob;
@@ -309,7 +309,9 @@ function createWriterFacade(lease, telemetry, modelCalls, pricing, screen) {
309
309
  modelCallSummary: (query, now) => read(() => modelCalls.summary(query, now)),
310
310
  modelCallBuckets: (query, groupBy, now) => read(() => modelCalls.buckets(query, groupBy, now)),
311
311
  modelCallLogs: (query, now, offset, limit) => read(() => modelCalls.logs(query, now, offset, limit)),
312
+ modelCallRunSettlementCoverage: (from, to) => read(() => modelCalls.runSettlementCoverage(from, to)),
312
313
  catchUpModelCallProjection: admitModelCallProjectionCatchUp,
314
+ recordUsageUnknownAttempt: (record) => admitSessionUsageMutation(record.sessionId, () => modelCalls.recordUsageUnknownAttempt(record)),
313
315
  },
314
316
  pricing: {
315
317
  snapshot: () => read(() => pricing.snapshot()),
@@ -343,6 +345,7 @@ function modelCallReader(ledger, run) {
343
345
  modelCallSummary: (query, now) => run(() => ledger.summary(query, now)),
344
346
  modelCallBuckets: (query, groupBy, now) => run(() => ledger.buckets(query, groupBy, now)),
345
347
  modelCallLogs: (query, now, offset, limit) => run(() => ledger.logs(query, now, offset, limit)),
348
+ modelCallRunSettlementCoverage: (from, to) => run(() => ledger.runSettlementCoverage(from, to)),
346
349
  });
347
350
  }
348
351
  function pricingReader(store, run) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maka-agent",
3
- "version": "0.2.0-dev.75.20261008",
3
+ "version": "0.2.0-dev.76.20261009",
4
4
  "description": "Apache Maka (Incubating) developer snapshot; not an Apache release.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",