mira-harness 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -101,11 +101,12 @@ a "typing…" fallback for a slow bot — replies run 5–62s) and capture, per
101
101
  | `login` | One-time interactive login → prints `TG_SESSION` |
102
102
  | `doctor` | Check `.env` / session / connectivity / @mira resolution (read-only) |
103
103
  | `send [message...]` | One probe → full reply as JSON (message via arg or stdin). `--quiet --settle --timeout --no-log` |
104
- | `loop` | Run the catalog paced. `--category --max --confirm --peer --gap --settle --timeout --list --catalog --quiet` |
104
+ | `loop` | Run the catalog paced; grades `expect` probes (exit 1 on failure). `--category --max --confirm --peer --gap --settle --timeout --list --catalog --no-fail --quiet` |
105
105
  | `catalog` | List the catalog (no sends). `--category --catalog --json` |
106
106
  | `watch` | Live-tail @mira's messages (observe-only). `--peer` |
107
107
  | `report` | Distill the run log into Markdown. `--in --out --category` |
108
108
  | `stats` | At-a-glance dashboard: totals, latency records, sparkline. `--in --category --json` |
109
+ | `diff` | Compare two run logs for @mira behavioral drift (exit 1 on a regression). `--json --no-fail` |
109
110
 
110
111
  Run `mira-harness --help` (or `<command> --help`) for full options.
111
112
 
@@ -130,13 +131,48 @@ per-feature flags — `MIRA_NO_BANNER=1` (mascot + tip), `MIRA_NO_NOTIFY=1` (com
130
131
  The built-in catalog (27 probes: `core` / `skills` / `generation` / `wallet`) is just a
131
132
  default. Point `--catalog <file.json>` (CLI) or `catalogFile` (MCP) at your own probe set
132
133
  to probe any bot — each entry needs `id` + `send` (`category` / `hypothesis` / `slow` /
133
- `confirm` / `note` optional). See [`examples/catalog.sample.json`](examples/catalog.sample.json):
134
+ `confirm` / `note` / `expect` optional). See [`examples/catalog.sample.json`](examples/catalog.sample.json):
134
135
 
135
136
  ```bash
136
137
  mira-harness loop --catalog ./examples/catalog.sample.json
137
138
  mira-harness catalog --catalog ./examples/catalog.sample.json --json
138
139
  ```
139
140
 
141
+ ### Assertions (PASS/FAIL)
142
+
143
+ Give a probe an optional `expect` block and `loop` grades it ✓/✗. The checks are **structural**
144
+ — @mira is an LLM (non-deterministic), so exact-text matches would flake:
145
+
146
+ | Check | Means |
147
+ |---|---|
148
+ | `replies: true` | a reply arrived (no timeout) |
149
+ | `textMatches: "<regex>"` | some message text matches (case-insensitive) |
150
+ | `minButtons` / `minLinks` | at least N inline buttons / links across messages |
151
+ | `hasWebApp: true` | a Mini App (`web_app` / startapp) "Launch" button is present |
152
+ | `media: "photo"` | a message carries media of that kind (`photo`/`video`/`audio`/…) |
153
+ | `maxFirstReplyMs` | first-reply latency within the bound |
154
+ | `json: true` | the first message text parses as JSON |
155
+
156
+ ```json
157
+ { "id": "json-strict", "send": "Reply with ONLY {\"ok\":true}", "expect": { "json": true } }
158
+ ```
159
+
160
+ Probes without `expect` stay observe-only (informational). `loop` **exits non-zero** if any
161
+ graded probe fails — so it drops straight into CI. Add `--no-fail` to report without failing.
162
+
163
+ ### Drift detection
164
+
165
+ `diff` compares two run logs and flags how @mira's behavior **changed** (structural, not
166
+ exact text). **Regressions** — an assertion that flipped ✓→✗, a probe that now times out, a
167
+ >2× latency blow-up — exit non-zero; surface changes (buttons / links / media) and
168
+ improvements are reported but pass. Snapshot a baseline, re-run later, diff:
169
+
170
+ ```bash
171
+ MIRA_RUNS_FILE=baseline.jsonl mira-harness loop --category core # snapshot a baseline
172
+ mira-harness loop --category core # a later run -> mira-runs.jsonl
173
+ mira-harness diff baseline.jsonl # vs the current run log
174
+ ```
175
+
140
176
  ## Use as a library
141
177
 
142
178
  The CLI is a thin frontend over an exported core:
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Probe assertions — turn an observed reply into a PASS/FAIL verdict.
3
+ *
4
+ * A probe's optional `expect` declares machine-checkable expectations; `evaluate`
5
+ * runs them against the captured ProbeResult. @mira is an LLM (non-deterministic),
6
+ * so the checks are deliberately STRUCTURAL and loose — a reply arrived, >= N
7
+ * links, valid JSON, a latency bound — never exact text, which would flake.
8
+ *
9
+ * Probes WITHOUT `expect` are not graded: they stay observe-only / informational.
10
+ */
11
+ import { z } from "zod";
12
+ import type { ProbeResult } from "./capture.js";
13
+ export declare const ExpectSchema: z.ZodObject<{
14
+ replies: z.ZodOptional<z.ZodBoolean>;
15
+ textMatches: z.ZodOptional<z.ZodString>;
16
+ minButtons: z.ZodOptional<z.ZodNumber>;
17
+ minLinks: z.ZodOptional<z.ZodNumber>;
18
+ hasWebApp: z.ZodOptional<z.ZodBoolean>;
19
+ media: z.ZodOptional<z.ZodEnum<{
20
+ photo: "photo";
21
+ video: "video";
22
+ audio: "audio";
23
+ document: "document";
24
+ webpage: "webpage";
25
+ other: "other";
26
+ }>>;
27
+ maxFirstReplyMs: z.ZodOptional<z.ZodNumber>;
28
+ json: z.ZodOptional<z.ZodBoolean>;
29
+ }, z.core.$strip>;
30
+ export type Expect = z.infer<typeof ExpectSchema>;
31
+ export interface Check {
32
+ name: string;
33
+ ok: boolean;
34
+ detail: string;
35
+ }
36
+ export interface Verdict {
37
+ ok: boolean;
38
+ checks: Check[];
39
+ }
40
+ /** Run a probe's expectations against its captured result. Pure — no network. */
41
+ export declare function evaluate(expect: Expect, result: ProbeResult): Verdict;
package/dist/catalog.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type Expect } from "./assert.js";
1
2
  /** Built-in categories. Custom catalogs may use any category string. */
2
3
  export type ProbeCategory = "core" | "skills" | "generation" | "wallet";
3
4
  export interface Probe {
@@ -15,6 +16,8 @@ export interface Probe {
15
16
  */
16
17
  confirm?: boolean;
17
18
  note?: string;
19
+ /** Optional machine-checkable expectations — graded by `loop` (PASS/FAIL). */
20
+ expect?: Expect;
18
21
  }
19
22
  export declare const CATEGORIES: ProbeCategory[];
20
23
  /**