@eigenpal/cli 0.7.2 → 0.9.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.
Files changed (29) hide show
  1. package/CHANGELOG.md +101 -23
  2. package/README.md +8 -8
  3. package/dist/cli.js +9300 -9059
  4. package/dist/skill/SKILL.md +87 -415
  5. package/dist/skill/reference/cli/agents.md +26 -26
  6. package/dist/skill/reference/cli/rerun.md +1 -1
  7. package/dist/skill/reference/cli/run.md +1 -1
  8. package/dist/skill/reference/cli/runs.md +23 -2
  9. package/dist/skill/reference/cli/surface.md +1 -2
  10. package/dist/skill/reference/cli/workflow.md +10 -10
  11. package/dist/skill/reference/dataset-format.md +74 -241
  12. package/dist/skill/reference/debugging.md +5 -5
  13. package/dist/skill/reference/evaluators.md +10 -10
  14. package/dist/skill/reference/step-exec.md +2 -2
  15. package/dist/skill/reference/step-types.md +7 -7
  16. package/dist/skill/reference/workflow-yaml.md +4 -4
  17. package/dist/templates/blank/README.md +4 -3
  18. package/dist/templates/blank/evaluators.yaml +1 -1
  19. package/package.json +1 -1
  20. package/dist/skill/reference/cli/completion.md +0 -9
  21. package/dist/skill/reference/cli/git.md +0 -9
  22. /package/dist/templates/blank/dataset/examples/sample/{expected/output.json → expected.json} +0 -0
  23. /package/dist/templates/blank/dataset/examples/sample/{input/arguments.json → input.json} +0 -0
  24. /package/dist/templates/pdf-extraction/dataset/examples/sample-invoice/{expected/output.json → expected.json} +0 -0
  25. /package/dist/templates/pdf-extraction/dataset/examples/sample-invoice/{input/arguments.json → input.json} +0 -0
  26. /package/dist/templates/text-classification/dataset/examples/negative/{expected/output.json → expected.json} +0 -0
  27. /package/dist/templates/text-classification/dataset/examples/negative/{input/arguments.json → input.json} +0 -0
  28. /package/dist/templates/text-classification/dataset/examples/positive/{expected/output.json → expected.json} +0 -0
  29. /package/dist/templates/text-classification/dataset/examples/positive/{input/arguments.json → input.json} +0 -0
@@ -1,6 +1,6 @@
1
1
  # eigenpal agents
2
2
 
3
- Manage Eigenpal agents: Git source, datasets, runs, experiments, sessions, and releases.
3
+ Manage Eigenpal agents: Git source, datasets, experiments, sessions, and releases.
4
4
 
5
5
  ## Contents
6
6
 
@@ -157,20 +157,20 @@ agents
157
157
  | Command | Description |
158
158
  | --------------------------------------------------------------- | -------------------------------------------------------------------------- |
159
159
  | `eigenpal agents dataset list\|ls [options] <agent-id-or-slug>` | List dataset examples for an agent. |
160
- | `eigenpal agents dataset push [options] <agent-id-or-slug>` | Upload dataset examples from a local dataset directory. |
161
- | `eigenpal agents dataset pull [options] <agent-id-or-slug>` | Download an agent dataset directory. |
160
+ | `eigenpal agents dataset push [options] <agent-id-or-slug>` | Upload dataset examples from a local dataset directory or zip archive. |
161
+ | `eigenpal agents dataset pull [options] <agent-id-or-slug>` | Download an agent dataset as a .zip archive. |
162
162
  | `eigenpal agents dataset validate [options] [path]` | Validate a local dataset directory against the agent input/output schemas. |
163
163
 
164
164
  ### Experiment
165
165
 
166
- | Command | Description |
167
- | ----------------------------------------------------------------------------------- | ----------------------------------------------- |
168
- | `eigenpal agents experiment\|exp run [options] <agent-id-or-slug>` | Start an experiment over dataset examples. |
169
- | `eigenpal agents experiment\|exp status [options] <agent-id-or-slug> <batch-id>` | Get experiment status. |
170
- | `eigenpal agents experiment\|exp results [options] <agent-id-or-slug> [batch-id]` | Print experiment results as JSON or CSV. |
171
- | `eigenpal agents experiment\|exp list\|ls [options] <agent-id-or-slug>` | List experiments. |
172
- | `eigenpal agents experiment\|exp compare\|diff [options] <batch-id-a> <batch-id-b>` | Compare two experiment batches. |
173
- | `eigenpal agents experiment\|exp cancel [options] <agent-id-or-slug> <batch-id>` | Cancel every active execution in an experiment. |
166
+ | Command | Description |
167
+ | ----------------------------------------------------------------------------------- | ------------------------------------------------ |
168
+ | `eigenpal agents experiment\|exp run [options] <agent-id-or-slug>` | Start an experiment over dataset examples. |
169
+ | `eigenpal agents experiment\|exp status [options] <agent-id-or-slug> <batch-id>` | Get experiment status. |
170
+ | `eigenpal agents experiment\|exp results [options] <agent-id-or-slug> [batch-id]` | Print experiment results as JSON or CSV. |
171
+ | `eigenpal agents experiment\|exp list\|ls [options] <agent-id-or-slug>` | List experiments. |
172
+ | `eigenpal agents experiment\|exp compare\|diff [options] <batch-id-a> <batch-id-b>` | Diff eval scores between two experiment batches. |
173
+ | `eigenpal agents experiment\|exp cancel [options] <agent-id-or-slug> <batch-id>` | Cancel every active execution in an experiment. |
174
174
 
175
175
  ### Session
176
176
 
@@ -572,7 +572,7 @@ List dataset examples for an agent.
572
572
 
573
573
  ### `eigenpal agents dataset push [options] <agent-id-or-slug>`
574
574
 
575
- Upload dataset examples from a local dataset directory.
575
+ Upload dataset examples from a local dataset directory or zip archive.
576
576
 
577
577
  ### Arguments
578
578
 
@@ -586,13 +586,13 @@ Upload dataset examples from a local dataset directory.
586
586
  | -------------------------- | -------- | ---------- | ---------------------------------------------------- |
587
587
  | `--base-url <url>` | no | | Server base URL |
588
588
  | `--json` | no | | Output the raw server response as JSON |
589
- | `--file <path>` | yes | | Dataset directory |
589
+ | `--file <path>` | yes | | Dataset directory or .zip archive |
590
590
  | `--mode <append\|replace>` | no | `"append"` | Upload mode |
591
591
  | `--yes` | no | | Confirm replace mode in non-interactive environments |
592
592
 
593
593
  ### `eigenpal agents dataset pull [options] <agent-id-or-slug>`
594
594
 
595
- Download an agent dataset directory.
595
+ Download an agent dataset as a .zip archive.
596
596
 
597
597
  ### Arguments
598
598
 
@@ -602,10 +602,10 @@ Download an agent dataset directory.
602
602
 
603
603
  ### Options
604
604
 
605
- | Flag | Required | Default | Description |
606
- | ------------------ | -------- | ----------- | ---------------- |
607
- | `--base-url <url>` | no | | Server base URL |
608
- | `--out <dir>` | no | `"dataset"` | Output directory |
605
+ | Flag | Required | Default | Description |
606
+ | ------------------ | -------- | ----------- | --------------------------------------- |
607
+ | `--base-url <url>` | no | | Server base URL |
608
+ | `--out <path>` | no | `"dataset"` | Output .zip path (default: dataset.zip) |
609
609
 
610
610
  ### `eigenpal agents dataset validate [options] [path]`
611
611
 
@@ -663,7 +663,7 @@ Get experiment status.
663
663
  | `--json` | no | | Output the raw server response as JSON |
664
664
  | `--watch` | no | | Poll until complete |
665
665
  | `--interval <seconds>` | no | `2` | Polling interval in seconds |
666
- | `--max-wait <seconds>` | no | `1800` | Maximum wait before exiting 2 |
666
+ | `--max-wait <seconds>` | no | `1800` | Maximum wait before exit code 2 |
667
667
  | `--include <parts>` | no | | Reserved for future detailed parts |
668
668
 
669
669
  ### `eigenpal agents experiment|exp results [options] <agent-id-or-slug> [batch-id]`
@@ -707,7 +707,7 @@ List experiments.
707
707
 
708
708
  ### `eigenpal agents experiment|exp compare|diff [options] <batch-id-a> <batch-id-b>`
709
709
 
710
- Compare two experiment batches.
710
+ Diff eval scores between two experiment batches.
711
711
 
712
712
  ### Arguments
713
713
 
@@ -718,12 +718,12 @@ Compare two experiment batches.
718
718
 
719
719
  ### Options
720
720
 
721
- | Flag | Required | Default | Description |
722
- | ---------------------------- | -------- | ------- | ------------------------------------------------------------- |
723
- | `--base-url <url>` | no | | Server base URL |
724
- | `--json` | no | | Output the raw server response as JSON |
725
- | `--sort <mode>` | no | | Accepted for compatibility; sorting happens client-side later |
726
- | `--regression-threshold <n>` | no | | Accepted for compatibility |
721
+ | Flag | Required | Default | Description |
722
+ | ------------------------------------------------------ | -------- | ------------------ | ------------------------------------------------------ |
723
+ | `--base-url <url>` | no | | Server base URL |
724
+ | `--json` | no | | Output the raw server response as JSON |
725
+ | `--sort <abs-delta-desc\|delta-asc\|delta-desc\|name>` | no | `"abs-delta-desc"` | Row sort order (default: biggest movers first) |
726
+ | `--regression-threshold <n>` | no | `0.05` | Δ below this is flagged as a regression (default 0.05) |
727
727
 
728
728
  ### `eigenpal agents experiment|exp cancel [options] <agent-id-or-slug> <batch-id>`
729
729
 
@@ -17,4 +17,4 @@ Create a new run from a previous run's stored input snapshot.
17
17
  | `--version <version>` | no | `"latest"` | Version/source ref for the new run: latest, original, or an explicit ref |
18
18
  | `--wait` | no | | Poll until the rerun reaches a terminal status |
19
19
  | `--interval <seconds>` | no | `2` | Polling interval in seconds |
20
- | `--max-wait <seconds>` | no | `1800` | Maximum wait before exiting 2 |
20
+ | `--max-wait <seconds>` | no | `1800` | Maximum wait before exit code 2 |
@@ -20,4 +20,4 @@ Start a workflow or agent run, e.g. workflows.extract-invoice.
20
20
  | `--dir <dir>` | no | | Local eigenpal directory for workflow examples |
21
21
  | `--wait` | no | | Poll until the run reaches a terminal status |
22
22
  | `--interval <seconds>` | no | `2` | Polling interval in seconds |
23
- | `--max-wait <seconds>` | no | `1800` | Maximum wait before exiting 2 |
23
+ | `--max-wait <seconds>` | no | `1800` | Maximum wait before exit code 2 |
@@ -15,6 +15,7 @@ Inspect, watch, and manage workflow, agent, and eval runs.
15
15
  - [`eigenpal runs get [options] <run-id>`](#eigenpal-runs-get-options-run-id)
16
16
  - [`eigenpal runs compare|diff [options] <reference-run-id> <run-id>`](#eigenpal-runs-comparediff-options-reference-run-id-run-id)
17
17
  - [`eigenpal runs rerun [options] <run-id>`](#eigenpal-runs-rerun-options-run-id)
18
+ - [`eigenpal runs promote [options] <run-id>`](#eigenpal-runs-promote-options-run-id)
18
19
  - [`eigenpal runs trace [options] <run-id>`](#eigenpal-runs-trace-options-run-id)
19
20
  - [`eigenpal runs watch [options] <run-id>`](#eigenpal-runs-watch-options-run-id)
20
21
  - [`eigenpal runs cancel [options] <run-id>`](#eigenpal-runs-cancel-options-run-id)
@@ -38,6 +39,7 @@ runs
38
39
  ├── get <run-id>
39
40
  ├── compare|diff <reference-run-id> <run-id>
40
41
  ├── rerun <run-id>
42
+ ├── promote <run-id>
41
43
  ├── artifacts|artifact
42
44
  │ ├── list|ls <run-id>
43
45
  │ └── fetch <run-id>
@@ -67,6 +69,7 @@ runs
67
69
  | `eigenpal runs get [options] <run-id>` | Get one run. |
68
70
  | `eigenpal runs compare\|diff [options] <reference-run-id> <run-id>` | Compare one run against another run. PDF/DOCX text comparison uses pdftotext/python3 and reports byte fallbacks. |
69
71
  | `eigenpal runs rerun [options] <run-id>` | Create a new run from a previous run's stored input snapshot. |
72
+ | `eigenpal runs promote [options] <run-id>` | Promote a run into a dataset example on the run's automation. |
70
73
  | `eigenpal runs trace [options] <run-id>` | Print raw trace.jsonl for a run, or write it with --out. |
71
74
  | `eigenpal runs watch [options] <run-id>` | Watch a run until it reaches a terminal status. |
72
75
  | `eigenpal runs cancel [options] <run-id>` | Cancel a run. |
@@ -196,7 +199,25 @@ Create a new run from a previous run's stored input snapshot.
196
199
  | `--source-ref <ref>` | no | | Alias for --version |
197
200
  | `--wait` | no | | Poll until the rerun reaches a terminal status |
198
201
  | `--interval <seconds>` | no | `2` | Polling interval in seconds |
199
- | `--max-wait <seconds>` | no | `1800` | Maximum wait before exiting 2 |
202
+ | `--max-wait <seconds>` | no | `1800` | Maximum wait before exit code 2 |
203
+
204
+ ### `eigenpal runs promote [options] <run-id>`
205
+
206
+ Promote a run into a dataset example on the run's automation.
207
+
208
+ ### Arguments
209
+
210
+ | Name | Required | Variadic | Description |
211
+ | -------- | -------- | -------- | ----------- |
212
+ | `run-id` | yes | no | |
213
+
214
+ ### Options
215
+
216
+ | Flag | Required | Default | Description |
217
+ | ------------------ | -------- | ------- | -------------------------------------- |
218
+ | `--base-url <url>` | no | | Server base URL |
219
+ | `--json` | no | | Output the raw server response as JSON |
220
+ | `--name <name>` | no | | Name for the created example |
200
221
 
201
222
  ### `eigenpal runs trace [options] <run-id>`
202
223
 
@@ -232,7 +253,7 @@ Watch a run until it reaches a terminal status.
232
253
  | `--base-url <url>` | no | | Server base URL |
233
254
  | `--json` | no | | Output the raw server response as JSON |
234
255
  | `--interval <seconds>` | no | `2` | Polling interval in seconds |
235
- | `--max-wait <seconds>` | no | `1800` | Maximum wait before exiting 2 |
256
+ | `--max-wait <seconds>` | no | `1800` | Maximum wait before exit code 2 |
236
257
 
237
258
  ### `eigenpal runs cancel [options] <run-id>`
238
259
 
@@ -108,6 +108,7 @@ eigenpal
108
108
  │ ├── get <run-id>
109
109
  │ ├── compare|diff <reference-run-id> <run-id>
110
110
  │ ├── rerun <run-id>
111
+ │ ├── promote <run-id>
111
112
  │ ├── artifacts|artifact
112
113
  │ │ ├── list|ls <run-id>
113
114
  │ │ └── fetch <run-id>
@@ -125,8 +126,6 @@ eigenpal
125
126
  │ │ └── delete <run-id> <name>
126
127
  │ ├── watch <run-id>
127
128
  │ └── cancel <run-id>
128
- ├── git [gitArgs...]
129
- ├── completion <shell>
130
129
  └── skill
131
130
  ├── install
132
131
  ├── uninstall [toolIds...]
@@ -96,14 +96,14 @@ workflow
96
96
 
97
97
  ### Core
98
98
 
99
- | Command | Description |
100
- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101
- | `eigenpal workflow list\|ls [options]` | List workflows the caller can read. |
102
- | `eigenpal workflow pull [options] <workflow-id>` | Download the YAML definition of the workflow at its current version. |
103
- | `eigenpal workflow push [options]` | Create or update a workflow from a YAML file. |
104
- | `eigenpal workflow move [options] <workflow-id>` | Move a workflow to a folder path, creating folders as needed |
105
- | `eigenpal workflow validate [options] [path]` | Local-only validation. Without [path]: runs the templated three-way check (./workflow.yaml + ./evaluators.yaml + ./dataset/). With [path] pointing at a YAML file: validates just that workflow.yaml. For per-noun targeting use `evaluators validate` or `dataset validate`. |
106
- | `eigenpal workflow clear-local [options] [examples...]` | Delete local execution artifacts under ./dataset/examples/. Keeps the latest run per example by default. |
99
+ | Command | Description |
100
+ | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
101
+ | `eigenpal workflow list\|ls [options]` | List workflows the caller can read. |
102
+ | `eigenpal workflow pull [options] <workflow-id>` | Download the YAML definition of the workflow at its current version. |
103
+ | `eigenpal workflow push [options]` | Create or update a workflow from a YAML file. |
104
+ | `eigenpal workflow move [options] <workflow-id>` | Move a workflow to a folder path, creating folders as needed |
105
+ | `eigenpal workflow validate [options] [path]` | Local-only validation. Without [path]: runs the templated three-way check (./workflow.yaml + ./evaluators.yaml + ./dataset/). With [path] pointing at a YAML file: validates that workflow.yaml. For per-noun targeting use `evaluators validate` or `dataset validate`. |
106
+ | `eigenpal workflow clear-local [options] [examples...]` | Delete local execution artifacts under ./dataset/examples/. Keeps the latest run per example by default. |
107
107
 
108
108
  ### Evaluators
109
109
 
@@ -233,7 +233,7 @@ Move a workflow to a folder path, creating folders as needed
233
233
 
234
234
  ### `eigenpal workflow validate [options] [path]`
235
235
 
236
- Local-only validation. Without [path]: runs the templated three-way check (./workflow.yaml + ./evaluators.yaml + ./dataset/). With [path] pointing at a YAML file: validates just that workflow.yaml. For per-noun targeting use `evaluators validate` or `dataset validate`.
236
+ Local-only validation. Without [path]: runs the templated three-way check (./workflow.yaml + ./evaluators.yaml + ./dataset/). With [path] pointing at a YAML file: validates that workflow.yaml. For per-noun targeting use `evaluators validate` or `dataset validate`.
237
237
 
238
238
  ### Arguments
239
239
 
@@ -601,7 +601,7 @@ Poll until terminal, then auto-pull results — replaces `status --watch` + `res
601
601
  | `--max-wait <seconds>` | no | `1800` | Hard ceiling in seconds (default 1800 = 30 min) |
602
602
  | `--pull-on-complete <path>` | no | | Destination for the results file. Default: ./results-<batchId>.<format> |
603
603
  | `--format <csv\|json>` | no | `"json"` | Results export format (default json) |
604
- | `--no-pull` | no | | Skip auto-pulling results on terminal (just watch) |
604
+ | `--no-pull` | no | | Skip auto-pulling results on terminal (watch only) |
605
605
  | `--base-url <url>` | no | | Server base URL |
606
606
 
607
607
  ### `eigenpal workflow versions list|ls [options] <workflow-id>`
@@ -1,309 +1,142 @@
1
1
  # Dataset folder format
2
2
 
3
- A dataset is a folder of named example folders. The folder structure
4
- itself is the manifest — there is no top-level `manifest.json`. The
5
- import endpoint **rejects** archives that contain one (legacy format).
3
+ A dataset is a folder of named example folders. The folder structure itself is
4
+ the manifest; there is no top-level `manifest.json`, and the import endpoint
5
+ rejects archives that contain one.
6
6
 
7
- This is the format the CLI builds and ships to
8
- `eigenpal workflow dataset push <workflow-id> --file dataset/`. After it lands
9
- on the server, the canonical per-row surface is
10
- `eigenpal workflow dataset example {get,create,update,delete}` (and
11
- `dataset list` for a table view) — the local folder is a pre-push
12
- staging area, not a synced mirror.
13
-
14
- Dataset folders are unrelated to workflow organization folders. To place
15
- the workflow itself under dashboard folders, use `workflow move --folder`;
16
- dataset push/pull never changes folder placement.
7
+ This is the archive format for automation datasets, shared by workflows and
8
+ agents. `eigenpal workflow dataset push <workflow-id> --file dataset/` builds
9
+ this archive locally and sends it to the server.
17
10
 
18
11
  ## Required layout
19
12
 
20
- ```
13
+ ```text
21
14
  dataset/
22
15
  └── examples/
23
16
  ├── invoice-foo/
17
+ │ ├── input.json REQUIRED — full run input object
24
18
  │ ├── input/
25
- │ │ ├── arguments.json REQUIRED — { language: "en" }
26
- │ │ └── contract/ file-argument folder
27
- │ │ ├── Contract_2026.pdf
28
- │ │ └── Appendix.pdf
19
+ │ │ ├── Contract_2026.pdf referenced by input.json
20
+ │ │ └── Appendix.pdf
21
+ │ ├── expected.json OPTIONAL — expected output or { "$error": ... }
29
22
  │ ├── expected/
30
- │ │ ├── output.json OPTIONAL — raw ground truth JSON (success-expected)
31
- │ │ └── generated_invoice/ OPTIONAL — expected file output
32
- │ │ └── Invoice.docx
23
+ │ │ └── Invoice.docx referenced by expected.json
33
24
  │ └── meta.json OPTIONAL — { rowOrder?, annotation?, overrides? }
34
25
  │
35
- ├── invoice-bar/
36
- │ └── input/
37
- │ └── arguments.json { amount: 42 }
38
- │
39
26
  └── unsupported-format/
40
- ├── input/
41
- │ └── arguments.json
42
- └── expected/
43
- └── error.json OPTIONAL — failure-expected, mutually exclusive with output.json
27
+ ├── input.json
28
+ └── expected.json { "$error": { "code": 422 } }
44
29
  ```
45
30
 
46
- The `expected/` subfolder mirrors `input/` exactly. `output.json` is
47
- the scalar/object data ground truth, and any expected file outputs go
48
- in `expected/<docKey>/<file>` folders (one folder per output path).
49
- `error.json` is the failure-expected counterpart (see "Failure-expected
50
- examples" below) and cannot coexist with `output.json` for the same
51
- example.
31
+ `input.json` is the source of truth for the full automation input. File values
32
+ are explicit references:
52
33
 
53
- ## Rules (every one is enforced server-side at import)
34
+ ```json
35
+ {
36
+ "language": "en",
37
+ "contract": [
38
+ { "$file": "input/Contract_2026.pdf" },
39
+ { "$file": "input/Appendix.pdf" }
40
+ ]
41
+ }
42
+ ```
54
43
 
55
- - **Folder names** (example names, argument names, expected document
56
- keys) must match `[a-z0-9][a-z0-9-_]*`.
57
- - **`input/arguments.json`** is REQUIRED for every example, even if it's
58
- just `{}`. It must be a JSON object (not array, not literal).
59
- - **File-arguments** live in `input/<arg-name>/<filename>`. The
60
- materialized shape on `triggerInput` is **driven by the workflow YAML's
61
- `inputs[].type`**, not by the file count:
44
+ `expected.json` mirrors the expected automation output. Expected files use the
45
+ same reference shape with the `expected/` prefix.
62
46
 
63
- | workflow.yaml input | dataset folder | `triggerInput.<name>` |
64
- | ------------------------------------------ | ------------------------- | --------------------- |
65
- | `type: file` | `input/<name>/<one-file>` | `{ fileId }` |
66
- | `type: array`, `items: { type: file }` | `input/<name>/<file>...` | `[{ fileId }, ...]` |
47
+ ## Rules
67
48
 
68
- Reference single-file inputs as `{{ input.<name> }}` (resolves to the
69
- `{ fileId }` object the worker reads). Reference array-of-file inputs
70
- as `{{ input.<name>[0] }}` etc. Dropping multiple binaries into an
71
- `input/<name>/` folder for an input declared `type: file` is rejected
72
- at import with `code: single_file_input_overpopulated` — either trim
73
- to one file or change the workflow input to the array form.
74
- - **Argument-name collisions are rejected.** If `arguments.json` has
75
- `{"contract": "..."}` AND `input/contract/file.pdf` exists, import
76
- fails with `code: argument_name_collision`.
77
- - **`expected/output.json`** is OPTIONAL. When present, it must be a
78
- JSON object that mirrors your workflow's `output:` shape 1:1, no
79
- envelope, no wrapper. A scalar/array at the top level is rejected
80
- with `code: invalid_expected_output`. Mutually exclusive with
81
- `expected/error.json` (see below).
82
- - **`expected/error.json`** is OPTIONAL and marks the example as
83
- failure-expected (the workflow is supposed to terminate via
84
- `control.fail`). Must be a JSON object with at least one of
85
- `{ code, messageContains, step }`; an empty object is rejected (it
86
- would match any failure). When set, the scorer matches the example's
87
- terminal `executions.error` envelope against the fields you specify
88
- and ignores `expected/output.json` entirely. `code` is the HTTP-style
89
- status code from `control.fail`'s `statusCode`; `messageContains` is a
90
- case-sensitive substring of the failure message; `step` is the name
91
- of the `control.fail` step expected to fire.
49
+ - Example folder names must match `[a-z0-9][a-z0-9-_]*`.
50
+ - Every example needs `input.json`, even if it is `{}`. It must be a JSON object.
51
+ - Files under `input/` must be referenced from `input.json` as
52
+ `{ "$file": "input/<path>" }`.
53
+ - Files under `expected/` must be referenced from `expected.json` as
54
+ `{ "$file": "expected/<path>" }`.
55
+ - File references cannot use `..`, absolute paths, backslashes, or null bytes.
56
+ - `expected.json` is optional. When present, it must be a JSON object.
57
+ - Failure-expected examples use `expected.json` with a single `$error` key:
92
58
 
93
- ```json
94
- // expected/error.json
95
- {
59
+ ```json
60
+ {
61
+ "$error": {
96
62
  "code": 422,
97
63
  "messageContains": "unsupported document type",
98
64
  "step": "reject-unsupported"
99
65
  }
100
- ```
101
-
102
- > **Note:** the importer is idempotent and accepts both bare objects and
103
- > pre-wrapped `{ data: { ... } }` forms; bare is preferred.
66
+ }
67
+ ```
104
68
 
105
- ```json
106
- // workflow.yaml
107
- // output:
108
- // invoiceNumber: '{{ steps.extract.output.invoiceNumber }}'
109
- // totalAmount: '{{ steps.extract.output.totalAmount }}'
110
- //
111
- // → expected/output.json
112
- {
113
- "invoiceNumber": "INV-001",
114
- "totalAmount": 1234.56
115
- }
116
- ```
117
- - **Expected file outputs** live in `expected/<docKey>/<filename>` —
118
- symmetric with `input/<argName>/<file>`. Each `<docKey>` folder is
119
- uploaded and becomes an `expectedDocuments[docKey]` entry server-side
120
- (single file ⇒ `{fileId}`, multiple ⇒ `[{fileId}, …]`). The judge
121
- compares by file presence + content, not byte-equal.
122
- - **`meta.json`** is OPTIONAL. Schema:
123
- ```json
124
- {
125
- "rowOrder": 0,
126
- "annotation": "free-form notes about this example",
127
- "overrides": {
128
- "steps": {
129
- "<stepName>": { ...stepOutputObject }
130
- }
131
- }
132
- }
133
- ```
134
- - `rowOrder` — sort order in the dashboard
135
- - `annotation` — surfaced in the UI; useful for "why this case is
136
- interesting" notes
137
- - `overrides.steps.<stepName>` — short-circuits the named step,
138
- returns the override as the step's output. Use for steps that
139
- depend on external state (third-party APIs, private connectors) so the rest
140
- of the example can still run.
141
- - **Path-traversal patterns** (`..`, leading `/`, backslashes, null
142
- bytes) reject the whole archive.
69
+ The `$error` object must contain at least one of `code`, `messageContains`, or
70
+ `step`. It asserts that the automation should fail with a matching typed
71
+ `control.fail` envelope.
143
72
 
144
- ## How the row materializes
73
+ ## How a row materializes
145
74
 
146
75
  For an example with:
147
76
 
148
- - `arguments.json`: `{"language": "en"}`
149
- - `input/contract/Contract_2026.pdf`
150
- - `input/contract/Appendix.pdf`
151
- - `expected/output.json`: `{"invoiceNumber": "INV-001"}`
152
- - `expected/generated_invoice/Invoice.docx`
77
+ - `input.json`: `{"language":"en","contract":[{"$file":"input/Contract_2026.pdf"},{"$file":"input/Appendix.pdf"}]}`
78
+ - `input/Contract_2026.pdf`
79
+ - `input/Appendix.pdf`
80
+ - `expected.json`: `{"invoiceNumber":"INV-001","generatedInvoice":{"$file":"expected/Invoice.docx"}}`
81
+ - `expected/Invoice.docx`
153
82
 
154
- The server materializes:
83
+ The stored row keeps the same input shape. When the example runs, each `$file`
84
+ reference is resolved into the S3 file descriptor the worker consumes.
155
85
 
156
86
  ```json
157
- // eval_examples.triggerInput
158
87
  {
159
88
  "language": "en",
160
89
  "contract": [
161
- { "fileId": "file_abc..." },
162
- { "fileId": "file_def..." }
90
+ { "kind": "s3", "ref": ".../input/Contract_2026.pdf", "filename": "Contract_2026.pdf" },
91
+ { "kind": "s3", "ref": ".../input/Appendix.pdf", "filename": "Appendix.pdf" }
163
92
  ]
164
93
  }
165
-
166
- // eval_examples.expectedOutput
167
- {
168
- "data": { "invoiceNumber": "INV-001" },
169
- "expectedDocuments": {
170
- "generated_invoice": { "fileId": "file_ghi..." }
171
- }
172
- }
173
94
  ```
174
95
 
175
- Each file is uploaded to S3 with a fresh `fileId`; the original
176
- filename is preserved on the `files` row and shows up in the dashboard.
177
- The `expectedOutput` envelope (`{ data?, expectedDocuments?, error? }`) is
178
- internal storage shape — your dataset folder stays clean (raw
179
- `output.json` or `error.json`, plus per-docKey folders).
180
-
181
- ## Failure-expected examples
182
-
183
- When a workflow uses `control.fail` to reject bad inputs, you usually
184
- want to test both paths: happy-path examples carry `expected/output.json`
185
- and assert the workflow completed; rejection examples carry
186
- `expected/error.json` and assert the workflow failed with the right
187
- status code.
188
-
189
- ```
190
- examples/
191
- ├── valid-invoice/
192
- │ ├── input/...
193
- │ └── expected/output.json # success-expected
194
- └── unsupported-format/
195
- ├── input/...
196
- └── expected/error.json # failure-expected
197
- # { "code": 422, "step": "reject-unsupported" }
198
- ```
199
-
200
- The two files are mutually exclusive for the same example (an example
201
- is either a success-case or a failure-case, never both). The `exact-diff`
202
- evaluator branches on the envelope shape and reports a clean
203
- `expectedFailureButSucceeded` or `actualWasUntypedError` reason when
204
- the actual run doesn't match the expectation.
96
+ Each file is uploaded to S3 and the original filename is preserved.
205
97
 
206
98
  ## Validate before pushing
207
99
 
208
100
  ```bash
209
- eigenpal workflow validate dataset ./dataset
101
+ eigenpal workflow dataset validate ./dataset
210
102
  ```
211
103
 
212
- Reports per-example issues:
104
+ Example output:
213
105
 
214
- ```
106
+ ```text
215
107
  ✗ dataset (./dataset) — 2 issues
216
- examples/Invoice-Foo Folder name must be lowercase kebab/snake-case.
217
- examples/foo/input/contract Argument-name collision: "contract" appears in both arguments.json and as an input/ folder.
108
+ examples/Invoice-Foo Folder name must be lowercase kebab/snake-case.
109
+ examples/foo/input.json:contract Referenced file does not exist: input/contract.pdf.
218
110
  ```
219
111
 
220
112
  ## Push
221
113
 
222
114
  ```bash
223
- # Replace = wipes existing examples for the workflow, uploads the folder fresh.
115
+ # Replace wipes existing examples for the automation, uploads the folder fresh.
224
116
  eigenpal workflow dataset push <workflow-id> --file ./dataset --mode replace
225
117
 
226
- # Append = adds to whatever's already on the server.
118
+ # Append adds to whatever is already on the server.
227
119
  eigenpal workflow dataset push <workflow-id> --file ./dataset --mode append
228
120
  ```
229
121
 
230
- The endpoint streams progress as NDJSON. The terminal `done` event
231
- includes `{ created, expectedSet, … }`:
122
+ The endpoint streams progress as NDJSON. The terminal `done` event includes
123
+ `{ created, expectedSet, … }`:
232
124
 
233
- - `created` — examples successfully persisted
234
- - `expectedSet` — how many of those carried an `expected/output.json`.
235
- If `expectedSet < created`, your zip didn't include the `expected/`
236
- folders — the dashboard's "Expected" tab will be blank and evals
237
- will only run un-graded.
125
+ - `created` — examples successfully persisted.
126
+ - `expectedSet` — how many of those carried an `expected.json`. If
127
+ `expectedSet < created`, the rest run un-graded.
238
128
 
239
129
  ## Editing a dataset on the server
240
130
 
241
- To inspect or round-trip what's currently on the server:
131
+ To inspect or round-trip what is currently on the server:
242
132
 
243
133
  ```bash
244
134
  eigenpal workflow dataset list <workflow-id>
245
135
  eigenpal workflow dataset pull <workflow-id> --out current.zip
246
- # (or omit --out to stream the zip to stdout for piping)
247
- ```
248
-
249
- For bulk changes — many examples at once, schema-level rework, file
250
- swaps — edit the local folder and re-push with
251
- `dataset push --mode replace`.
252
-
253
- For one-row tweaks — capturing a corrected output as the new GT,
254
- deleting one bad example, adding one example to a known dataset — use
255
- the per-example CRUD commands instead. They avoid the round-trip of
256
- re-zipping and re-uploading the whole folder.
257
-
258
- ### Inspect one example end-to-end
259
-
260
- ```bash
261
- eigenpal workflow dataset example get <workflow-id> <example-id>
262
- eigenpal workflow dataset example get <workflow-id> <example-id> --json | jq '.expectedOutput'
263
- ```
264
-
265
- Pretty mode prints labelled `Inputs` / `Expected` / `Metadata` sections;
266
- `--json` returns the full row (`triggerInput`, `expectedOutput`,
267
- `annotation`, `rowOrder`, timestamps) for piping.
268
-
269
- ### Capture a corrected output as the new ground truth
270
-
271
- ```bash
272
- # 1. Look at what the workflow actually returned for the example
273
- eigenpal runs get exec_… --json | jq '.output.data' > /tmp/correct.json
274
-
275
- # 2. Patch the example's expected output in place. PATCH semantics:
276
- # every flag you omit is left alone.
277
- eigenpal workflow dataset example update <workflow-id> <example-id> \
278
- --expected-file /tmp/correct.json
279
- ```
280
-
281
- `--expected-file -` reads JSON from stdin if you'd rather pipe. Pass
282
- empty string to clear an annotation: `--annotation ""`.
283
-
284
- ### Add one new example
285
-
286
- ```bash
287
- eigenpal workflow dataset example create <workflow-id> \
288
- --name missing-required-field \
289
- --input-json '{"language":"en"}' \
290
- --expected-file /tmp/expected.json \
291
- --annotation "edge case: required field absent from input"
292
- ```
293
-
294
- `--input-json` / `--expected-json` accept inline JSON literals; the
295
- `-file` variants read from disk (or `-` for stdin). They're mutually
296
- exclusive — pass one or the other, not both.
297
-
298
- File-arg uploads still go through `dataset push`; CRUD only handles
299
- scalar args + `expected/output.json`-style outputs.
300
-
301
- ### Delete one bad example
302
-
303
- ```bash
304
- eigenpal workflow dataset example delete <workflow-id> <example-id> --yes
305
136
  ```
306
137
 
307
- `--yes` is required for non-TTY shells (CI). Interactive shells may
308
- omit it; single-row deletes have a small blast radius compared to
309
- `dataset push --mode replace`, so no confirmation prompt fires.
138
+ For bulk changes, edit the local folder and re-push with
139
+ `dataset push --mode replace`. For one-row tweaks, use
140
+ `eigenpal workflow dataset example {get,create,update,delete}`. File uploads
141
+ still go through `dataset push`; CRUD only handles JSON input and
142
+ `expected.json`-style outputs.