@meterbility/spec 0.3.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.
package/LICENSE ADDED
@@ -0,0 +1,33 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Meterbility authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ----------------------------------------------------------------------
24
+
25
+ Note: This MIT license covers everything in this repository EXCEPT the
26
+ contents of the /ee directory (when present), which are licensed under the
27
+ Elastic License 2.0 (ELv2) — see /ee/LICENSE.
28
+
29
+ The /ee directory is reserved for Enterprise Edition modules: multi-tenant
30
+ fleet orchestration, SSO/RBAC, audit logs, and long-retention features.
31
+
32
+ Nothing under /ee today; the directory is created as a forward-compatible
33
+ marker so the licensing boundary is visible before any /ee code lands.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # @meterbility/spec
2
+
3
+ Part of [Meterbility](https://github.com/HoneycombHairDevelopers/Meterbility) — the debugger for AI agents. Capture every run, inspect every decision, pause and inject live, fork from any step.
4
+
5
+ Trace-format version constants, JSON schemas, and the model pricing table used to compute per-step cost.
6
+
7
+ ```bash
8
+ npm install @meterbility/spec
9
+ ```
10
+
11
+ See the [Meterbility documentation](https://github.com/HoneycombHairDevelopers/Meterbility#readme) for the full guide. MIT licensed.
@@ -0,0 +1,66 @@
1
+ import { ModelPricing } from '@meterbility/shared';
2
+
3
+ /**
4
+ * Cents per million tokens. Source: Anthropic public pricing as of May 2026.
5
+ * If a model isn't listed, cost calc falls back to {@link PRICING_FALLBACK}
6
+ * and the run is tagged `cost:approx`.
7
+ *
8
+ * Cache write rates: Anthropic charges 1.25× input for the 5-minute
9
+ * ephemeral cache and 2× input for the 1-hour ephemeral cache. Claude
10
+ * Code uses the 1-hour cache for the long-lived system prompt + tool
11
+ * defs, which is typically the dominant cost line on long sessions.
12
+ *
13
+ * Numbers chosen conservatively — pricing tables drift, and Meterbility's role is
14
+ * "give the operator a magnitude," not "audit the invoice."
15
+ */
16
+ declare const PRICING: ModelPricing[];
17
+ declare const PRICING_FALLBACK: ModelPricing;
18
+ declare function pricingFor(model: string): {
19
+ pricing: ModelPricing;
20
+ approx: boolean;
21
+ };
22
+ interface UsageCents {
23
+ input: number;
24
+ output: number;
25
+ cached_read: number;
26
+ /** 5-minute ephemeral cache write tokens. */
27
+ cache_creation: number;
28
+ /** 1-hour ephemeral cache write tokens (optional). */
29
+ cache_creation_1h?: number;
30
+ }
31
+ /**
32
+ * Compute cost (in fractional cents) for one step from a token-usage breakdown.
33
+ * Returns a float — caller rounds when persisting.
34
+ *
35
+ * The 1h cache rate falls back to 2× input price when a model entry
36
+ * doesn't specify it explicitly — this matches Anthropic's published
37
+ * formula and avoids silently zero-pricing the dominant cost line on
38
+ * Claude Code-style sessions.
39
+ */
40
+ declare function costCents(model: string, usage: UsageCents): {
41
+ cost_cents: number;
42
+ approx: boolean;
43
+ };
44
+
45
+ /**
46
+ * Trace format version.
47
+ *
48
+ * - **0.1.0** — v0 of Meterbility. Run + steps only.
49
+ * - **0.2.0** — v0.1. Cross-vendor source_runtime values, fork/regression
50
+ * metadata, agent-SDK content_ref convention (true blob hashes everywhere).
51
+ * - **0.3.0** — v0.3 (this constant). Adds file-capture data: `file_changes`
52
+ * array, `baseline_trees` array, plus the new `run.baseline_tree_id` and
53
+ * `run.probe_state` columns surfaced via the run object. Per SPEC-V0_3 §12,
54
+ * the format is additive: v0.2 readers ignore unknown components per the
55
+ * v0.2 §11 skip-unknown rule.
56
+ *
57
+ * Backward compatibility: v0.1.0 + v0.2.0 traces import cleanly — every
58
+ * additive field is optional and the run.source_runtime values overlap.
59
+ * Meterbility writes 0.3.0 going forward; older readers should fall back to
60
+ * skipping unknown components rather than failing.
61
+ */
62
+ declare const TRACE_FORMAT_VERSION = "0.3.0";
63
+ declare const SUPPORTED_TRACE_VERSIONS: readonly ["0.1.0", "0.2.0", "0.3.0"];
64
+ type TraceFormatVersion = (typeof SUPPORTED_TRACE_VERSIONS)[number];
65
+
66
+ export { PRICING, PRICING_FALLBACK, SUPPORTED_TRACE_VERSIONS, TRACE_FORMAT_VERSION, type TraceFormatVersion, type UsageCents, costCents, pricingFor };
package/dist/index.js ADDED
@@ -0,0 +1,153 @@
1
+ // src/pricing.ts
2
+ var PRICING = [
3
+ {
4
+ model: "claude-opus-4-7",
5
+ input_per_million_cents: 1500,
6
+ output_per_million_cents: 7500,
7
+ cached_read_per_million_cents: 150,
8
+ cache_creation_per_million_cents: 1875,
9
+ cache_creation_1h_per_million_cents: 3e3
10
+ },
11
+ {
12
+ model: "claude-opus-4-6",
13
+ input_per_million_cents: 1500,
14
+ output_per_million_cents: 7500,
15
+ cached_read_per_million_cents: 150,
16
+ cache_creation_per_million_cents: 1875,
17
+ cache_creation_1h_per_million_cents: 3e3
18
+ },
19
+ {
20
+ model: "claude-opus-4-5",
21
+ input_per_million_cents: 1500,
22
+ output_per_million_cents: 7500,
23
+ cached_read_per_million_cents: 150,
24
+ cache_creation_per_million_cents: 1875,
25
+ cache_creation_1h_per_million_cents: 3e3
26
+ },
27
+ {
28
+ model: "claude-sonnet-4-6",
29
+ input_per_million_cents: 300,
30
+ output_per_million_cents: 1500,
31
+ cached_read_per_million_cents: 30,
32
+ cache_creation_per_million_cents: 375,
33
+ cache_creation_1h_per_million_cents: 600
34
+ },
35
+ {
36
+ model: "claude-sonnet-4-5",
37
+ input_per_million_cents: 300,
38
+ output_per_million_cents: 1500,
39
+ cached_read_per_million_cents: 30,
40
+ cache_creation_per_million_cents: 375,
41
+ cache_creation_1h_per_million_cents: 600
42
+ },
43
+ {
44
+ model: "claude-haiku-4-5-20251001",
45
+ input_per_million_cents: 80,
46
+ output_per_million_cents: 400,
47
+ cached_read_per_million_cents: 8,
48
+ cache_creation_per_million_cents: 100,
49
+ cache_creation_1h_per_million_cents: 160
50
+ },
51
+ // ── OpenAI ────────────────────────────────────────────────────────────
52
+ // OpenAI prompt caching exposes only one tier (no 5m/1h split), so the
53
+ // cache_creation rate matches input (writes are free), and cached_read
54
+ // gets the discounted rate. cache_creation_1h is N/A — left as 2× input
55
+ // by the costCents() fallback so the math doesn't break.
56
+ {
57
+ model: "gpt-5",
58
+ input_per_million_cents: 125,
59
+ output_per_million_cents: 1e3,
60
+ cached_read_per_million_cents: 12,
61
+ cache_creation_per_million_cents: 125
62
+ },
63
+ {
64
+ model: "gpt-5-mini",
65
+ input_per_million_cents: 25,
66
+ output_per_million_cents: 200,
67
+ cached_read_per_million_cents: 2,
68
+ cache_creation_per_million_cents: 25
69
+ },
70
+ {
71
+ model: "gpt-4o",
72
+ input_per_million_cents: 250,
73
+ output_per_million_cents: 1e3,
74
+ cached_read_per_million_cents: 125,
75
+ cache_creation_per_million_cents: 250
76
+ },
77
+ {
78
+ model: "gpt-4o-mini",
79
+ input_per_million_cents: 15,
80
+ output_per_million_cents: 60,
81
+ cached_read_per_million_cents: 7,
82
+ cache_creation_per_million_cents: 15
83
+ },
84
+ {
85
+ model: "gpt-4-turbo",
86
+ input_per_million_cents: 1e3,
87
+ output_per_million_cents: 3e3,
88
+ cached_read_per_million_cents: 500,
89
+ cache_creation_per_million_cents: 1e3
90
+ },
91
+ {
92
+ model: "o1",
93
+ input_per_million_cents: 1500,
94
+ output_per_million_cents: 6e3,
95
+ cached_read_per_million_cents: 750,
96
+ cache_creation_per_million_cents: 1500
97
+ },
98
+ {
99
+ model: "o1-mini",
100
+ input_per_million_cents: 110,
101
+ output_per_million_cents: 440,
102
+ cached_read_per_million_cents: 55,
103
+ cache_creation_per_million_cents: 110
104
+ },
105
+ {
106
+ model: "o3",
107
+ input_per_million_cents: 1e3,
108
+ output_per_million_cents: 4e3,
109
+ cached_read_per_million_cents: 250,
110
+ cache_creation_per_million_cents: 1e3
111
+ },
112
+ {
113
+ model: "o3-mini",
114
+ input_per_million_cents: 110,
115
+ output_per_million_cents: 440,
116
+ cached_read_per_million_cents: 55,
117
+ cache_creation_per_million_cents: 110
118
+ }
119
+ ];
120
+ var PRICING_FALLBACK = {
121
+ model: "unknown",
122
+ input_per_million_cents: 1500,
123
+ output_per_million_cents: 7500,
124
+ cached_read_per_million_cents: 150,
125
+ cache_creation_per_million_cents: 1875,
126
+ cache_creation_1h_per_million_cents: 3e3
127
+ };
128
+ function pricingFor(model) {
129
+ for (const row of PRICING) {
130
+ if (model === row.model || model.startsWith(row.model)) {
131
+ return { pricing: row, approx: false };
132
+ }
133
+ }
134
+ return { pricing: PRICING_FALLBACK, approx: true };
135
+ }
136
+ function costCents(model, usage) {
137
+ const { pricing, approx } = pricingFor(model);
138
+ const cache1hRate = pricing.cache_creation_1h_per_million_cents ?? pricing.input_per_million_cents * 2;
139
+ const cost = usage.input * pricing.input_per_million_cents / 1e6 + usage.output * pricing.output_per_million_cents / 1e6 + usage.cached_read * pricing.cached_read_per_million_cents / 1e6 + usage.cache_creation * pricing.cache_creation_per_million_cents / 1e6 + (usage.cache_creation_1h ?? 0) * cache1hRate / 1e6;
140
+ return { cost_cents: cost, approx };
141
+ }
142
+
143
+ // src/index.ts
144
+ var TRACE_FORMAT_VERSION = "0.3.0";
145
+ var SUPPORTED_TRACE_VERSIONS = ["0.1.0", "0.2.0", "0.3.0"];
146
+ export {
147
+ PRICING,
148
+ PRICING_FALLBACK,
149
+ SUPPORTED_TRACE_VERSIONS,
150
+ TRACE_FORMAT_VERSION,
151
+ costCents,
152
+ pricingFor
153
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@meterbility/spec",
3
+ "version": "0.3.1",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "description": "Trace-format versions, JSON schemas, and model pricing tables for Meterbility",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/HoneycombHairDevelopers/Meterbility.git",
17
+ "directory": "packages/spec"
18
+ },
19
+ "homepage": "https://github.com/HoneycombHairDevelopers/Meterbility#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/HoneycombHairDevelopers/Meterbility/issues"
22
+ },
23
+ "engines": {
24
+ "node": ">=20.6"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "schemas"
32
+ ],
33
+ "types": "dist/index.d.ts",
34
+ "tsup": {
35
+ "entry": [
36
+ "src/index.ts"
37
+ ],
38
+ "format": [
39
+ "esm"
40
+ ],
41
+ "dts": true,
42
+ "clean": true
43
+ },
44
+ "scripts": {
45
+ "build": "tsup"
46
+ },
47
+ "dependencies": {
48
+ "@meterbility/shared": "^0.3.0"
49
+ }
50
+ }
@@ -0,0 +1,91 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://meter.dev/schemas/trace-format/0.1.0",
4
+ "title": "Meterbility Trace Format",
5
+ "version": "0.1.0",
6
+ "description": "Wire format for Meterbility traces. Hosts emit one JSONL file per run; each line is a Step. Context snapshots and decision blobs are content-addressed and referenced by SHA256.",
7
+ "type": "object",
8
+ "required": ["meter_trace_version", "run", "steps"],
9
+ "properties": {
10
+ "meter_trace_version": { "const": "0.1.0" },
11
+ "run": { "$ref": "#/$defs/Run" },
12
+ "steps": { "type": "array", "items": { "$ref": "#/$defs/Step" } },
13
+ "blobs": {
14
+ "type": "object",
15
+ "additionalProperties": { "type": "string", "description": "base64-encoded UTF-8 content" }
16
+ }
17
+ },
18
+ "$defs": {
19
+ "Run": {
20
+ "type": "object",
21
+ "required": ["run_id", "started_at", "source_runtime", "status"],
22
+ "properties": {
23
+ "run_id": { "type": "string" },
24
+ "agent_id": { "type": "string" },
25
+ "project_id": { "type": "string" },
26
+ "source_runtime": { "enum": ["claude-code", "codex-cli", "cursor", "sdk-ts", "fork"] },
27
+ "source_session_id": { "type": "string" },
28
+ "title": { "type": "string" },
29
+ "status": { "enum": ["ok", "error", "abandoned", "in_progress"] },
30
+ "started_at": { "type": "string", "format": "date-time" },
31
+ "ended_at": { "type": "string", "format": "date-time" },
32
+ "git_branch": { "type": "string" },
33
+ "cwd": { "type": "string" },
34
+ "fork_origin_run_id": { "type": "string" },
35
+ "fork_origin_step_id": { "type": "string" },
36
+ "tags": { "type": "array", "items": { "type": "string" } }
37
+ }
38
+ },
39
+ "Step": {
40
+ "type": "object",
41
+ "required": ["step_id", "sequence", "timestamp", "model", "action", "outcome"],
42
+ "properties": {
43
+ "step_id": { "type": "string" },
44
+ "run_id": { "type": "string" },
45
+ "parent_step_id": { "type": "string" },
46
+ "fork_origin_id": { "type": "string" },
47
+ "sequence": { "type": "integer", "minimum": 0 },
48
+ "timestamp": { "type": "string", "format": "date-time" },
49
+ "model": { "type": "string" },
50
+ "context_snapshot_id": { "type": "string", "description": "SHA256 of canonicalized context snapshot" },
51
+ "decision_ref": { "type": "string", "description": "SHA256 of raw model output bytes" },
52
+ "action": {
53
+ "type": "object",
54
+ "required": ["kind"],
55
+ "properties": {
56
+ "kind": { "enum": ["tool_call", "message", "sub_agent_dispatch", "thinking_only", "none"] },
57
+ "tool_name": { "type": "string" },
58
+ "tool_use_id": { "type": "string" },
59
+ "tool_input": {},
60
+ "text": { "type": "string" },
61
+ "sub_agent": { "type": "string" }
62
+ }
63
+ },
64
+ "outcome": {
65
+ "type": "object",
66
+ "required": ["status"],
67
+ "properties": {
68
+ "status": { "enum": ["ok", "error", "pending"] },
69
+ "tool_result_ref": { "type": "string" },
70
+ "is_error": { "type": "boolean" },
71
+ "summary": { "type": "string" }
72
+ }
73
+ },
74
+ "tokens": {
75
+ "type": "object",
76
+ "properties": {
77
+ "input": { "type": "integer" },
78
+ "output": { "type": "integer" },
79
+ "cached_read": { "type": "integer" },
80
+ "cache_creation": { "type": "integer" },
81
+ "reasoning": { "type": "integer" }
82
+ }
83
+ },
84
+ "latency_ms": { "type": "integer" },
85
+ "cost_cents": { "type": "number" },
86
+ "status": { "enum": ["ok", "error", "abandoned", "in_progress"] },
87
+ "tags": { "type": "array", "items": { "type": "string" } }
88
+ }
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,146 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://meter.dev/schemas/trace-format/0.2.0",
4
+ "title": "Meterbility Trace Format",
5
+ "version": "0.2.0",
6
+ "description": "Wire format for Meterbility traces. Hosts emit one JSON document per run; each step is content-addressed via SHA256 refs. v0.2 adds cross-vendor source_runtime values, fork/regression metadata, and explicit blob refs everywhere.",
7
+ "type": "object",
8
+ "required": ["meter_trace_version", "run", "steps"],
9
+ "properties": {
10
+ "meter_trace_version": { "enum": ["0.1.0", "0.2.0"] },
11
+ "run": { "$ref": "#/$defs/Run" },
12
+ "steps": { "type": "array", "items": { "$ref": "#/$defs/Step" } },
13
+ "blobs": {
14
+ "type": "object",
15
+ "additionalProperties": { "type": "string", "description": "base64-encoded UTF-8 content" }
16
+ },
17
+ "annotations": {
18
+ "type": "array",
19
+ "items": { "$ref": "#/$defs/Annotation" }
20
+ },
21
+ "regressions": {
22
+ "type": "array",
23
+ "items": { "$ref": "#/$defs/RegressionResult" }
24
+ }
25
+ },
26
+ "$defs": {
27
+ "Run": {
28
+ "type": "object",
29
+ "required": ["run_id", "started_at", "source_runtime", "status"],
30
+ "properties": {
31
+ "run_id": { "type": "string" },
32
+ "agent_id": { "type": "string" },
33
+ "project_id": { "type": "string" },
34
+ "source_runtime": {
35
+ "enum": ["claude-code", "codex-cli", "cursor", "sdk-ts", "sdk-py", "fork"]
36
+ },
37
+ "source_session_id": { "type": "string" },
38
+ "title": { "type": "string" },
39
+ "status": { "enum": ["ok", "error", "abandoned", "in_progress"] },
40
+ "started_at": { "type": "string", "format": "date-time" },
41
+ "ended_at": { "type": "string", "format": "date-time" },
42
+ "git_branch": { "type": "string" },
43
+ "cwd": { "type": "string" },
44
+ "fork_origin_run_id": { "type": "string" },
45
+ "fork_origin_step_id": { "type": "string" },
46
+ "tags": { "type": "array", "items": { "type": "string" } }
47
+ }
48
+ },
49
+ "Step": {
50
+ "type": "object",
51
+ "required": ["step_id", "sequence", "timestamp", "model", "action", "outcome"],
52
+ "properties": {
53
+ "step_id": { "type": "string" },
54
+ "run_id": { "type": "string" },
55
+ "parent_step_id": { "type": "string" },
56
+ "fork_origin_id": { "type": "string" },
57
+ "sequence": { "type": "integer", "minimum": 0 },
58
+ "timestamp": { "type": "string", "format": "date-time" },
59
+ "model": { "type": "string" },
60
+ "context_snapshot_id": { "type": "string" },
61
+ "decision_ref": { "type": "string" },
62
+ "action": {
63
+ "type": "object",
64
+ "required": ["kind"],
65
+ "properties": {
66
+ "kind": { "enum": ["tool_call", "message", "sub_agent_dispatch", "thinking_only", "none"] },
67
+ "tool_name": { "type": "string" },
68
+ "tool_use_id": { "type": "string" },
69
+ "tool_input": {},
70
+ "text": { "type": "string" },
71
+ "sub_agent": { "type": "string" }
72
+ }
73
+ },
74
+ "outcome": {
75
+ "type": "object",
76
+ "required": ["status"],
77
+ "properties": {
78
+ "status": { "enum": ["ok", "error", "pending"] },
79
+ "tool_result_ref": { "type": "string" },
80
+ "is_error": { "type": "boolean" },
81
+ "summary": { "type": "string" }
82
+ }
83
+ },
84
+ "tokens": {
85
+ "type": "object",
86
+ "properties": {
87
+ "input": { "type": "integer" },
88
+ "output": { "type": "integer" },
89
+ "cached_read": { "type": "integer" },
90
+ "cache_creation": { "type": "integer" },
91
+ "reasoning": { "type": "integer" }
92
+ }
93
+ },
94
+ "latency_ms": { "type": "integer" },
95
+ "cost_cents": { "type": "number" },
96
+ "status": { "enum": ["ok", "error", "abandoned", "in_progress"] },
97
+ "tags": { "type": "array", "items": { "type": "string" } }
98
+ }
99
+ },
100
+ "Annotation": {
101
+ "type": "object",
102
+ "required": ["target_kind", "target_id", "author", "created_at"],
103
+ "properties": {
104
+ "target_kind": { "enum": ["step", "run"] },
105
+ "target_id": { "type": "string" },
106
+ "author": { "type": "string" },
107
+ "verdict": {
108
+ "enum": ["correct", "incorrect", "unclear", "good_decision", "bad_decision"]
109
+ },
110
+ "note": { "type": "string" },
111
+ "created_at": { "type": "string", "format": "date-time" }
112
+ }
113
+ },
114
+ "RegressionResult": {
115
+ "type": "object",
116
+ "required": ["test_id", "test_name", "run_id", "passed"],
117
+ "properties": {
118
+ "test_id": { "type": "string" },
119
+ "test_name": { "type": "string" },
120
+ "run_id": { "type": "string" },
121
+ "passed": { "type": "boolean" },
122
+ "assertions": {
123
+ "type": "array",
124
+ "items": {
125
+ "type": "object",
126
+ "required": ["assertion", "passed", "reason"],
127
+ "properties": {
128
+ "assertion": {
129
+ "type": "object",
130
+ "required": ["kind", "value"],
131
+ "properties": {
132
+ "kind": { "type": "string" },
133
+ "value": {},
134
+ "at": { "type": "integer" },
135
+ "label": { "type": "string" }
136
+ }
137
+ },
138
+ "passed": { "type": "boolean" },
139
+ "reason": { "type": "string" }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }