saffron-ai 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +252 -0
- package/README.md +13 -2
- package/dist-pkg/cli.js +180 -148
- package/package.json +16 -2
- package/skills/saffron/references/config.md +18 -9
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,258 @@ All notable changes to Saffron, in one place: the `saffron-ai` runner (npm),
|
|
|
4
4
|
the VS Code extension (`ChathurangaJayasinghe.saffron-vscode`) and the
|
|
5
5
|
JetBrains plugin (`ai.saffron.jetbrains`). Dates are publish dates.
|
|
6
6
|
|
|
7
|
+
## 0.9.2
|
|
8
|
+
|
|
9
|
+
Released 2026-09-30.
|
|
10
|
+
|
|
11
|
+
Runner
|
|
12
|
+
|
|
13
|
+
- **Browser tool calls are checked call by call.** A security review of
|
|
14
|
+
the agent's tools found three ways a prompt-injected page could reach
|
|
15
|
+
the machine: `view-source:file://` slipped past Playwright's `file:`
|
|
16
|
+
check and read any file; the `filename` option of the snapshot,
|
|
17
|
+
screenshot, console and network tools wrote page content over any
|
|
18
|
+
project file (a response body into a bin is code execution on the next
|
|
19
|
+
run); and a file upload could hand `.env`, with every secret, to the
|
|
20
|
+
page. Navigation now opens web pages only, uploads take existing files
|
|
21
|
+
inside the project by their real path (a link out of the project is
|
|
22
|
+
out) and never dotfiles, `filename` is dropped, and anything the
|
|
23
|
+
vendor's tools write goes to a private folder of the session, removed
|
|
24
|
+
when it ends. The checks sit in the one
|
|
25
|
+
proxy every provider and both paths use, and replay applies the same
|
|
26
|
+
ones to a cache, which is data anyone can commit. A project reached
|
|
27
|
+
through a symlinked path (`/var` and `/private/var` on macOS) counts
|
|
28
|
+
as the project. Claude sessions also set
|
|
29
|
+
`strictMcpConfig`, so a `.mcp.json` or user-level MCP server of yours
|
|
30
|
+
never joins a recording. Codex sessions turn off its remaining
|
|
31
|
+
execution features (`unified_exec`, `multi_agent`, `code_mode_host`,
|
|
32
|
+
the in-app and external browsers) besides the shell.
|
|
33
|
+
- **Go-to-definition works under a folder with a space, `#` or `%` in
|
|
34
|
+
its name, and on Windows.** The language server sent unencoded file
|
|
35
|
+
paths as URIs; a strict client rejected them. A nested project without
|
|
36
|
+
a `features` key now gets the default folder instead of the outer
|
|
37
|
+
project's, and on Windows a StepSet in a subfolder is no longer flagged
|
|
38
|
+
as defined twice.
|
|
39
|
+
- **`--filter "not @wip @slow"` is refused** instead of reading as
|
|
40
|
+
`not @wip or @slow`: once `and`, `not` or parentheses appear, every
|
|
41
|
+
tag needs its `and` or `or`. The plain list forms are unchanged.
|
|
42
|
+
- **Outline values no longer replace inside a test id or frame selector
|
|
43
|
+
by coincidence** (`add` in `add-to-cart`), while a short id with a
|
|
44
|
+
digit still does (`1234` in `order-1234`), and a `<name>` that opens a
|
|
45
|
+
regex named group is left alone at replay.
|
|
46
|
+
- **The cost trend leaves a gap for runs whose provider reported no cost**
|
|
47
|
+
instead of plotting them as $0, and every other run keeps its place. Cursor's turn limit counts tool calls,
|
|
48
|
+
not the pieces their arguments stream in. An Antigravity session that
|
|
49
|
+
hits the turn limit says so, and how to raise it, even when agy
|
|
50
|
+
reports the session a success.
|
|
51
|
+
|
|
52
|
+
- **No agent can run code on your machine through Playwright.**
|
|
53
|
+
Playwright MCP's `browser_run_code_unsafe` runs any JavaScript in the
|
|
54
|
+
Playwright server process (its own docs call it RCE-equivalent); a
|
|
55
|
+
Codex test session used it to read a file from the home directory. It
|
|
56
|
+
is now hidden from every provider's agent, Claude's included, and a call
|
|
57
|
+
to it by name is refused. Nothing recorded depends on it.
|
|
58
|
+
`browser_evaluate`, which runs in the page, stays.
|
|
59
|
+
- **Claude recordings no longer carry your claude.ai connectors.** On a
|
|
60
|
+
Claude subscription login, Claude Code loads the account's claude.ai
|
|
61
|
+
connectors (Gmail, Figma, Canva, ElevenLabs and the like) into every
|
|
62
|
+
session from its second request on. On one account that added 294 tool
|
|
63
|
+
definitions, 1.16 MB of schema, to every model call: recording the
|
|
64
|
+
guestbook example cost $15.74 at API rates and 11.5M cache-read tokens.
|
|
65
|
+
Saffron's sessions now set `disableClaudeAiConnectors`, and the same
|
|
66
|
+
recording costs $0.37 with 279k cache-read tokens, in 70 s instead of
|
|
67
|
+
180 s. The agent never needed those tools; your connectors in Claude
|
|
68
|
+
itself are untouched.
|
|
69
|
+
- **Record and heal on the subscription you already have.** Besides
|
|
70
|
+
Claude (still the default), `"provider"` in `saffron.config.json` (or
|
|
71
|
+
`--provider`) now takes `codex` (a ChatGPT plan, through
|
|
72
|
+
`@openai/codex-sdk`), `antigravity` (Google AI Pro/Ultra, through
|
|
73
|
+
Google's `agy` CLI; experimental) and `cursor` (through `@cursor/sdk`).
|
|
74
|
+
Each runs on the login its official client already has on the machine.
|
|
75
|
+
These agents reach the browser through a local MCP gateway that records
|
|
76
|
+
every call with the same hooks as Claude, so their recordings, heals and
|
|
77
|
+
proof replays work the same. `model` takes the provider's own model
|
|
78
|
+
names; `saffron author` takes `--provider` too. The two SDKs are
|
|
79
|
+
optional peer dependencies: install the one you use. Codex runs without
|
|
80
|
+
your own `~/.codex/config.toml` apart from its login settings (a keyring
|
|
81
|
+
login works), which needs macOS or Linux for now: on Windows the Codex
|
|
82
|
+
provider refuses to start rather than run with your own MCP servers.
|
|
83
|
+
`agy` gets an explicit `--print-timeout` sized to the turn budget, and
|
|
84
|
+
an `agy` that cannot start fails its scenario instead of the run.
|
|
85
|
+
Antigravity is verified live with agy 1.2.13 (record, proof replay and
|
|
86
|
+
heal): its MCP calls go through agy's `call_mcp_tool`, allowed by two
|
|
87
|
+
rules in agy's settings and limited by a hook to Saffron's two servers.
|
|
88
|
+
- **`saffron login [provider]`** says what a provider still needs on this
|
|
89
|
+
machine, and does the one-time setup where one is needed:
|
|
90
|
+
`saffron login cursor` runs Cursor's sign-in, and
|
|
91
|
+
`saffron login antigravity` checks that agy is signed in and allows
|
|
92
|
+
Saffron's tools in agy's settings (two rules, everything else kept).
|
|
93
|
+
- **New docs page: AI providers and sign-in** (`docs/PROVIDERS.md`), with
|
|
94
|
+
every provider's sign-in, what `saffron login` sets up, what Saffron does
|
|
95
|
+
in its sessions, how to undo it, and troubleshooting.
|
|
96
|
+
- **A pass can no longer leave an assertion unchecked.** `saffron_done`
|
|
97
|
+
with success is refused once while an announced `Then` step has
|
|
98
|
+
recorded no check, and Saffron's tool refusals are now MCP errors, so an
|
|
99
|
+
agent that batches its calls stops and fixes the step instead of filing
|
|
100
|
+
an unverified proposal. A second pass stands (the proof replay still
|
|
101
|
+
marks it unverified), so no session burns its turns on it. A step
|
|
102
|
+
announced twice keeps one adaptation note, and an agent that fails
|
|
103
|
+
after `saffron_done` keeps its verdict.
|
|
104
|
+
- **Node.js 20.6 or newer** is now required (22.13 for the Cursor
|
|
105
|
+
provider).
|
|
106
|
+
- **Cost not reported, not $0.** Runs whose provider reports no price say
|
|
107
|
+
so in the summary line and the report instead of showing $0.0000.
|
|
108
|
+
- **The editor finds a project nested inside a bigger folder.** When the
|
|
109
|
+
IDE is opened above the Saffron project (a solution folder with the
|
|
110
|
+
tests in a subfolder), `saffron lsp` now resolves each `.saffron` file
|
|
111
|
+
against the nearest `saffron.config.json` above it. StepSets defined in
|
|
112
|
+
a sibling file and `{data:...}` references were reported as undefined
|
|
113
|
+
in that layout; they resolve now, and completion, hover and
|
|
114
|
+
go-to-definition follow the same root.
|
|
115
|
+
- **A browser tool result can no longer flood the agent's context.** A
|
|
116
|
+
recording fetched a 1.5 MB API response body with
|
|
117
|
+
`browser_network_request` (about 400k tokens). Claude Code then compacted
|
|
118
|
+
the session in a loop and failed the scenario after a few dollars of
|
|
119
|
+
tokens. Every browser tool result is now cut at 60,000 characters, with
|
|
120
|
+
a note asking for less (a snapshot of one region, a filtered request
|
|
121
|
+
list, or a `saffron_assert` on the field). The Playwright code Saffron
|
|
122
|
+
records comes first in a result, so a cut result still records its
|
|
123
|
+
action. The cap applies with and without the trace bridge, and to Codex,
|
|
124
|
+
Antigravity and Cursor sessions through the tool gateway.
|
|
125
|
+
- **Each Outline row acts on its own element through a locator chain.** A
|
|
126
|
+
chain keeps the row's value inside its selector; that value now becomes
|
|
127
|
+
the row's `<param>` (or data-table reference) there too, so another row
|
|
128
|
+
no longer clicks the recorded row's card. The same goes for every other
|
|
129
|
+
field replay resolves: an assertion's regex pattern (an `href`, a
|
|
130
|
+
heading's name), a test id, a prompt answer, a frame and a drag's drop
|
|
131
|
+
target. An Outline checking a link's `href` failed as written on every
|
|
132
|
+
row but the recorded one.
|
|
133
|
+
- **A changed table or data value still written in the recording is found
|
|
134
|
+
escaped too** (in a selector's quoted or regex body, or a pattern), so
|
|
135
|
+
the recording is made again instead of replaying the old value.
|
|
136
|
+
- **Chains with a comma, an apostrophe or a role option record the element
|
|
137
|
+
they clicked.** `filter({ hasText: 'Paris, France' })` and
|
|
138
|
+
`getByText(/^Don't$/)` recorded nothing, a regex with an apostrophe made
|
|
139
|
+
a selector Playwright read differently, and `getByRole('heading', {
|
|
140
|
+
level: 2 })` recorded any heading. Role options are kept as selector
|
|
141
|
+
attributes; one with no selector form records nothing.
|
|
142
|
+
- **Tag lists read the way 0.9.0 read them.** An empty entry (`@a,@b,`) is
|
|
143
|
+
skipped again, and a tag's own parentheses (`@issue(42)`) stay part of
|
|
144
|
+
it; a parenthesis after a space or a keyword still groups.
|
|
145
|
+
- **Replay fixes.** A heal that fixes a network assertion is checked
|
|
146
|
+
against the response the agent's session produced, instead of failing
|
|
147
|
+
"as written" while waiting for a new one. A replay no longer inherits an
|
|
148
|
+
upload armed on its page by an earlier replay (a pending proposal that
|
|
149
|
+
failed), which failed its first click. A value resolved into a regex
|
|
150
|
+
pattern (`{env:VAR}`, `<param>`, `{data:...}`) matches as written: the
|
|
151
|
+
`+` in `a+b@x.com` or the `$` in `$9.99` no longer change the pattern.
|
|
152
|
+
- **A failed tool call records nothing** in Codex, Antigravity and Cursor
|
|
153
|
+
sessions, as in Claude's: an upload refused because no file chooser was
|
|
154
|
+
open was recorded, and broke replay.
|
|
155
|
+
- **Antigravity gets its prompt on stdin.** The prompt carries resolved
|
|
156
|
+
`{env:VAR}` values, and `agy -p` put it in the command line, readable by
|
|
157
|
+
every account on the machine.
|
|
158
|
+
- **Tools a recording cannot replay are hidden from every agent:** going
|
|
159
|
+
back or forward, reloading, resizing and dropping files.
|
|
160
|
+
`browser_navigate` reaches any page instead, and is recorded.
|
|
161
|
+
- **`saffron_done` is refused once when a `Then` step recorded only setup**
|
|
162
|
+
(a click that opens a panel) and no check, the rule replay applies.
|
|
163
|
+
- **Playwright's MCP server gets the whole environment again** when the
|
|
164
|
+
trace bridge is off, so `PLAYWRIGHT_MCP_*` settings apply there too.
|
|
165
|
+
- **A proposal lock held by a live process on this machine is broken after
|
|
166
|
+
an hour**: a pid reused after its holder died kept it forever.
|
|
167
|
+
- **`saffron status` says "cost not reported"** for a provider with no
|
|
168
|
+
price, instead of $0.00. `saffron status --json` leaves such a cost
|
|
169
|
+
out (the last run's, history's and proposals'), and the editors'
|
|
170
|
+
last-run lines say "cost not reported" too (VS Code and JetBrains, next
|
|
171
|
+
editor release).
|
|
172
|
+
- **A Codex or Cursor session stopped at the turn limit says so**, and to
|
|
173
|
+
raise `maxTurns`, instead of "ended without finalizing".
|
|
174
|
+
- **`saffron login cursor` on Node below 22.13 names the Node version it
|
|
175
|
+
needs**, and a vendor SDK missing a part of its own reports that part
|
|
176
|
+
instead of asking to install the SDK again.
|
|
177
|
+
- **A single locator with options records the element it acted on.**
|
|
178
|
+
`getByText('Save', { exact: true })`, `getByLabel('Email', { exact:
|
|
179
|
+
true })` and `getByText(/^Save$/)` recorded nothing, so the click or
|
|
180
|
+
fill was missing from the recording, and `getByRole('checkbox', {
|
|
181
|
+
checked: false })` recorded any checkbox. They record as a selector now,
|
|
182
|
+
as in a chain.
|
|
183
|
+
- **An Outline row's value inside a regex matches the row replaying.**
|
|
184
|
+
Playwright writes exact text as a regex (`filter({ hasText: /^Garden
|
|
185
|
+
Queen$/ })`); that value becomes the row's `<param>` as well, and replay
|
|
186
|
+
writes the next row's value escaped, quotes, slashes and brackets
|
|
187
|
+
included.
|
|
188
|
+
- **A secret inside a selector or an assertion's pattern is masked where
|
|
189
|
+
it is written escaped** (a quote in a quoted name, a `.` in a regex), and
|
|
190
|
+
replay writes it back the same way. A short one next to an escape
|
|
191
|
+
(`\buat\b`) counts as standing alone.
|
|
192
|
+
- **The recorder reads only the code Playwright ran.** A snapshot or a
|
|
193
|
+
response body that showed Playwright code (a docs page, a code viewer)
|
|
194
|
+
was recorded as actions.
|
|
195
|
+
- **Only tools a recording can hold reach the agent**, whatever
|
|
196
|
+
`PLAYWRIGHT_MCP_CAPS` turns on: an allowlist of the tools that record or
|
|
197
|
+
only read replaces the list of hidden ones, so coordinate mouse,
|
|
198
|
+
storage, cookie and route tools stay out.
|
|
199
|
+
- **A heal checks a network assertion against the response that came
|
|
200
|
+
late.** The failed replay's response log keeps listening through the
|
|
201
|
+
heal, so a response that arrived just after replay gave up counts.
|
|
202
|
+
- **A cut browser result never splits an emoji**, which made Codex drop
|
|
203
|
+
the whole result.
|
|
204
|
+
- **`saffron login antigravity` writes through a settings link** (a
|
|
205
|
+
dotfiles repo, even before the file exists there, through chains and
|
|
206
|
+
linked folders) and keeps the file's permissions; a link into a
|
|
207
|
+
read-only place (a Nix store) is left as it is, with the two rules to
|
|
208
|
+
add named.
|
|
209
|
+
- **The editor keeps each nested project's vocabulary**, built once, in
|
|
210
|
+
editors that watch its files for it (VS Code, JetBrains), and a changed
|
|
211
|
+
file refreshes every open project's diagnostics. A project whose build
|
|
212
|
+
fails keeps its last good one, and coming back to a project checks its
|
|
213
|
+
documents again. Opening a file it already read (go to definition)
|
|
214
|
+
rebuilds nothing.
|
|
215
|
+
- **A browser action a Codex, Antigravity or Cursor agent gave up on is
|
|
216
|
+
still recorded.** Playwright finishes a click or a goto whatever the
|
|
217
|
+
agent does meanwhile, so the recording keeps what the browser did.
|
|
218
|
+
|
|
219
|
+
Editors (VS Code extension 0.3.3, JetBrains plugin 0.3.3)
|
|
220
|
+
|
|
221
|
+
- **A project nested inside the folder the IDE opened.** Both editors
|
|
222
|
+
now find the nearest `saffron.config.json` above a file and treat that
|
|
223
|
+
folder as the project: runs start there, status, proposals, `.env` and
|
|
224
|
+
the step vocabulary come from there, and the project's own `saffron`
|
|
225
|
+
install is used. JetBrains shows a chooser in the tool window title
|
|
226
|
+
when the IDE root holds several projects, and adds a Working directory
|
|
227
|
+
to run configurations. Before, a run from a nested project wrote
|
|
228
|
+
`.saffron/` into the parent folder with default settings and recorded
|
|
229
|
+
everything again.
|
|
230
|
+
- **JetBrains: nothing runs in a project you have not trusted.** No
|
|
231
|
+
status, runs, diffs or language server until the project is trusted;
|
|
232
|
+
the tool window says so and loads as soon as you trust it. The
|
|
233
|
+
language server and the Run menu entries appear only inside a Saffron
|
|
234
|
+
project (a `saffron.config.json`, an installed `saffron-ai`, or
|
|
235
|
+
`.saffron` files), so a Cucumber or Behat folder of `.feature` files
|
|
236
|
+
no longer launches `npx -y`. A nested project is left out of the outer
|
|
237
|
+
project's file list and never run from the outer root.
|
|
238
|
+
- **VS Code on Windows: runs no longer pass through a `.cmd` shim**,
|
|
239
|
+
which cmd.exe re-parses. The installed CLI runs with node directly;
|
|
240
|
+
the npx fallback escapes every argument.
|
|
241
|
+
- **JetBrains: reused run configurations no longer keep a Re-record tick
|
|
242
|
+
from an earlier use.** `.feature` files appear in the Files tab and
|
|
243
|
+
run from the context menu. A new `login` command takes the provider.
|
|
244
|
+
Links in the Dashboard open in your browser. The tool window refreshes
|
|
245
|
+
only for Saffron runs and changes inside the project.
|
|
246
|
+
- **Both editors list proposal files the runner cannot read**, with the
|
|
247
|
+
reason, and count them as pending. A run with no reported cost shows
|
|
248
|
+
as not reported, never as $0.
|
|
249
|
+
- **VS Code:** the panel keeps the project you were on when a webview,
|
|
250
|
+
diff or image takes focus, and Select Project lists nested projects;
|
|
251
|
+
Run Tagged keeps tags written with parentheses such as `@issue(42)`;
|
|
252
|
+
stale status after a run is gone; status errors show the
|
|
253
|
+
error line, not the last stack frame; lines inside docstrings are not
|
|
254
|
+
read as steps; `Example:` is a scenario keyword; Re-record works with
|
|
255
|
+
Replay only ticked; `{data:...}` flags an empty list and a header-only
|
|
256
|
+
CSV; Run Tagged skips tags `--filter` cannot express and says which;
|
|
257
|
+
diagnostics clear when a file closes or is deleted.
|
|
258
|
+
|
|
7
259
|
## 0.9.1
|
|
8
260
|
|
|
9
261
|
Released 2026-09-29.
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Free to use · [npm: saffron-ai](https://www.npmjs.com/package/saffron-ai) · [site](https://saffron-ai.io)
|
|
6
6
|
|
|
7
|
-
Write tests in plain language, as `.saffron` files or standard `.feature` files. `.saffron` is Saffron's own test language and goes further than Gherkin: step sets for reusable flows, `{env:VAR}` tokens that keep secrets out of recordings and reports, and `{data:...}` references to data files. There are no step definitions and no glue code. On the first run, an AI agent (Claude
|
|
7
|
+
Write tests in plain language, as `.saffron` files or standard `.feature` files. `.saffron` is Saffron's own test language and goes further than Gherkin: step sets for reusable flows, `{env:VAR}` tokens that keep secrets out of recordings and reports, and `{data:...}` references to data files. There are no step definitions and no glue code. On the first run, an AI agent (Claude by default, or Codex, Google AI or Cursor on the subscription you already have, driving Playwright MCP) executes each scenario in a real browser and records what it did into a **cache**. A human-reviewable JSON action list committed to git. Every run after that, Saffron replays the cache in a real browser: **zero AI calls, zero tokens**, at the speed of the browser and your app. The cache is Saffron's own JSON, not a generated script; replay runs on Playwright's browser engine, and Playwright MCP is only how the agent operates the browser while it records or heals.
|
|
8
8
|
|
|
9
9
|
When the UI changes and a cached step fails at runtime, the agent takes over mid-execution with full scenario context, adapts, and finishes the run, then files a **cache proposal** you accept or reject, like a snapshot test update. Reports state exactly what was adapted and what it cost.
|
|
10
10
|
|
|
@@ -16,6 +16,7 @@ When the UI changes and a cached step fails at runtime, the agent takes over mid
|
|
|
16
16
|
- **Caches are git artifacts.** Proposals show diffs, including a suggested feature-file edit when the written steps no longer match reality, and nothing is committed without you.
|
|
17
17
|
- **Verified proposals.** Every recording/heal is proof-replayed zero-AI before it's filed (with one bounded refinement pass on failure), so proposals arrive stamped `verified ✓` or honestly `UNVERIFIED ✗`.
|
|
18
18
|
- **Honest cost reporting.** Reports break out prompt-cache reads/writes (the real bill of agent sessions) next to the in+out token count, per scenario and in totals, and name the models used. On an API key the dollars are what was billed; on a Claude subscription the headline is the 5-hour plan window before and after the run, with the dollars as the API-equivalent.
|
|
19
|
+
- **Your subscription, your choice of agent.** Recording and healing run on the plan already logged in on the machine: Claude (default), Codex on a ChatGPT plan, Google AI Pro/Ultra through the Antigravity CLI (experimental), or Cursor. Set `"provider"` in the config; each one runs through its vendor's official client, and every provider records through the same recorder, so the caches are identical in kind.
|
|
19
20
|
- **Trend memory.** Every run appends to `.saffron/history.jsonl`; reports show deltas vs the previous run and 20-run sparklines, and Saffron flags **chronic scenarios** (healing repeatedly. Re-record instead of paying again) plus recurring failure themes.
|
|
20
21
|
|
|
21
22
|
## Step reuse: new scenarios get cheaper as your suite grows
|
|
@@ -62,7 +63,7 @@ node examples/demo-app/server.mjs 4173 &
|
|
|
62
63
|
node dist/cli/index.js -p examples run
|
|
63
64
|
|
|
64
65
|
# delete a cache and watch the agent re-record it (needs Claude Code auth
|
|
65
|
-
# or ANTHROPIC_API_KEY)
|
|
66
|
+
# or ANTHROPIC_API_KEY, or another provider's login: see "provider" below)
|
|
66
67
|
rm examples/.saffron/cache/login/successful-login.json
|
|
67
68
|
node dist/cli/index.js -p examples run
|
|
68
69
|
node dist/cli/index.js -p examples accept --all
|
|
@@ -95,6 +96,7 @@ Configuration lives in `saffron.config.json` at your project root:
|
|
|
95
96
|
"pollIntervalMs": 100,
|
|
96
97
|
"retries": 1,
|
|
97
98
|
"screenshot": "viewport",
|
|
99
|
+
"provider": "claude",
|
|
98
100
|
"model": "claude-sonnet-5",
|
|
99
101
|
"maxTurns": 100,
|
|
100
102
|
"storageState": ".auth/state.json",
|
|
@@ -109,6 +111,15 @@ Configuration lives in `saffron.config.json` at your project root:
|
|
|
109
111
|
}
|
|
110
112
|
```
|
|
111
113
|
|
|
114
|
+
`provider` (also `--provider`) picks whose agent records and heals:
|
|
115
|
+
`claude` (default), `codex` (ChatGPT plan; `npm i -D @openai/codex-sdk`),
|
|
116
|
+
`antigravity` (Google AI Pro/Ultra through Google's `agy` CLI,
|
|
117
|
+
experimental; `npx saffron login antigravity` once) or `cursor`
|
|
118
|
+
(`npm i -D @cursor/sdk`, then `npx saffron login cursor`). `model` takes
|
|
119
|
+
that provider's model names. `npx saffron login` says what a provider
|
|
120
|
+
still needs on this machine. Every sign-in flow is in
|
|
121
|
+
[docs/PROVIDERS.md](docs/PROVIDERS.md).
|
|
122
|
+
|
|
112
123
|
`storageState` (also `--storage-state`) points to a Playwright storage-state
|
|
113
124
|
JSON (cookies + localStorage) applied to every scenario's browser context, so
|
|
114
125
|
cached replays *and* the agent start authenticated. Generate one with
|