@martinloop/mcp 0.8.3 → 0.8.4

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 +1 @@
1
- export declare const MARTIN_MCP_PACKAGE_VERSION = "0.8.3";
1
+ export declare const MARTIN_MCP_PACKAGE_VERSION = "0.8.4";
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by build — do not edit by hand.
2
2
  // Source of truth: packages/mcp/package.json#version
3
- export const MARTIN_MCP_PACKAGE_VERSION = "0.8.3";
3
+ export const MARTIN_MCP_PACKAGE_VERSION = "0.8.4";
@@ -63,6 +63,8 @@ export interface AgentCliAdapterOptions {
63
63
  streamingTokenCap?: boolean;
64
64
  /** Provider detail-token fields are already included in input/output token totals. */
65
65
  streamingUsageDetailsIncludedInTotals?: boolean;
66
+ /** Whether provider usage arrives incrementally or only on a terminal event. */
67
+ streamingUsageReportingMode?: "incremental" | "terminal";
66
68
  /** Estimated floor used by the host to reject impossible token caps before spawning. */
67
69
  budgetPreflight?: {
68
70
  minimumViableTokens: number;
@@ -383,7 +383,7 @@ function extractGeminiJsonResult(stdout, modelLabel) {
383
383
  })
384
384
  };
385
385
  }
386
- function createStreamingUsageInspector(capUsd, modelLabel, promptTokenEstimate, capTokens, detailTokensIncludedInTotals = false, observedUsageGovernor) {
386
+ function createStreamingUsageInspector(capUsd, modelLabel, promptTokenEstimate, capTokens, detailTokensIncludedInTotals = false, observedUsageGovernor, usageReportingMode = "incremental") {
387
387
  let pricingResolution = resolveModelPricing(modelLabel);
388
388
  // Safety margin: terminate at 80% of cap to bound one-turn overshoot.
389
389
  // Without this, a single expensive turn can blow past the cap before the
@@ -424,15 +424,22 @@ function createStreamingUsageInspector(capUsd, modelLabel, promptTokenEstimate,
424
424
  finalObservationPublished = true;
425
425
  try {
426
426
  const decision = observedUsageGovernor({ cumulativeUsd, cumulativeTokens, turns, final });
427
- if (decision.action === "terminate") {
427
+ if (decision.action === "terminate" && !(usageReportingMode === "terminal" && final)) {
428
428
  terminate(`Parent observed-usage governor terminated the subprocess: ${decision.reason}.`);
429
429
  }
430
430
  }
431
431
  catch (error) {
432
- terminate(`Parent observed-usage governor failed closed: ${error instanceof Error ? error.message : String(error)}.`);
432
+ if (!(usageReportingMode === "terminal" && final)) {
433
+ terminate(`Parent observed-usage governor failed closed: ${error instanceof Error ? error.message : String(error)}.`);
434
+ }
433
435
  }
434
436
  return;
435
437
  }
438
+ // Codex reports authoritative usage on turn.completed, after the provider
439
+ // turn and its workspace edits have already finished. Record that terminal
440
+ // settlement, but do not kill and discard completed work at this point.
441
+ if (usageReportingMode === "terminal" && final)
442
+ return;
436
443
  if (capTokens !== undefined && capTokens > 0 && cumulativeTokens > capTokens) {
437
444
  terminate(`Streaming token lease exceeded after ${String(turns)} turn(s): observed ${String(cumulativeTokens)} tokens ` +
438
445
  `surpassed the per-attempt token cap ${String(capTokens)}. Subprocess terminated to prevent additional token usage.`);
@@ -557,7 +564,7 @@ function createStreamingUsageInspector(capUsd, modelLabel, promptTokenEstimate,
557
564
  }
558
565
  return;
559
566
  }
560
- extractUsageFromEvent(event, terminate);
567
+ extractUsageFromEvent(event, terminate, usageReportingMode === "terminal" && event.type === "turn.completed");
561
568
  };
562
569
  return {
563
570
  onChunk: (chunk, terminate) => {
@@ -572,7 +579,8 @@ function createStreamingUsageInspector(capUsd, modelLabel, promptTokenEstimate,
572
579
  const bytesCeiling = blendedCostPerChar && blendedCostPerChar > 0
573
580
  ? Math.ceil((capUsd / blendedCostPerChar) * 2)
574
581
  : undefined;
575
- if (!usageEventSeen && capUsd > 0 && bytesCeiling !== undefined && totalBytes > bytesCeiling) {
582
+ if (usageReportingMode === "incremental" &&
583
+ !usageEventSeen && capUsd > 0 && bytesCeiling !== undefined && totalBytes > bytesCeiling) {
576
584
  terminate(`Streaming byte ceiling exceeded (${String(totalBytes)} bytes > ${String(bytesCeiling)} ceiling) ` +
577
585
  `without any usage events parsed. The Claude stream-json event format may have changed. ` +
578
586
  `Subprocess terminated as a fallback budget guard for cap $${capUsd.toFixed(4)}.`);
@@ -582,7 +590,8 @@ function createStreamingUsageInspector(capUsd, modelLabel, promptTokenEstimate,
582
590
  // without a single usage event, estimate spend from byte volume and
583
591
  // enforce the cap. This catches cases where Claude's event format
584
592
  // changed but bytes are still flowing.
585
- if (!usageEventSeen &&
593
+ if (usageReportingMode === "incremental" &&
594
+ !usageEventSeen &&
586
595
  capUsd > 0 &&
587
596
  firstChunkAt !== undefined &&
588
597
  Date.now() - firstChunkAt > USAGE_BLIND_TIMEOUT_MS &&
@@ -726,7 +735,7 @@ export function createAgentCliAdapter(options) {
726
735
  const streamingUsage = options.streamingUsageCap &&
727
736
  (request.context.remainingBudgetUsd > 0 ||
728
737
  (options.streamingTokenCap && (request.context.remainingTokens ?? 0) > 0))
729
- ? createStreamingUsageInspector(request.context.remainingBudgetUsd, options.model ?? options.command, estimatedUsage.tokensIn, options.streamingTokenCap ? request.context.remainingTokens : undefined, options.streamingUsageDetailsIncludedInTotals, request.observedUsageGovernor)
738
+ ? createStreamingUsageInspector(request.context.remainingBudgetUsd, options.model ?? options.command, estimatedUsage.tokensIn, options.streamingTokenCap ? request.context.remainingTokens : undefined, options.streamingUsageDetailsIncludedInTotals, request.observedUsageGovernor, options.streamingUsageReportingMode)
730
739
  : undefined;
731
740
  const agentResult = await runSubprocess(options.executionCommand ?? options.command, args, {
732
741
  cwd: workingDirectory,
@@ -51,6 +51,7 @@ export function createCodexCliAdapter(options = {}) {
51
51
  streamingUsageCap: true,
52
52
  streamingTokenCap: true,
53
53
  streamingUsageDetailsIncludedInTotals: true,
54
+ streamingUsageReportingMode: "terminal",
54
55
  budgetPreflight: {
55
56
  minimumViableTokens: CODEX_MINIMUM_VIABLE_TOKEN_BUDGET,
56
57
  basis: CODEX_TOKEN_BUDGET_PREFLIGHT_BASIS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@martinloop/mcp",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "mcpName": "io.github.Keesan12/martin-loop",
5
5
  "private": false,
6
6
  "type": "module",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/Keesan12/martin-loop",
8
8
  "source": "github"
9
9
  },
10
- "version": "0.8.3",
10
+ "version": "0.8.4",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@martinloop/mcp",
15
- "version": "0.8.3",
15
+ "version": "0.8.4",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  }