@eigenpal/cli 0.13.2 → 0.14.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.
@@ -14,6 +14,48 @@ metadata:
14
14
  Use long command names in generated commands and documentation. Short aliases may
15
15
  appear in user examples, but do not prefer them.
16
16
 
17
+ ## Non-interactive / agent terminals (read first)
18
+
19
+ **Never run a CLI command that opens a confirmation or picker without checking
20
+ `--help` first.** Agent terminals, CI, and piped shells are usually non-TTY — an
21
+ interactive prompt will hang indefinitely.
22
+
23
+ Rules:
24
+
25
+ - **`--yes`** — skip confirmations and pickers when a deterministic default exists,
26
+ or acknowledge a destructive action in scripts. Required for most cancel/delete/replace
27
+ flows in non-TTY.
28
+ - **`--force`** — only where documented: overwrite user-edited skill files on reinstall
29
+ (`skill install --force`). Do not use as a generic “yes to everything”.
30
+ - **Provide explicit inputs** instead of prompts: `--file`, `--template`, `--tools`,
31
+ `--stdin` / `--value-file`, `--base-url`, profile name for `auth use <name>`,
32
+ `EIGENPAL_API_KEY` instead of `auth login` in CI.
33
+
34
+ Commands that can prompt or block (always inspect `--help` for the exact flag):
35
+
36
+ | Area | Command | Non-interactive bypass |
37
+ | --- | --- | --- |
38
+ | Init | `init [name]` | `--template <name>` and/or `--yes` (default template: blank) |
39
+ | Auth | `auth login` | Do not run non-interactively; set `EIGENPAL_API_KEY` and optionally `EIGENPAL_BASE_URL` |
40
+ | Auth | `auth use` | positional profile name |
41
+ | Skill | `skill install` | `--tools claude,cursor` and/or `--yes`; `--force` to overwrite edits |
42
+ | Skill | `skill uninstall` | tool ids, `--all`, or `--target` + `--yes` |
43
+ | Workflow | `workflow push` | `--file workflow.yaml` (TTY-only path picker otherwise) |
44
+ | Workflow | `workflow dataset push --mode replace` | `--yes` |
45
+ | Workflow | `workflow dataset example delete` | `--yes` |
46
+ | Workflow | `workflow experiment cancel` | `--yes` |
47
+ | Workflow | `workflow templates delete` | `--yes` |
48
+ | Agents | `agents dataset push --mode replace` | `--yes` |
49
+ | Agents | `agents experiment cancel` | `--yes` |
50
+ | Agents | `agents session stop` | `--yes` |
51
+ | Agents | `agents secrets set` | `--stdin` or `--value-file` |
52
+ | Runs | `runs cancel` | `--yes` |
53
+ | Runs | `runs reviews clear` | `--yes` |
54
+ | Runs | `runs expected delete` | `--yes` |
55
+
56
+ If a command fails with “requires --yes when run non-interactively”, re-run with
57
+ `--yes` only when the action is intentional — never to blindly retry.
58
+
17
59
  ## Choose A Command
18
60
 
19
61
  - `eigenpal workflow` manages YAML workflow definitions, datasets, evaluators,
@@ -30,15 +72,28 @@ appear in user examples, but do not prefer them.
30
72
  - `eigenpal runs` inspects, watches, compares, reruns, cancels, and downloads
31
73
  artifacts for workflow or agent runs.
32
74
 
75
+ ## Authenticate And Select A Workspace
76
+
77
+ ```bash
78
+ eigenpal auth login
79
+ eigenpal status --json
80
+ eigenpal auth list
81
+ eigenpal auth use <profile>
82
+ ```
83
+
84
+ For CI, set `EIGENPAL_API_KEY` and optionally `EIGENPAL_BASE_URL` instead of
85
+ running the interactive login.
86
+
33
87
  Use stable ids in scripts:
34
88
 
35
89
  - Workflow id: `wf_...`
36
90
  - Template id: `tmpl_...`
37
91
  - Template revision id: `tmpr_...`
38
- - Workflow execution id: `evx_...`
92
+ - Workflow execution id: `exec_...`
39
93
  - Agent execution id: `aex_...`
40
94
  - Experiment batch id: `evb_...`
41
- - Dataset example id: `ex_...`
95
+ - Workflow dataset example id: `evx_...`
96
+ - Agent dataset example id: `aeg_...`
42
97
 
43
98
  ## Workflow Loop
44
99
 
@@ -48,9 +103,11 @@ eigenpal init workflow invoices --template pdf-extraction
48
103
  cd invoices
49
104
  $EDITOR workflow.yaml
50
105
 
51
- # Inspect step schemas before editing config.
106
+ # Inspect step and evaluator schemas before editing config.
52
107
  eigenpal workflow step-type list --search extract
53
- eigenpal workflow step-type get ai.extract --json | jq '.configSchema.properties'
108
+ eigenpal workflow step-type get ai.extract | jq '.configSchema.properties'
109
+ eigenpal workflow evaluator-type list
110
+ eigenpal workflow evaluator-type get exact-diff | jq '.configSchema'
54
111
 
55
112
  # Validate and push.
56
113
  eigenpal workflow validate ./workflow.yaml
@@ -61,7 +118,10 @@ eigenpal workflow validate ./workflow.yaml
61
118
  # workflow project unless you pass --allow-external-templates.
62
119
  eigenpal workflow validate ./workflow.yaml --online
63
120
  eigenpal models list --json
64
- eigenpal workflow push --file workflow.yaml
121
+ # First push creates a new wf_…; capture the id from --json output.
122
+ eigenpal workflow push --file workflow.yaml --json | jq '.id'
123
+ # Later pushes must pass --workflow-id to update in place (omit it only on first create).
124
+ eigenpal workflow push --file workflow.yaml --workflow-id wf_…
65
125
 
66
126
  # Run with ad-hoc input and read the output (use this for API-triggered workflows).
67
127
  # `--wait --json` polls to completion and prints the run with top-level `output`.
@@ -125,6 +185,8 @@ Key rules:
125
185
  # Clone source and create a branch.
126
186
  eigenpal agents clone --out ./source
127
187
  cd ./source
188
+ # Use `agents save` to validate, commit, and push source. For other Git ops, use
189
+ # `eigenpal git -- <git-args>` (not bare `git` — the passthrough wires credentials).
128
190
  eigenpal git -- switch -c <short-task-branch>
129
191
 
130
192
  # Inspect source state.
@@ -175,15 +237,34 @@ DOCX/XLSX templates for **runtime agents** live in Git source, not in the
175
237
  workspace Templates table. YAML workflows use workspace templates via
176
238
  `transform.template` and `tmpl_...` instead — see `reference/step-types.md`.
177
239
 
178
- Workspace DOCX/XLSX templates for YAML workflows:
240
+ Workspace DOCX/XLSX templates for YAML workflows (`tmpl_…` / `tmpr_…`):
179
241
 
180
242
  ```bash
243
+ # Upload a new template (returns tmpl_… + tmpr_… with --json).
181
244
  eigenpal workflow templates upload ./templates/roster.xlsx --json
245
+
246
+ # List and inspect server templates.
247
+ eigenpal workflow templates list --json
248
+ eigenpal workflow templates get tmpl_…
249
+ eigenpal workflow templates get tmpl_… --json | jq '.currentRevision.id'
250
+
251
+ # Download current bytes (--out required).
252
+ eigenpal workflow templates download tmpl_… --out ./templates/roster.xlsx
253
+
254
+ # Replace with a new local file (creates a new tmpr_… revision).
255
+ eigenpal workflow templates replace tmpl_… ./templates/roster-v2.xlsx --json
256
+
257
+ # Delete the tmpl_… pointer (--yes required outside a TTY).
258
+ eigenpal workflow templates delete tmpl_… --yes
259
+
260
+ # Smoke-fill locally (no server) or from a tmpl_… id (downloads bytes, fills locally).
182
261
  eigenpal workflow templates smoke ./templates/roster.xlsx --data ./fixture.json --out ./filled.xlsx
262
+ eigenpal workflow templates smoke tmpl_… --data ./fixture.json --out ./filled.xlsx
263
+
183
264
  # YAML may keep a source path; push uploads and sends tmpl_ + tmpr_ ids.
184
265
  # template: ./templates/roster.xlsx
185
266
  eigenpal workflow validate ./workflow.yaml --online
186
- eigenpal workflow push --file workflow.yaml
267
+ eigenpal workflow push --file workflow.yaml --workflow-id wf_…
187
268
  ```
188
269
 
189
270
  **Local template** (one agent):
@@ -269,10 +350,10 @@ Agent examples are runtime data, not Git source. `agents save` does not persist
269
350
  them.
270
351
 
271
352
  ```bash
272
- eigenpal agents dataset validate /workspace/evals --agent-dir agents/<slug>
273
- eigenpal agents dataset push agents.<slug> --file /workspace/evals
353
+ eigenpal agents dataset validate ./dataset --agent-dir agents/<slug>
354
+ eigenpal agents dataset push agents.<slug> --file ./dataset
274
355
  eigenpal agents dataset list agents.<slug>
275
- eigenpal agents dataset pull agents.<slug> --out ./dataset
356
+ eigenpal agents dataset pull agents.<slug> --out ./dataset.zip
276
357
  ```
277
358
 
278
359
  Expected outputs should include stable fields only. Omit timestamps, random IDs,
@@ -301,16 +382,18 @@ Use `--version original` only to reproduce the previous resolved version.
301
382
  ```bash
302
383
  eigenpal runs reviews update <agent-execution-id> \
303
384
  --status open \
304
- --verdict needs_changes \
385
+ --verdict incorrect \
305
386
  --note "Expected the filing date to be extracted."
306
387
  eigenpal runs reviews close <agent-execution-id> \
307
388
  --note "Fixed and verified."
308
389
  eigenpal runs reviews clear <agent-execution-id> --yes
309
390
 
310
- eigenpal runs reviews expected list <agent-execution-id>
311
- eigenpal runs reviews expected upload <agent-execution-id> expected.json --name expected.json
312
- eigenpal runs reviews expected copy-output <agent-execution-id> result.json --name expected.json
313
- eigenpal runs reviews expected pull <agent-execution-id> --out expected/
391
+ eigenpal runs expected list <agent-execution-id>
392
+ eigenpal runs expected upload <agent-execution-id> expected.json --name expected.json
393
+ eigenpal runs expected copy-output <agent-execution-id> result.json --name expected.json
394
+ eigenpal runs expected pull <agent-execution-id> --out expected/
395
+ eigenpal runs expected rename <agent-execution-id> old.json new.json
396
+ eigenpal runs expected delete <agent-execution-id> old.json --yes
314
397
  ```
315
398
 
316
399
  ## Output
@@ -336,12 +419,25 @@ Status messages go to stderr. Pipeable data goes to stdout.
336
419
 
337
420
  Use generated references for exact flags and defaults:
338
421
 
422
+ CLI command references:
423
+
339
424
  - [`reference/cli/workflow.md`](reference/cli/workflow.md)
340
425
  - [`reference/cli/agents.md`](reference/cli/agents.md)
341
426
  - [`reference/cli/runs.md`](reference/cli/runs.md)
427
+ - [`reference/cli/run.md`](reference/cli/run.md)
428
+ - [`reference/cli/rerun.md`](reference/cli/rerun.md)
342
429
  - [`reference/cli/auth.md`](reference/cli/auth.md)
430
+ - [`reference/cli/init.md`](reference/cli/init.md)
431
+ - [`reference/cli/models.md`](reference/cli/models.md)
432
+ - [`reference/cli/skill.md`](reference/cli/skill.md)
433
+ - [`reference/cli/status.md`](reference/cli/status.md)
434
+ - [`reference/cli/surface.md`](reference/cli/surface.md)
435
+
436
+ Workflow and eval references:
437
+
343
438
  - [`reference/workflow-yaml.md`](reference/workflow-yaml.md)
344
439
  - [`reference/step-types.md`](reference/step-types.md)
440
+ - [`reference/step-exec.md`](reference/step-exec.md)
345
441
  - [`reference/dataset-format.md`](reference/dataset-format.md)
346
442
  - [`reference/evaluators.md`](reference/evaluators.md)
347
443
  - [`reference/debugging.md`](reference/debugging.md)
@@ -1,5 +1,7 @@
1
+ passThreshold: 1.0
1
2
  evaluators:
2
3
  - name: exact-match
3
4
  type: exact-diff
4
5
  config:
5
- passThreshold: 1.0
6
+ rules:
7
+ $: {}
@@ -519,12 +519,12 @@ Encrypt and set a secret value in secrets.enc.yaml.
519
519
 
520
520
  ### Options
521
521
 
522
- | Flag | Required | Default | Description |
523
- | ---------------------- | -------- | ------- | --------------------------------- |
524
- | `--dir <dir>` | no | | Directory to inspect |
525
- | `--stdin` | no | | Read the secret value from stdin |
526
- | `--value-file <path>` | no | | Read the secret value from a file |
527
- | `--description <text>` | no | | Secret description |
522
+ | Flag | Required | Default | Description |
523
+ | ---------------------- | -------- | ------- | ------------------------------------------------------------------- |
524
+ | `--dir <dir>` | no | | Directory to inspect |
525
+ | `--stdin` | no | | Read the secret value from stdin (required in CI / agent terminals) |
526
+ | `--value-file <path>` | no | | Read the secret value from a file |
527
+ | `--description <text>` | no | | Secret description |
528
528
 
529
529
  ### `eigenpal agents secrets|secret unset [options] <name>`
530
530
 
@@ -607,13 +607,13 @@ Upload dataset examples from a local dataset directory or zip archive.
607
607
 
608
608
  ### Options
609
609
 
610
- | Flag | Required | Default | Description |
611
- | -------------------------- | -------- | ---------- | ---------------------------------------------------- |
612
- | `--base-url <url>` | no | | Server base URL |
613
- | `--json` | no | | Output the raw server response as JSON |
614
- | `--file <path>` | yes | | Dataset directory or .zip archive |
615
- | `--mode <append\|replace>` | no | `"append"` | Upload mode |
616
- | `--yes` | no | | Confirm replace mode in non-interactive environments |
610
+ | Flag | Required | Default | Description |
611
+ | -------------------------- | -------- | ---------- | ---------------------------------------------------------------------------------- |
612
+ | `--base-url <url>` | no | | Server base URL |
613
+ | `--json` | no | | Output the raw server response as JSON |
614
+ | `--file <path>` | yes | | Dataset directory or .zip archive |
615
+ | `--mode <append\|replace>` | no | `"append"` | Upload mode |
616
+ | `--yes` | no | | Skip typed-slug confirmation for --mode replace (required in CI / agent terminals) |
617
617
 
618
618
  ### `eigenpal agents dataset pull [options] <agent-id-or-slug>`
619
619
 
@@ -763,11 +763,11 @@ Cancel every active execution in an experiment.
763
763
 
764
764
  ### Options
765
765
 
766
- | Flag | Required | Default | Description |
767
- | ------------------ | -------- | ------- | ---------------------------------------- |
768
- | `--base-url <url>` | no | | Server base URL |
769
- | `--json` | no | | Output the raw server response as JSON |
770
- | `--yes` | no | | Required in non-interactive environments |
766
+ | Flag | Required | Default | Description |
767
+ | ------------------ | -------- | ------- | ------------------------------------------------------------------ |
768
+ | `--base-url <url>` | no | | Server base URL |
769
+ | `--json` | no | | Output the raw server response as JSON |
770
+ | `--yes` | no | | Skip confirmation (required in CI / agent terminals without a TTY) |
771
771
 
772
772
  ### `eigenpal agents session list|ls [options] <agent-id-or-slug>`
773
773
 
@@ -854,11 +854,11 @@ Stop a builder session.
854
854
 
855
855
  ### Options
856
856
 
857
- | Flag | Required | Default | Description |
858
- | ------------------ | -------- | ------- | ---------------------------------------- |
859
- | `--base-url <url>` | no | | Server base URL |
860
- | `--json` | no | | Output the raw server response as JSON |
861
- | `--yes` | no | | Required in non-interactive environments |
857
+ | Flag | Required | Default | Description |
858
+ | ------------------ | -------- | ------- | ------------------------------------------------------------------ |
859
+ | `--base-url <url>` | no | | Server base URL |
860
+ | `--json` | no | | Output the raw server response as JSON |
861
+ | `--yes` | no | | Skip confirmation (required in CI / agent terminals without a TTY) |
862
862
 
863
863
  ### `eigenpal agents env pull [options] [target]`
864
864
 
@@ -270,11 +270,11 @@ Cancel a run.
270
270
 
271
271
  ### Options
272
272
 
273
- | Flag | Required | Default | Description |
274
- | ------------------ | -------- | ------- | ---------------------------------------- |
275
- | `--base-url <url>` | no | | Server base URL |
276
- | `--json` | no | | Output the raw server response as JSON |
277
- | `--yes` | no | | Required in non-interactive environments |
273
+ | Flag | Required | Default | Description |
274
+ | ------------------ | -------- | ------- | ------------------------------------------------------------------ |
275
+ | `--base-url <url>` | no | | Server base URL |
276
+ | `--json` | no | | Output the raw server response as JSON |
277
+ | `--yes` | no | | Skip confirmation (required in CI / agent terminals without a TTY) |
278
278
 
279
279
  ### `eigenpal runs artifacts|artifact list|ls [options] <run-id>`
280
280
 
@@ -393,11 +393,11 @@ Delete review metadata, corrected JSON, and corrected files for a run.
393
393
 
394
394
  ### Options
395
395
 
396
- | Flag | Required | Default | Description |
397
- | ------------------ | -------- | ------- | ---------------------------------------- |
398
- | `--base-url <url>` | no | | Server base URL |
399
- | `--json` | no | | Output the raw server response as JSON |
400
- | `--yes` | no | | Required in non-interactive environments |
396
+ | Flag | Required | Default | Description |
397
+ | ------------------ | -------- | ------- | ----------------------------------------------------------------------------- |
398
+ | `--base-url <url>` | no | | Server base URL |
399
+ | `--json` | no | | Output the raw server response as JSON |
400
+ | `--yes` | no | | Skip typed-slug confirmation (required in CI / agent terminals without a TTY) |
401
401
 
402
402
  ### `eigenpal runs expected list|ls [options] <run-id>`
403
403
 
@@ -503,8 +503,8 @@ Delete an expected artifact.
503
503
 
504
504
  ### Options
505
505
 
506
- | Flag | Required | Default | Description |
507
- | ------------------ | -------- | ------- | ---------------------------------------- |
508
- | `--base-url <url>` | no | | Server base URL |
509
- | `--json` | no | | Output the raw server response as JSON |
510
- | `--yes` | no | | Required in non-interactive environments |
506
+ | Flag | Required | Default | Description |
507
+ | ------------------ | -------- | ------- | ----------------------------------------------------------------------------- |
508
+ | `--base-url <url>` | no | | Server base URL |
509
+ | `--json` | no | | Output the raw server response as JSON |
510
+ | `--yes` | no | | Skip typed-slug confirmation (required in CI / agent terminals without a TTY) |
@@ -16,9 +16,10 @@ eigenpal run workflows.<workflow-id> --example <example-name>
16
16
 
17
17
  `<workflow-id>` is either a `wf_…` id or a slug (the YAML's `name:`
18
18
  field). The workflow must be pushed (`eigenpal workflow push`) before
19
- `eigenpal run` works — only saved workflows execute. Streams a live
20
- step list as the run progresses. Bails to the prompt the moment a step
21
- transitions to `failed`.
19
+ `eigenpal run` works — only saved workflows execute. Without `--wait`,
20
+ the command enqueues the run and exits after printing the run id — it
21
+ does not stream step progress. Add `--wait` to poll until a terminal
22
+ status, or use `runs watch <exec-id>` for a live step-by-step view.
22
23
 
23
24
  If the workflow is server-stable and the failure is on a real
24
25
  historical execution, skip the rerun and pull the recorded execution
@@ -56,19 +56,75 @@ finance) who never see the YAML or the judge prompt. Write it for them:
56
56
 
57
57
  ### `exact-diff` — JSON deep-diff against `expected.json`
58
58
 
59
+ Author comparison with a per-field `rules` map. Each key is a path into the
60
+ workflow output (the `.data` object). Use `$` to target the full output.
61
+ An ancestor rule covers its whole subtree; more specific descendants override
62
+ inherited settings. `{}` selects a path with inherited platform defaults.
63
+
59
64
  ```yaml
60
65
  - name: invoice-fields
61
66
  type: exact-diff
62
67
  config:
63
- passThreshold: 1.0 # 1.0 = byte-equal; lower = allow some slack
64
- paths: [invoiceNumber, totalAmount] # OPTIONAL — restrict to specific dotted paths
68
+ rules:
69
+ invoiceNumber: {}
70
+ total:
71
+ numericTolerance: 0.01
72
+ lineItems:
73
+ order: unordered
74
+ items: exactly
75
+ matchBy: sku
76
+ lineItems[]:
77
+ allowExtraFields: false
78
+ lineItems[].unitPrice:
79
+ numericTolerance: 0.01
65
80
  ```
66
81
 
67
- - `passThreshold` — number in `[0, 1]`. The diff produces a similarity
68
- score; `score >= passThreshold` ⇒ `passed: true`.
69
- - `paths` — array of dotted paths into the actual / expected outputs.
70
- When set, only those paths are diffed. When absent, the whole object
71
- is compared.
82
+ - `rules` — map of path → comparison settings. Only listed paths (and their
83
+ descendants, unless a descendant adds its own rule) are scored. Omit a path
84
+ to ignore it, or set `$: {}` to compare the entire output with defaults.
85
+ - `numericTolerance` — maximum absolute difference between expected and actual
86
+ numeric values at this path. Use `0` for exact integers; `0.01` is typical
87
+ for currency. When omitted, numeric comparison keeps the historical relative
88
+ epsilon.
89
+ - `order: unordered` — on an array path, match items by value instead of
90
+ position. Matches always use distinct item slots; whether missing expected
91
+ items fail is controlled independently by `items`.
92
+ - `matchBy` — optional identity for unordered arrays of objects. Requires
93
+ `order: unordered` on the same rule. Set on the array path (`lineItems`),
94
+ not the item path (`lineItems[]`). A relative field (`sku`) or unique
95
+ fields (`[country, sku]`) inside each item. Expected and actual objects are
96
+ paired by exact identity first, then the paired objects are compared with
97
+ inherited nested rules, so a matching SKU with the wrong amount reports
98
+ `lineItems[i].amount` instead of "no matching item". Applies only at the
99
+ array path where it is set and is not inherited. Identity type and value
100
+ must both match: numeric `10` differs from string `'10'`; no coercion or
101
+ numeric tolerance. With `items: at-least`, extra actual items may omit or
102
+ duplicate identities; with `at-most`, missing expected identities are
103
+ allowed but every actual item being checked needs a unique valid identity;
104
+ `exactly` validates both sides. Omit `matchBy` to keep structural matching,
105
+ which still allows legitimate duplicate objects.
106
+ - `items: at-least` — require every expected item while allowing additional
107
+ actual items (the default). Use `at-most` to permit a subset of expected
108
+ items but reject unexpected actual items, or `exactly` to permit neither.
109
+ - `order` is independent. Ordered arrays use positional prefix matching:
110
+ `at-least` requires expected to match actual from index 0 (trailing actual
111
+ extras only); `at-most` requires actual to match expected from index 0
112
+ (trailing expected extras only); `exactly` is equal-length positional.
113
+ `unordered` ignores positions. Set `order`, `items`, and `matchBy` on
114
+ `lineItems`, not `lineItems[]`.
115
+ - `allowExtraFields: false` — on an object path (often `someArray[]`), reject
116
+ object keys present in actual but absent from expected.
117
+
118
+ Path syntax: `lineItems` targets the array; `lineItems[]` targets each element;
119
+ `lineItems[].unitPrice` targets a field inside every element. Nested unordered
120
+ lists use paths such as `groups[].members` with `order: unordered` on that path.
121
+
122
+ Extract steps may attach `_grounding` metadata under output fields. Exact-diff
123
+ strips that metadata before comparison, so you do not need a rule to exclude it.
124
+
125
+ Configs authored before per-field rules used other top-level `exact-diff`
126
+ options. Those configs keep working unchanged. Do not mix legacy options with
127
+ `rules` in the same evaluator; validation rejects mixed configs.
72
128
 
73
129
  `exact-diff` branches on the example's expected shape: success-expected
74
130
  examples (`expected.json`) are diffed against the workflow's actual output, and
@@ -120,15 +176,16 @@ one of the keys in `labels`; the harness then looks up the score.
120
176
  config:
121
177
  passThreshold: 1.0
122
178
  function: |
123
- type WorkflowOutput = {
179
+ type WorkflowData = {
124
180
  totalAmount: number;
125
181
  };
182
+ type WorkflowOutput = { data: WorkflowData };
126
183
  type Expected = WorkflowOutput;
127
184
  type Actual = WorkflowOutput;
128
185
 
129
186
  function scoreScript(expected: WorkflowOutput, actual: WorkflowOutput): number {
130
- const a = actual.totalAmount ?? 0;
131
- const e = expected.totalAmount ?? 0;
187
+ const a = actual.data.totalAmount ?? 0;
188
+ const e = expected.data.totalAmount ?? 0;
132
189
  return Math.abs(a - e) <= 0.01 ? 1 : 0;
133
190
  }
134
191
  ```
@@ -142,6 +199,9 @@ enforced at parse time. The dashboard seeds new evaluators with a
142
199
  declaration, plus `Expected` / `Actual` aliases for compatibility. Plain
143
200
  `unknown` works too if you do not want a typed signature.
144
201
 
202
+ Both arguments are envelopes. Read workflow fields from `expected.data` and
203
+ `actual.data`; the actual envelope may also include `files`.
204
+
145
205
  The signature shape is **locked**:
146
206
 
147
207
  - Function name MUST be `scoreScript`.
@@ -263,10 +323,22 @@ _Generated from `EvalConfigYamlSchema` in `@eigenpal/types/src/eval/evaluator-co
263
323
 
264
324
  | Field | Type | Required | Default | Description |
265
325
  | --- | --- | --- | --- | --- |
266
- | `numericTolerance` | number | no | `0.000001` | Maximum absolute difference between numeric values that still counts as equal. Use 1e-6 for floats, 0 for exact integer match. |
267
- | `allowExtraFields` | boolean | no | `true` | When on, extra fields in the actual output do not fail the diff. Off = actual must match expected exactly. |
326
+ | `rules` | record<string, object> | no | | Per-path comparison rules. Keys select compared paths (`$` = full output). More-specific keys inherit then override parent rules. `{}` selects a path using inherited platform defaults. Do not mix with legacy `paths`, `unorderedPaths`, `numericTolerance`, or `allowExtraFields`. |
327
+ | `numericTolerance` | number | no | | Legacy global numeric tolerance (absolute). When omitted, comparison uses historical relative epsilon. Prefer `rules.<path>.numericTolerance`. Accepted only when `rules` is omitted. |
328
+ | `allowExtraFields` | boolean | no | | Legacy global extra-field/extra-item flag. Prefer `rules.<path>.allowExtraFields` and `rules.<path>.items`. `true` normalizes to object extras allowed and array `items: at-least`; `false` normalizes to object extras rejected and array `items: exactly`. Accepted only when `rules` is omitted. |
268
329
  | `passThreshold` | number | no | | Legacy per-evaluator pass threshold. Pass/fail now uses the single workflow-level pass threshold; this is read only to preserve the run gate of configs authored before the single-threshold model. |
269
- | `paths` | array<string> | no | | Dot-paths to scope the diff to a subset of the expected tree. Empty = diff entire expected. Syntax: `header.id`, `lineItems[].total`, `lineItems[0].sku`. Use this when expected has noisy sections you do not want to score. |
330
+ | `paths` | array<string> | no | | Legacy scoped paths. Prefer selecting those paths as `rules` keys. Syntax: `header.id`, `lineItems[].total`, `lineItems[0].sku`. |
331
+ | `unorderedPaths` | array<string> | no | | Legacy unordered array paths. Prefer `rules.<path>.order: unordered`. Name the array field itself (`subjects`, not `subjects[]`). |
332
+
333
+ #### Per-path `rules` fields
334
+
335
+ | Field | Type | Required | Default | Description |
336
+ | --- | --- | --- | --- | --- |
337
+ | `order` | `"ordered"` \| `"unordered"` | no | | Array matching mode at this path. Independent of `items`. Set on the array path (`lineItems`), not the item path (`lineItems[]`). `unordered` matches distinct items in any order. Arrays stay ordered when omitted. Unordered matching is limited to 1000 items, 1000000 candidate pairs, and 2000000 recursive operations per comparison. |
338
+ | `items` | `"at-least"` \| `"at-most"` \| `"exactly"` | no | | How expected and actual array items relate. Set on the array path (`lineItems`), not the item path (`lineItems[]`). Independent of `order`. Ordered arrays use positional prefix matching: `at-least` (default) requires expected to match actual from index 0 and allows trailing actual extras; `at-most` requires actual to match expected from index 0 and allows trailing expected extras; `exactly` is equal-cardinality positional comparison. Unordered arrays match distinct items in any order: `at-least` requires every expected item and allows extra actual items (including extras with missing or duplicate identities); `at-most` requires every actual item and allows missing expected items (every actual item being checked needs a valid unique identity); `exactly` is a bijection (both sides need valid unique identities). |
339
+ | `matchBy` | string \| array<string> | no | | Identity fields for unordered arrays of objects. Requires `order: unordered` on the same rule. Set on the array path (`lineItems`), not the item path (`lineItems[]`). A relative path (`sku`) or unique paths (`[country, sku]`) inside each item. Pairs expected and actual objects by exact identity, then diffs the paired objects with inherited nested rules. Identity type and value must both match: numeric `10` differs from string `'10'`; no coercion or numeric tolerance. Omitted = structural matching. Applies only at the array path where it is set and is not inherited by descendants. Nested object fields are allowed; array indexes, commas, and wildcards are not. Empty or comma-only values are rejected; composite paths must be unique. |
340
+ | `allowExtraFields` | boolean | no | | When false, extra actual keys at this object fail the diff. When omitted, extra keys are allowed. Applies only to objects. |
341
+ | `numericTolerance` | number | no | | Maximum absolute difference for numeric leaves at or under this path. Omitted = inherit, then historical relative epsilon. Explicit `0` is exact. |
270
342
 
271
343
 
272
344
  ### `llm-judge` — LLM-as-judge scoring
@@ -285,7 +357,7 @@ _Generated from `EvalConfigYamlSchema` in `@eigenpal/types/src/eval/evaluator-co
285
357
 
286
358
  | Field | Type | Required | Default | Description |
287
359
  | --- | --- | --- | --- | --- |
288
- | `function` | string | yes | | Full TypeScript source for optional `type` aliases plus `function scoreScript(expected, actual): number { ... }`. Receives `expected` (the example's expected output) and `actual` (the workflow's actual output), returns a number in [0, 1] (the `: number` annotation is required). The `: number` return-type annotation is required and enforced at parse time. Throws are caught and scored as 0. |
360
+ | `function` | string | yes | | Full TypeScript source for optional `type` aliases plus `function scoreScript(expected, actual): number { ... }`. Receives `expected` (the example's expected-output envelope (`{ data: ... }`)) and `actual` (the workflow result envelope (`{ data: ..., files?: ... }`)), returns a number in [0, 1] (the `: number` annotation is required). The `: number` return-type annotation is required and enforced at parse time. Throws are caught and scored as 0. |
289
361
  | `timeoutMs` | integer | no | `5000` | Maximum wall-clock time the script can run before it is killed and the run is marked failed. |
290
362
  | `memoryLimitMb` | integer | no | `10` | Maximum memory the sandbox may allocate. Increase if the script processes large arrays or strings. |
291
363
  | `passThreshold` | number | no | | Legacy per-evaluator pass threshold. Pass/fail now uses the single workflow-level pass threshold; this is read only to preserve the run gate of configs authored before the single-threshold model. |
@@ -431,7 +431,7 @@ Extract text from documents (PDF, DOCX, images) using native extraction, OCR, or
431
431
  | `imageQuality` | integer | no | `85` | JPEG quality for rendered PDF page images sent to VLM parsing. Higher values reduce compression artifacts at larger payload sizes. |
432
432
  | `prompt` | string | no | | Custom extraction prompt |
433
433
  | `languages` | array<string> | no | | OCR language hints |
434
- | `outputFormat` | `"plain"` \| `"markdown"` \| `"djot"` \| `"html"` | no | `"markdown"` | Format for extracted text. `markdown` (default) keeps structure and is best for LLM extraction; `plain` is unstyled text; `djot`/`html` preserve more layout. Only the native (Kreuzberg) parser respects this — OCR/VLM always emit markdown. |
434
+ | `outputFormat` | `"plain"` \| `"markdown"` \| `"djot"` \| `"html"` \| `"layout"` | no | `"markdown"` | Format for extracted text. `markdown` (default) keeps structure; `plain` is unstyled text; `djot`/`html` preserve more markup. `layout` is native-PDF spatial text with column gaps preserved as spaces. It requires `parseMode: native` or `parseMode: native-or-ocr` and a PDF — omitted parseMode and `nativeText: true` are not enough. Native mode is layout-only and fail-closed. `native-or-ocr` keeps layout text on accepted native pages and uses OCR markdown on suspect or empty pages, so the document can mix fixed-width layout and markdown. Extractor failure never falls back to another parser. Office, plaintext, and images fail. OCR/vision cannot request `layout`. |
435
435
  | `nativeText` | boolean | no | `false` | Extract native/embedded text from PDFs without OCR/VLM. Faster and uses no credits. Falls back to OCR/VLM if the PDF has no embedded text. |
436
436
  | `describeFigures` | boolean | no | | Opt-in (default off). After text extraction, detect which pages contain figures with an in-worker layout model, then caption those pages with a vision model and append `<figure>description</figure>` to their text — so image-only pages (property photos, signatures, charts) become findable by text-based steps like ai.split. Note: the layout scan runs over all pages, and the caption step and its vision calls are billed. Skipped for plaintext. |
437
437
  | `figureInstructions` | string | no | | Custom instruction for the figure-description pass, e.g. "Describe each figure; label a handwritten signature as `<figure>signature</figure>` and a stamp as `<figure>stamp</figure>`; for property photos note the room or exterior shown." Applied only when describeFigures runs. |
@@ -740,9 +740,9 @@ Embed OCR text layer into scanned PDFs/images to make them searchable
740
740
  | `wordCount` | number | yes | | Number of words embedded |
741
741
  | `text` | string | yes | | Extracted text from the document |
742
742
 
743
- #### `transform.xlsx-to-json` — XLSX to JSON
743
+ #### `transform.xlsx-to-json` — Spreadsheet to JSON
744
744
 
745
- Convert XLSX spreadsheet to JSON array of row objects for use in scripts or downstream steps
745
+ Convert an XLS or XLSX spreadsheet to a JSON array of row objects. Supports headerless files, named or positional columns, displayed text, and a single rectangular range.
746
746
 
747
747
  **Durable retry:** Transforms, including those that write files, are not durably retried.
748
748
 
@@ -751,16 +751,26 @@ Convert XLSX spreadsheet to JSON array of row objects for use in scripts or down
751
751
  | Field | Type | Required | Default | Description |
752
752
  | --- | --- | --- | --- | --- |
753
753
  | `input` | string | yes | | File input - template expression e.g. {{input.document}} resolving to a scoped $file artifact at runtime |
754
- | `sheet` | integer \| string | no | | Sheet to read: 0-based index or sheet name. Omit for first sheet. |
755
- | `outputCsv` | boolean | no | `false` | If true, also write CSV to storage and include fileId in output |
754
+ | `sheet` | integer \| string | no | | Sheet to read: 0-based index or exact sheet name. Omit for the first sheet. |
755
+ | `outputCsv` | boolean | no | `false` | If true, also write CSV to storage and include fileId. Zero-config uses the historical full-sheet SheetJS CSV. When columns, range, headerRow, valueMode, blankCells, or blankRows are set, CSV matches that projection. |
756
+ | `includeMetadata` | boolean | no | `false` | If true, include sheet metadata and diagnostics in the step output. Omit to keep output as rows (and fileId when outputCsv is true). Warnings are still logged when this is false. |
756
757
  | `outputFilename` | string | no | | Output CSV filename when outputCsv is true - supports LiquidJS e.g. {{filename}}.csv |
758
+ | `headerRow` | `false` \| integer | no | | Header row: a positive 1-based Excel row, or false to keep the first effective row as data. Omit to use the first effective row as the header. When range is set and this is omitted, the first range row is the header. |
759
+ | `columns` | array<object> | no | | Ordered output columns. Each item needs a key and exactly one source: index (0-based absolute column) or header (exact displayed header text). Named header sources require a header row. Omit to keep every column in the effective range. |
760
+ | `valueMode` | `"raw"` \| `"displayed"` | no | `"raw"` | raw (default) returns typed cached cell values. displayed returns formatted cell text (dates, leading zeros, punctuation, diacritics, embedded newlines). Formulas are never calculated. |
761
+ | `range` | string | no | | Optional rectangular A1 range without a sheet qualifier, e.g. A1:D20. Disjoint ranges are rejected. |
762
+ | `blankCells` | `"empty-string"` \| `"null"` \| `"omit"` | no | `"empty-string"` | How truly empty cells appear in each row object. Default empty-string. 0, false, and a formula that cached an empty string are not empty cells. |
763
+ | `blankRows` | `"skip"` \| `"keep"` | no | `"skip"` | skip (default) drops rows whose projected columns are all truly empty. keep retains them. Detection uses projected columns only. |
764
+ | `limits` | object | no | | Optional workload caps that can only lower the server defaults. Omitted fields use the server defaults. |
757
765
 
758
766
  **Output:** `object`
759
767
 
760
768
  | Field | Type | Required | Default | Description |
761
769
  | --- | --- | --- | --- | --- |
762
- | `rows` | array<record<string, unknown>> | yes | | Array of row objects (first row = headers as keys) |
770
+ | `rows` | array<record<string, unknown>> | yes | | Array of row objects (first row = headers as keys unless headerRow is false) |
763
771
  | `fileId` | string | no | | File ID of stored CSV when outputCsv is true |
772
+ | `sheet` | object | no | | Selected sheet metadata after projection. Present only when includeMetadata is true. |
773
+ | `diagnostics` | array<object> | no | | Non-fatal warnings collected while reading the sheet. Present only when includeMetadata is true. Warnings are still logged when metadata is omitted. |
764
774
 
765
775
  #### `transform.json-to-xlsx` — JSON to XLSX
766
776
 
@@ -899,7 +909,7 @@ Execute another workflow and return its output
899
909
 
900
910
  **Output:** `record<string, unknown>`
901
911
 
902
- > When wait is true, the invoked workflow's declared output fields are flattened to the top level alongside a `files` array — reference them as {{ steps.<invoke>.output.<field> }} (there is no `.data` or `.result` wrapper). When wait is false, execution metadata. Call get_workflow_output_schema to see the exact resolved fields.
912
+ > When wait is true, the invoked workflow's declared output fields are flattened to the top level alongside a `files` array — reference them as {{ steps.<invoke>.output.<field> }} (there is no `.data` or `.result` wrapper). When wait is false, execution metadata. CLI authors can run `eigenpal workflow schema <workflow-id>` to inspect resolved fields; Studio builder agents can call `get_workflow_output_schema`.
903
913
 
904
914
  #### `action.website-reader` — Website Reader
905
915
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eigenpal/cli",
3
- "version": "0.13.2",
3
+ "version": "0.14.1",
4
4
  "description": "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
5
5
  "keywords": [
6
6
  "eigenpal",