@mono-agent/agent-runtime 0.12.0 → 0.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mono-agent/agent-runtime",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Agent runtime supporting Claude SDK/CLI, Codex, OpenCode, and Pi SDK bridges out of the box",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-only",
@@ -334,7 +334,7 @@ function readSkillTool(skillNames = [], { skillsRoot, dataDir, skills = [] } = {
334
334
  return {
335
335
  name: "ReadSkill",
336
336
  label: "Read Skill",
337
- description: "Load the full instructions for a named skill.",
337
+ description: "Load the complete instructions for a named skill. Use ReadSkill instead of Read for SKILL.md files.",
338
338
  parameters: objectSchema({ name: { type: "string", enum: enumNames } }, ["name"]),
339
339
  async execute(_toolCallId, { name }) {
340
340
  if (sharedRoot) {
@@ -7,6 +7,7 @@
7
7
  // takes its inputs explicitly and returns the verbatim runtime-result contract
8
8
  // (diagnostics key spellings, error fields, and shape unchanged — I9).
9
9
 
10
+ import { calculateContextTokens } from "@earendil-works/pi-agent-core";
10
11
  import { isContextLimitError } from "../pi-errors.js";
11
12
  import { isLikelyContextTermination } from "../../../agent/compaction.js";
12
13
  import { isProviderAuthFailureText } from "../../failure.js";
@@ -31,6 +32,28 @@ export function usageFromMessages(messages = []) {
31
32
  return usage;
32
33
  }
33
34
 
35
+ /**
36
+ * Normalize the final provider request's usage into an exact context snapshot.
37
+ * Unlike usageFromMessages(), this deliberately does not aggregate earlier
38
+ * requests in the run: the last assistant usage is the same provider-counted
39
+ * value Pi's compaction logic trusts, so it can decrease after compaction.
40
+ * @param {any} assistantMessage
41
+ * @returns {{input: number, output: number, cacheRead: number, cacheCreation: number, total: number}|null}
42
+ */
43
+ export function contextUsageFromAssistantMessage(assistantMessage) {
44
+ if (assistantMessage?.role !== "assistant" || !assistantMessage.usage) return null;
45
+ if (assistantMessage.stopReason === "error" || assistantMessage.stopReason === "aborted") return null;
46
+ const total = Number(calculateContextTokens(assistantMessage.usage)) || 0;
47
+ if (total <= 0) return null;
48
+ return {
49
+ input: Number(assistantMessage.usage.input) || 0,
50
+ output: Number(assistantMessage.usage.output) || 0,
51
+ cacheRead: Number(assistantMessage.usage.cacheRead) || 0,
52
+ cacheCreation: Number(assistantMessage.usage.cacheWrite) || 0,
53
+ total,
54
+ };
55
+ }
56
+
34
57
  /**
35
58
  * Classify a pi error message into a runtime failure kind. Context-window
36
59
  * overflows map to context_limit so the router can try the configured fallback;
@@ -51,9 +74,19 @@ export function failureKindForPiError(message, diagnostics, { maxTurnsHit = fals
51
74
 
52
75
  /**
53
76
  * Emit the per-run cache / cost / provider-completed events.
54
- * @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, estimatedCost: number, start: number, externalAbort: boolean}} params
77
+ * @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, contextUsage?: {input: number, output: number, cacheRead: number, cacheCreation: number, total: number}|null, contextWindow?: number, estimatedCost: number, start: number, externalAbort: boolean}} params
55
78
  */
56
- export function emitUsageCostEvents({ onEvent, resolved, reference, usage, estimatedCost, start, externalAbort }) {
79
+ export function emitUsageCostEvents({
80
+ onEvent,
81
+ resolved,
82
+ reference,
83
+ usage,
84
+ contextUsage,
85
+ contextWindow,
86
+ estimatedCost,
87
+ start,
88
+ externalAbort,
89
+ }) {
57
90
  if (usage.cacheRead > 0) {
58
91
  onEvent({ type: "cache_hit", sdk: resolved.sdk, model: reference, tokens: usage.cacheRead, source: "prompt_cache" });
59
92
  }
@@ -72,6 +105,16 @@ export function emitUsageCostEvents({ onEvent, resolved, reference, usage, estim
72
105
  cacheCreationTokens: Number(usage.cacheWrite) || 0,
73
106
  },
74
107
  });
108
+ if (contextUsage) {
109
+ const effectiveContextWindow = Number(contextWindow) || 0;
110
+ onEvent({
111
+ type: "context_usage",
112
+ sdk: resolved.sdk,
113
+ model: reference,
114
+ ...(effectiveContextWindow > 0 ? { contextWindow: effectiveContextWindow } : {}),
115
+ tokens: contextUsage,
116
+ });
117
+ }
75
118
  onEvent({
76
119
  type: "provider_request_completed",
77
120
  sdk: resolved.sdk,
@@ -52,6 +52,7 @@ import {
52
52
  buildErrorDetails,
53
53
  buildErrorResult,
54
54
  buildSuccessResult,
55
+ contextUsageFromAssistantMessage,
55
56
  emitCapabilitiesResolved,
56
57
  emitUsageCostEvents,
57
58
  usageFromMessages,
@@ -608,7 +609,19 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
608
609
  cachedTokens: usage.cacheRead,
609
610
  cacheWriteTokens: usage.cacheWrite,
610
611
  });
611
- emitUsageCostEvents({ onEvent, resolved, reference, usage, estimatedCost, start, externalAbort: runState.externalAbort });
612
+ emitUsageCostEvents({
613
+ onEvent,
614
+ resolved,
615
+ reference,
616
+ usage,
617
+ contextUsage: runState.externalAbort || runState.maxTurnsHit || runError
618
+ ? null
619
+ : contextUsageFromAssistantMessage(lastAssistant),
620
+ contextWindow: runState.compaction.policy?.contextWindow,
621
+ estimatedCost,
622
+ start,
623
+ externalAbort: runState.externalAbort,
624
+ });
612
625
 
613
626
  const rawErrorMessage = runState.externalAbort
614
627
  ? null
@@ -10,6 +10,21 @@ export function usageFromMessages(messages?: Array<any>): {
10
10
  cacheWrite: number;
11
11
  cost: number;
12
12
  };
13
+ /**
14
+ * Normalize the final provider request's usage into an exact context snapshot.
15
+ * Unlike usageFromMessages(), this deliberately does not aggregate earlier
16
+ * requests in the run: the last assistant usage is the same provider-counted
17
+ * value Pi's compaction logic trusts, so it can decrease after compaction.
18
+ * @param {any} assistantMessage
19
+ * @returns {{input: number, output: number, cacheRead: number, cacheCreation: number, total: number}|null}
20
+ */
21
+ export function contextUsageFromAssistantMessage(assistantMessage: any): {
22
+ input: number;
23
+ output: number;
24
+ cacheRead: number;
25
+ cacheCreation: number;
26
+ total: number;
27
+ } | null;
13
28
  /**
14
29
  * Classify a pi error message into a runtime failure kind. Context-window
15
30
  * overflows map to context_limit so the router can try the configured fallback;
@@ -25,9 +40,9 @@ export function failureKindForPiError(message: string | null, diagnostics: Recor
25
40
  }): string | null;
26
41
  /**
27
42
  * Emit the per-run cache / cost / provider-completed events.
28
- * @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, estimatedCost: number, start: number, externalAbort: boolean}} params
43
+ * @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, contextUsage?: {input: number, output: number, cacheRead: number, cacheCreation: number, total: number}|null, contextWindow?: number, estimatedCost: number, start: number, externalAbort: boolean}} params
29
44
  */
30
- export function emitUsageCostEvents({ onEvent, resolved, reference, usage, estimatedCost, start, externalAbort }: {
45
+ export function emitUsageCostEvents({ onEvent, resolved, reference, usage, contextUsage, contextWindow, estimatedCost, start, externalAbort, }: {
31
46
  onEvent: (event: any) => void;
32
47
  resolved: any;
33
48
  reference: string;
@@ -38,6 +53,14 @@ export function emitUsageCostEvents({ onEvent, resolved, reference, usage, estim
38
53
  cacheWrite: number;
39
54
  cost: number;
40
55
  };
56
+ contextUsage?: {
57
+ input: number;
58
+ output: number;
59
+ cacheRead: number;
60
+ cacheCreation: number;
61
+ total: number;
62
+ } | null;
63
+ contextWindow?: number;
41
64
  estimatedCost: number;
42
65
  start: number;
43
66
  externalAbort: boolean;