@moda-ai/cli 1.9.6 → 1.10.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/dist/schemas.d.ts CHANGED
@@ -141,6 +141,26 @@ export declare const ToolFailureDetailSchema: z.ZodObject<{
141
141
  include_window?: unknown;
142
142
  subtype?: string | undefined;
143
143
  }>;
144
+ /**
145
+ * moda audit <conversation_id|trace_id> — raw, non-deduped span/trace read.
146
+ * `kind=auto` (default) lets the backend try trace first, then conversation.
147
+ */
148
+ export declare const AuditSchema: z.ZodObject<{
149
+ id: z.ZodString;
150
+ kind: z.ZodOptional<z.ZodDefault<z.ZodEnum<["auto", "trace", "conversation"]>>>;
151
+ include_raw: z.ZodOptional<z.ZodEffects<z.ZodBoolean, boolean, unknown>>;
152
+ limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
153
+ }, "strip", z.ZodTypeAny, {
154
+ id: string;
155
+ kind?: "auto" | "trace" | "conversation" | undefined;
156
+ limit?: number | undefined;
157
+ include_raw?: boolean | undefined;
158
+ }, {
159
+ id: string;
160
+ kind?: "auto" | "trace" | "conversation" | undefined;
161
+ limit?: number | undefined;
162
+ include_raw?: unknown;
163
+ }>;
144
164
  /**
145
165
  * moda search "<query>" — keyword/semantic/hybrid search across conversation
146
166
  * messages. `q` arrives as a positional arg; the runCommand handler rejects an
@@ -165,49 +185,6 @@ export declare const SearchSchema: z.ZodObject<{
165
185
  limit?: number | undefined;
166
186
  user_id?: string | undefined;
167
187
  }>;
168
- /** moda workflows — recurring workflow catalog (read from agent_job_classes). */
169
- export declare const WorkflowsSchema: z.ZodObject<{
170
- time_range: z.ZodOptional<z.ZodDefault<z.ZodEnum<["all", "1h", "3d", "7d", "24h", "30d", "90d"]>>>;
171
- user_id: z.ZodOptional<z.ZodString>;
172
- }, "strip", z.ZodTypeAny, {
173
- time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
174
- user_id?: string | undefined;
175
- }, {
176
- time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
177
- user_id?: string | undefined;
178
- }>;
179
- /** moda runs — list workflow executions with classified outcomes. */
180
- export declare const RunsSchema: z.ZodObject<{
181
- time_range: z.ZodOptional<z.ZodDefault<z.ZodEnum<["all", "1h", "3d", "7d", "24h", "30d", "90d"]>>>;
182
- outcome: z.ZodOptional<z.ZodEnum<["running", "succeeded", "partial", "failed", "loop", "abandoned", "unknown"]>>;
183
- /** Filter to one workflow class id (e.g. cron:helpscout-triage-hourly). */
184
- class_id: z.ZodOptional<z.ZodString>;
185
- user_id: z.ZodOptional<z.ZodString>;
186
- limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
187
- offset: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
188
- }, "strip", z.ZodTypeAny, {
189
- time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
190
- limit?: number | undefined;
191
- offset?: number | undefined;
192
- user_id?: string | undefined;
193
- outcome?: "unknown" | "running" | "failed" | "succeeded" | "partial" | "loop" | "abandoned" | undefined;
194
- class_id?: string | undefined;
195
- }, {
196
- time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
197
- limit?: number | undefined;
198
- offset?: number | undefined;
199
- user_id?: string | undefined;
200
- outcome?: "unknown" | "running" | "failed" | "succeeded" | "partial" | "loop" | "abandoned" | undefined;
201
- class_id?: string | undefined;
202
- }>;
203
- /** moda run <runId> — single execution detail with side-effect receipts. */
204
- export declare const RunDetailSchema: z.ZodObject<{
205
- run_id: z.ZodString;
206
- }, "strip", z.ZodTypeAny, {
207
- run_id: string;
208
- }, {
209
- run_id: string;
210
- }>;
211
188
  /** moda loops — currently active loop detections. */
212
189
  export declare const LoopsSchema: z.ZodObject<{
213
190
  limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
package/dist/schemas.js CHANGED
@@ -68,13 +68,16 @@ export const ToolFailureDetailSchema = z.object({
68
68
  include_window: boolFlag(),
69
69
  window: z.number().min(1).max(5).default(1).optional(),
70
70
  });
71
- // ─── Workflows + Runs (agent runs runtime) ──────────────────
72
- //
73
- // Naming model:
74
- // workflow = the recurring class definition (e.g. cron:helpscout-triage-hourly)
75
- // run = one execution of a workflow
76
- // `moda workflows` lists the catalog; `moda runs` lists executions.
77
- const RUN_OUTCOMES = ['running', 'succeeded', 'partial', 'failed', 'loop', 'abandoned', 'unknown'];
71
+ /**
72
+ * moda audit <conversation_id|trace_id> — raw, non-deduped span/trace read.
73
+ * `kind=auto` (default) lets the backend try trace first, then conversation.
74
+ */
75
+ export const AuditSchema = z.object({
76
+ id: z.string().min(1),
77
+ kind: z.enum(['auto', 'trace', 'conversation']).default('auto').optional(),
78
+ include_raw: boolFlag(),
79
+ limit: z.number().min(1).max(1000).default(200).optional(),
80
+ });
78
81
  const TIME_RANGES = ['all', '1h', '3d', '7d', '24h', '30d', '90d'];
79
82
  const SEARCH_MODES = ['keyword', 'semantic', 'hybrid'];
80
83
  /**
@@ -89,25 +92,6 @@ export const SearchSchema = z.object({
89
92
  time_range: z.enum(TIME_RANGES).default('all').optional(),
90
93
  limit: z.number().min(1).max(100).default(20).optional(),
91
94
  });
92
- /** moda workflows — recurring workflow catalog (read from agent_job_classes). */
93
- export const WorkflowsSchema = z.object({
94
- time_range: z.enum(TIME_RANGES).default('all').optional(),
95
- user_id: z.string().optional(),
96
- });
97
- /** moda runs — list workflow executions with classified outcomes. */
98
- export const RunsSchema = z.object({
99
- time_range: z.enum(TIME_RANGES).default('7d').optional(),
100
- outcome: z.enum(RUN_OUTCOMES).optional(),
101
- /** Filter to one workflow class id (e.g. cron:helpscout-triage-hourly). */
102
- class_id: z.string().optional(),
103
- user_id: z.string().optional(),
104
- limit: z.number().min(1).max(100).default(50).optional(),
105
- offset: z.number().min(0).default(0).optional(),
106
- });
107
- /** moda run <runId> — single execution detail with side-effect receipts. */
108
- export const RunDetailSchema = z.object({
109
- run_id: z.string().uuid({ message: 'run_id must be a UUID' }),
110
- });
111
95
  /** moda loops — currently active loop detections. */
112
96
  export const LoopsSchema = z.object({
113
97
  limit: z.number().min(1).max(200).default(25).optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.9.6",
3
+ "version": "1.10.1",
4
4
  "description": "CLI for Moda - AI agent analytics and observability",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: moda-cli
3
3
  version: 2.0.0
4
- description: Query Moda's AI agent conversation analytics and manage code-first prompt versions from the terminal — semantic/keyword/hybrid message search, overview KPIs, topic clusters, message context, user frustration detections, tool failures, agent jobs, recurring workflows, active loop detections, and moda prompts status/sync/promote. Use when the user asks about moda, modaflows, conversation analytics, prompt management, user frustrations, agent observability, tool failure debugging, why an agent run partially completed or looped, wants to find conversations or tool calls about a topic, or wants to investigate how their AI agent is performing.
4
+ description: Query Moda's AI agent conversation analytics and manage code-first prompt versions from the terminal — semantic/keyword/hybrid message search, overview KPIs, topic clusters, message context, user frustration detections, tool failures, active loop detections, and moda prompts status/sync/promote. Use when the user asks about moda, modaflows, conversation analytics, prompt management, user frustrations, agent observability, tool failure debugging, wants to find conversations or tool calls about a topic, or wants to investigate how their AI agent is performing.
5
5
  ---
6
6
 
7
7
  # Moda CLI
@@ -78,6 +78,87 @@ npx -p @moda-ai/cli moda <command>
78
78
  export MODA_API_KEY="moda_sk_..." # from moda.dev/settings
79
79
  ```
80
80
 
81
+ ## Agent protocol
82
+
83
+ `moda` is agent-safe: it picks an output mode automatically, signals how much
84
+ to trust an answer through exit codes, and (in agent mode) emits a
85
+ schema-versioned `moda.agent.v1` JSON envelope you can parse instead of
86
+ scraping prose.
87
+
88
+ ### Output mode auto-selection
89
+
90
+ The mode is resolved in this priority order — first match wins, so you usually
91
+ need no flags at all:
92
+
93
+ 1. **Explicit flags** override everything:
94
+ - `--agent` — `moda.agent.v1` schema-versioned JSON envelope
95
+ - `--json` — raw JSON payload (no envelope)
96
+ - `--agent --stream` — NDJSON stream of `moda.agent_event.v1` events
97
+ - `--human` — force human prose
98
+ 2. **`MODA_FORMAT` env var** — one of `human | json | agent | agent-stream`.
99
+ 3. **Agent-environment auto-detect** — if `CLAUDECODE`, `CURSOR`, `CI`, or
100
+ `MODA_AGENT` is set, default to the agent JSON envelope.
101
+ 4. **TTY auto-detect** — if stdout is not a TTY (piped or redirected), default
102
+ to the agent JSON envelope; an interactive terminal defaults to human prose.
103
+
104
+ Running `moda` from inside Claude Code / Cursor, in CI, or through a pipe
105
+ already yields agent JSON — no flag required.
106
+
107
+ ### Exit codes
108
+
109
+ Branch on the exit code; it is stable and cheaper than parsing the payload:
110
+
111
+ | Exit | Meaning | How to treat it |
112
+ |---|---|---|
113
+ | `0` | ok — answer is grounded in Moda Cloud intelligence | Trust it. |
114
+ | `3` | degraded — Moda Cloud was unavailable; answer synthesized from local Data API evidence | Answer is present but lower-trust; verify before acting. |
115
+ | `1` | error — bad input, unknown command, or unexpected failure | Read `errors[]` in the envelope. |
116
+
117
+ ### The `moda.agent.v1` envelope
118
+
119
+ Fields an agent should read:
120
+
121
+ - `status` — `ok` | `degraded` | `error`.
122
+ - `summary.text` + `summary.confidence` — the answer and how much to trust it
123
+ (`high | medium | low | unknown`).
124
+ - `evidence_refs[]` — each `{ id, kind, label, path }`. When `path` looks like
125
+ `conversation/<id>`, open it with `moda context <id>`.
126
+ - `next_commands[]` — suggested follow-up commands (each carries `mutability`
127
+ and `requires_approval`).
128
+ - `warnings[]`, `errors[]`.
129
+ - `data` — the raw command payload. For `ask`, `data.source` is `cloud` or
130
+ `local_fallback` and `data.degraded` is a boolean — the canonical in-payload
131
+ way to detect degradation (in addition to exit code 3).
132
+
133
+ ### Detecting a degraded answer
134
+
135
+ Treat an answer as degraded if **any** of these hold:
136
+
137
+ - exit code is `3`, **or**
138
+ - `data.degraded === true` (for `ask`; equivalently `data.source === "local_fallback"`), **or**
139
+ - `status === "degraded"`.
140
+
141
+ ### Schema introspection
142
+
143
+ ```bash
144
+ moda --json-schemas # print the JSON Schemas for the agent output contract
145
+ ```
146
+
147
+ ### Recommended agent usage
148
+
149
+ ```bash
150
+ answer=$(moda ask "what should I fix first?")
151
+ case $? in
152
+ 0) printf 'grounded answer:\n%s\n' "$answer" ;;
153
+ 3) printf 'DEGRADED (local fallback — verify before acting):\n%s\n' "$answer" ;;
154
+ *) echo "moda ask failed:"; echo "$answer" | jq -r '.errors[].message'; exit 1 ;;
155
+ esac
156
+
157
+ # Pull the trusted parts straight out of the envelope
158
+ echo "$answer" | jq -r '.summary.text'
159
+ echo "$answer" | jq -r '.evidence_refs[] | "\(.kind)\t\(.label)\t\(.path // "")"'
160
+ ```
161
+
81
162
  ## Procedure
82
163
 
83
164
  Two entry points depending on the question:
@@ -146,6 +227,47 @@ moda overview --days-back=30
146
227
  Returns total conversations, trend, frustration rate, tool failure summary,
147
228
  top clusters, recent activity.
148
229
 
230
+ ### 2b. Ask & investigate (production intelligence)
231
+
232
+ `moda ask` answers a natural-language production/harness question; `moda
233
+ investigate` returns a ranked, evidence-backed list of the issues worth fixing
234
+ first. Both default to the last 7 days (`--days-back=N`, 1–90).
235
+
236
+ ```bash
237
+ moda ask "what should I fix first?" # grounded answer + evidence
238
+ moda ask "why are refunds failing?" --days-back=30
239
+ moda investigate # ranked findings, last 7d
240
+ moda investigate --days-back=14
241
+ moda overview # health briefing (see §2)
242
+ ```
243
+
244
+ In an agent environment these emit the `moda.agent.v1` envelope (see
245
+ [Agent protocol](#agent-protocol)). For `ask`, branch on the exit code (`0`
246
+ grounded, `3` degraded, `1` error) and read `data.source` /
247
+ `data.degraded` to know whether the answer came from Moda Cloud or a local
248
+ Data API fallback. Abbreviated envelope:
249
+
250
+ ```json
251
+ {
252
+ "schema_version": "moda.agent.v1",
253
+ "command": "ask",
254
+ "status": "degraded",
255
+ "summary": { "text": "Strongest signal is: lookupCustomer is failing in production.", "confidence": "medium" },
256
+ "evidence_refs": [
257
+ { "id": "tool:lookupCustomer", "kind": "tool_failure", "label": "lookupCustomer: 12 failure(s)", "path": null }
258
+ ],
259
+ "next_commands": [
260
+ { "command": "moda tool-failure-detail lookupCustomer --include-window", "purpose": "Inspect tool failure examples and conversation anchors.", "mutability": "read", "requires_approval": false }
261
+ ],
262
+ "warnings": ["Cloud ask endpoint unavailable; synthesized answer from local Data API evidence."],
263
+ "errors": [],
264
+ "data": { "source": "local_fallback", "degraded": true, "question": "what should I fix first?", "answer": "…", "confidence": "medium" }
265
+ }
266
+ ```
267
+
268
+ `moda investigate` also accepts scoping flags (`--tool`, `--run`,
269
+ `--conversation`, `--loop`) to focus the ranking on one signal.
270
+
149
271
  ### 3. Browse topic clusters
150
272
 
151
273
  ```bash
@@ -178,6 +300,27 @@ moda context <conversation_id> --msg-index=5 # center on message 5
178
300
  moda context <conversation_id> --window=3 # 3 messages each side (max 5)
179
301
  ```
180
302
 
303
+ `moda context` is turn-windowed (≤5 turns, deduped, no tool spans). For the raw,
304
+ complete picture of what was captured — every span, the parent/child hierarchy,
305
+ tool calls, prompt/response bodies, and `gen_ai.*` attributes — use `moda audit`.
306
+
307
+ ### 4b. Audit raw spans/trace (completeness, orphans, duplicates)
308
+
309
+ ```bash
310
+ moda audit <conversation_id|trace_id> # auto-detects trace vs conversation
311
+ moda audit <trace_id> --kind=trace # force trace lookup
312
+ moda audit <conversation_id> --include-raw # attach verbatim raw_event bodies
313
+ ```
314
+
315
+ Returns the raw, NON-deduped span set from the Layer-0 store (`events_raw`), so
316
+ nothing is collapsed. Use it to verify capture completeness: the `summary` gives
317
+ `total_spans`, `by_type` (llm/tool/agent/…), plus `orphan_count` (spans whose
318
+ parent is missing) and `duplicate_count` (double-instrumentation — the same
319
+ logical call emitted as >1 span). Each span carries `span_id`, `parent_span_id`,
320
+ `trace_id`, `type` (tool spans included), timing, `prompt`, `response`, and the
321
+ full semconv `attributes`. This is the honest substrate for auditing whether
322
+ Moda captured every LLM/tool call for a conversation.
323
+
181
324
  ### 5. Frustration analysis
182
325
 
183
326
  ```bash
@@ -226,42 +369,7 @@ Pass `--include-window` to attach a `window` field per row with the message
226
369
  slice centered on the anchor. `--window=N` (1–5, default `1`) is the
227
370
  half-width; `--window=1` ⇒ 3 messages total (anchor ± 1).
228
371
 
229
- ### 7. Workflow runs and outcomes
230
-
231
- Every triggered run of an agent (cron invocation, scheduled task, or
232
- interactive session) is classified `succeeded | partial | failed | loop |
233
- abandoned`. Use this layer to answer "did the job actually do its work?",
234
- which is structurally different from "did it error?" — most jobs that
235
- partially completed never threw.
236
-
237
- ```bash
238
- moda runs # last 7d, all outcomes
239
- moda runs --time-range=24h --outcome=partial # which runs only half-finished
240
- moda runs --outcome=loop # runs caught in active loops
241
- moda runs --class-id=cron:helpscout-triage-hourly --limit=20
242
- moda run <run_id> # one execution + its side-effect receipts
243
- ```
244
-
245
- `moda run <run_id>` returns the `agent_jobs` row plus every
246
- `side_effect_receipts` entry for the run — i.e. every tool call the agent
247
- made (read / write / send / spawn / compute), with success status and
248
- target system. This is the agent-as-employee log; use it instead of (or
249
- alongside) `moda context` when debugging "what did the agent actually
250
- DO".
251
-
252
- ### 8. Recurring workflows / cron status board
253
-
254
- ```bash
255
- moda workflows # health per recurring class
256
- moda workflows --time-range=7d # filter to last 7d of activity
257
- ```
258
-
259
- Each row is one job class (a cron slug, idea-widget id, or sourceType)
260
- with 30d rolling success / partial / failed / loop counts and a
261
- `health_score` (0–1). Sort by health to find degraded customer-facing
262
- crons that aren't crashing but aren't completing either.
263
-
264
- ### 9. Active loop investigation
372
+ ### 7. Active loop investigation
265
373
 
266
374
  ```bash
267
375
  moda loops # everything currently stuck
@@ -275,7 +383,7 @@ agent re-ran the same action 378 times. Use the `loop_signature_human`
275
383
  field to recognize cross-conversation patterns (one tool definition bug
276
384
  will produce identical signatures across many users).
277
385
 
278
- ### 10. Prompt management
386
+ ### 8. Prompt management
279
387
 
280
388
  Use these only when working on the code-first prompt registry.
281
389
 
@@ -362,19 +470,6 @@ moda clusters --parent-id=<node_id>
362
470
  moda cluster-conversations <node_id>
363
471
  ```
364
472
 
365
- ### Find degraded crons before customers do
366
-
367
- ```bash
368
- # Health-sorted workflow board
369
- moda workflows | jq '.workflows | sort_by(.health) | .[] | select(.runs >= 4 and .health < 0.85)'
370
-
371
- # What loops are eating tokens right now?
372
- moda loops --limit=20 | jq '.loops[] | {sig: .loop_signature_human, visits: .cycle_visits, conv: .conversation_id}'
373
-
374
- # Drill into a partial run
375
- moda runs --outcome=partial --limit=5 | jq -r '.runs[].job_id' | head -1 | xargs -I{} moda run {}
376
- ```
377
-
378
473
  ### One-liner chains with jq
379
474
 
380
475
  ```bash
@@ -393,6 +488,9 @@ moda tool-failures | jq '.tools[] | {tool: .tool_name, failures: .failure_count}
393
488
  ## Validation — how to know it worked
394
489
 
395
490
  - Every successful command exits 0 and prints valid JSON to stdout.
491
+ - `moda ask` / `moda investigate` may exit `3` (degraded): a real,
492
+ lower-trust answer synthesized from local evidence — not a failure. Treat
493
+ exit `1` as the only hard error (see [Agent protocol](#agent-protocol)).
396
494
  - Errors print to stderr with non-zero exit. Check stderr before claiming
397
495
  success.
398
496
  - If the JSON has `"conversations": []` or `"frustrations": []`, the call
@@ -441,6 +539,8 @@ npx: `npx -p @moda-ai/cli moda <command>`.
441
539
  | `moda init` | Browser auth + provision API key + install agent rules |
442
540
  | `moda search "<query>"` | **Message-grain semantic/keyword/hybrid search (incl. tool calls); primary content-discovery path** |
443
541
  | `moda overview` | Dashboard KPIs, top clusters, recent activity |
542
+ | `moda ask "<question>"` | Natural-language production/harness answer with evidence (exit `3` = degraded local fallback) |
543
+ | `moda investigate` | Rank production issues with evidence + next commands |
444
544
  | `moda clusters` | Browse topic cluster hierarchy |
445
545
  | `moda cluster-conversations <node_id>` | Conversations in a cluster |
446
546
  | `moda conversations` | List/filter conversations by structured fields |
@@ -448,9 +548,6 @@ npx: `npx -p @moda-ai/cli moda <command>`.
448
548
  | `moda frustrations` | User frustration detections with evidence |
449
549
  | `moda tool-failures` | Tool failure overview |
450
550
  | `moda tool-failure-detail <tool_name>` | Per-tool failure breakdown + examples |
451
- | `moda workflows` | Recurring workflow catalog with 30d health per class |
452
- | `moda runs` | Recent workflow executions with classified outcomes |
453
- | `moda run <run_id>` | One execution + its side-effect receipts |
454
551
  | `moda loops` | Currently-active loop detections |
455
552
  | `moda loop <detection_id>` | One loop signature with blast radius |
456
553
  | `moda prompts status` | Read-only local prompt status |
@@ -462,8 +559,7 @@ npx: `npx -p @moda-ai/cli moda <command>`.
462
559
  `--user-id`, `--time-range=<range>`, `--limit=N` (1–100, default 20).
463
560
 
464
561
  Common flags across most commands: `--days-back=N` (1–90), `--limit=N`,
465
- `--offset=N`, `--time-range=<range>`. Runs commands also accept
466
- `--outcome=<outcome>`, `--class-id=<workflow_class_id>`, and `--user-id=<id>`.
562
+ `--offset=N`, `--time-range=<range>`.
467
563
  `frustrations` and `tool-failure-detail` additionally accept
468
564
  `--include-window` (attach a `window` slice per row) and `--window=N` (1–5,
469
565
  default `1`; half-width, so `--window=1` ⇒ 3 messages).