synartesis 0.8.7 → 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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,148 @@
2
2
 
3
3
  What changed, and why it mattered. Dates are release dates.
4
4
 
5
+ ## 0.9.0 — 2026-09-24
6
+
7
+ 0.8.4 to 0.8.8 made Synartesis correct once you were using it. This release is
8
+ about the part before and around that: connecting what you already have without
9
+ breaking it, and being asked less once you have. Several bugs made someone who
10
+ followed the instructions worse off than someone who never installed it. Those
11
+ come first.
12
+
13
+ ### Fixed: wrapping a server no longer breaks it
14
+
15
+ - **Wrapping a server dropped its API keys.** `install` copied the client
16
+ entry's `env` onto the proxy's entry, and the proxy then started the real
17
+ server with almost none of it. Every server now starts through one path,
18
+ with the environment it was given. Existing installs are fixed without
19
+ re-running anything.
20
+ - **`undo` could not authenticate, and in one case undid the wrong store.** It
21
+ started servers with only what the policy declared, so a token held in the
22
+ client config was missing. For the memory server that was silent: without
23
+ `MEMORY_FILE_PATH` it opened its default file, sent the inverse there, and
24
+ reported success. `undo` now reads the environment and working directory from
25
+ the client entry that wraps the server. Each session records a keyed
26
+ fingerprint of every declared variable, never the value, and an undo whose
27
+ variables differ refuses and names them.
28
+ - **One server's missing token was everybody's problem.** `${VAR}` in a policy
29
+ was expanded for every server when the file was read, so undoing a filesystem
30
+ session failed unless the GitHub token was exported. It is expanded per
31
+ server, when that server starts.
32
+ - **A server update took GitHub down.** github-mcp-server 1.12.2 renamed the
33
+ tools the shipped policy read from, and the proxy refused to start. A rule
34
+ whose snapshot, inverse or verify tool has gone now falls back to being held,
35
+ and the proxy starts and says which rules it disabled. `check` still fails
36
+ hard. `github.yaml` is rewritten for 1.12.
37
+ - **`install` covers what it says.** It reads every Claude Code project, not
38
+ only the one it was run from (Claude Code's default scope is per project).
39
+ Running it again after `uninstall` covers the same server again instead of
40
+ duplicating it and then dropping it. Skip reasons are printed whole, where
41
+ "Please set SLACK_BOT_TOKEN" used to be cut off. It will not rewrite a Codex
42
+ table it could not fully read. `installed.json`, which holds the original
43
+ entries and their tokens, is now owner-only.
44
+ - **Bare `undo` and `show` skip empty sessions.** Most sessions record nothing;
45
+ bare `undo` refused on the empty one instead of finding the one with work.
46
+
47
+ ### Added: cover more of what people connect
48
+
49
+ - **Browsers.** Policies for Playwright MCP and Chrome DevTools MCP, written
50
+ from each server's real tool list. Reads are reads. Every interaction is
51
+ recorded, arguments included, and not held: nothing a browser does can be
52
+ undone, and holding every click made both unusable. Running agent-written
53
+ code and uploading files are still held.
54
+ - **A tool its server marks read-only is read as a read** when no rule mentions
55
+ it, instead of held. The MCP specification calls annotations untrusted unless
56
+ they come from a trusted server; the trust relied on is that you chose to
57
+ connect this one. It only ever loosens a hold on a read. It never applies on a
58
+ pinned server, `trust_annotations: false` turns it off, and `check` lists
59
+ every tool let through this way.
60
+ - **Hosted servers.** A server with a token in its headers is reached directly:
61
+ Streamable HTTP, falling back to SSE only where the server says it does not
62
+ speak the newer one, with redirects refused. `install` moves the header values
63
+ into the client entry's `env`, and the policy names the variables. A request
64
+ refused at the door, such as an expired token, is recorded as never sent. A
65
+ server that signs in through a browser is covered with `install --remote`,
66
+ through mcp-remote, which keeps the sign-in itself.
67
+ - **Four more clients:** Gemini CLI, GitHub Copilot CLI, Antigravity, and
68
+ Devin Desktop (what Windsurf became) plus the older Windsurf path. Every path
69
+ and variable syntax was checked against the vendor's own documentation.
70
+ - **Read-only policies** for fetch, Brave Search, Exa, Tavily and the AWS
71
+ documentation server. Each server was started and its tool list read first.
72
+
73
+ ### Added: ask less, tell the person
74
+
75
+ - **A desktop notification when a call is held**, on macOS and Linux. It names
76
+ the server and the tool, never the arguments. `synartesis notify --test`
77
+ checks it reaches you; `SYNARTESIS_NOTIFY=0` turns it off.
78
+ - **`synartesis allow <server.tool>`.** `--for 1h` stops holding that tool until
79
+ the time runs out, with no restart. `--always` edits the policy in place,
80
+ keeping its comments, and a tool that cannot be undone stays marked that way
81
+ (you type its name to confirm). `--stop` holds it again. `A` in `watch` and
82
+ the console, and a button on the desktop card, approve the call in front of
83
+ you and allow the tool for an hour.
84
+ - **A person's no reaches the agent.** A retry of a denied call is told who
85
+ said no, when and why, instead of opening a fresh hold. `approve` on the
86
+ denied call takes the denial back. Denials live in their own table, so a
87
+ spent approval, which is also stored as denied, is never reported as a refusal.
88
+ - **Why a call is held.** A tool with no rule says so, rather than "this action
89
+ cannot be undone".
90
+
91
+ ### Changed
92
+
93
+ - **The agent is never handed the command to approve its own call.** It used
94
+ to be in the refusal text and in the instructions every agent receives, and
95
+ an agent with a shell could run it, recorded under your name. `approve` and
96
+ `allow` now need a person at a terminal unless given `--unattended`, which is
97
+ recorded. This stops an agent following instructions or making a mistake. It
98
+ does not stop a process working to defeat it while running as you, which can
99
+ write the journal directly.
100
+ - **`show` and `undo` speak the same words as everything else.** No more
101
+ `reversible applied` or `result rolled_back` at the end of a dry run that
102
+ wrote nothing. A read-heavy session's reads fold into one line.
103
+ - **`install --print` starts nothing.** It promised to write nothing and then
104
+ started every server, which meant a download for each npx server and a
105
+ browser window for each sign-in. `install` also says which server it is
106
+ starting, instead of going quiet until the last one answers.
107
+ - `check` offers `pin` only once the policy has been used. The desktop window
108
+ points at `install`, not bare `init`. The `--http` server answers an ended
109
+ session with 404, as the MCP specification asks.
110
+ - A policy using `{{args.id}}` (loaded, then sent as those characters) or
111
+ `${args.id}` (failed with no line number) is refused with the line and the
112
+ spelling that works.
113
+
114
+ ### Docs
115
+
116
+ - The user guide's only complete policy example did not parse. It does now,
117
+ and a test loads every policy shown in the README, the guide and the install
118
+ page. The manual setup snippets give each server its own entry with
119
+ `--server`, and the Claude Code command has its `--`.
120
+
121
+ ## 0.8.8 — 2026-09-22
122
+
123
+ ### Fixed
124
+
125
+ - **The GitHub Models preset added in 0.8.7 could never have worked.** GitHub
126
+ retired its inference API on 30 July 2026, three weeks before that entry was
127
+ written. The host is still there and still answers -- `200 OK`, with
128
+ `content-type: text/plain` and a body of `OK`, whatever path you ask for --
129
+ which is exactly the shape of thing that looks alive until you try to use
130
+ it. The entry was built from blog posts and community threads, every one of
131
+ them written before the retirement, while the vendor's own documentation
132
+ said in plain words that the API was gone.
133
+
134
+ Removed, and replaced with **OpenRouter**: one key, many models, and
135
+ nineteen of the free ones currently advertise tool support, which is the
136
+ constraint that actually decides whether a model can drive this window at
137
+ all. Its catalogue is public, so which nineteen can be checked rather than
138
+ guessed.
139
+
140
+ Groq, added in the same release, was checked the same way and is fine.
141
+
142
+ The note now at the head of the preset list is the part worth keeping: send
143
+ a request to the host before adding one. A preset that does not work is
144
+ worse than no preset, because the person who picks it concludes this window
145
+ is broken rather than that the service is.
146
+
5
147
  ## 0.8.7 — 2026-09-22
6
148
 
7
149
  ### Added
package/README.md CHANGED
@@ -102,7 +102,8 @@ Then, from anywhere:
102
102
  synartesis install
103
103
  ```
104
104
 
105
- That finds what Claude Code, Claude Desktop, Cursor or Codex already list,
105
+ That finds what Claude Code, Claude Desktop, Cursor, Codex, Gemini CLI, Copilot
106
+ CLI, Antigravity or Devin Desktop (Windsurf) already list,
106
107
  writes one policy covering all of it, and points each entry at the proxy.
107
108
  Servers it recognises get the policy that ships for them and work immediately;
108
109
  the rest are drafted with every tool held until you say how to undo it. Your
@@ -219,12 +220,15 @@ Every tool gets one of four classifications, written down in a manifest:
219
220
  | `readonly` | Changes nothing | `get_customer` | Recorded, forwarded |
220
221
  | `reversible` | Prior state can be restored exactly | `update_customer` | State captured before the write; written back on undo |
221
222
  | `compensable` | Cannot be reversed, but can be offset | `create_charge` | A different call neutralises it |
222
- | `irreversible` | Neither | `send_email` | **Suspended until a human approves it** |
223
+ | `irreversible` | Neither | `send_email` | **Suspended until a human approves it**, or recorded without asking where the policy says `gate: never` |
223
224
 
224
225
  A tool your manifest does not mention is treated as `irreversible`. That is
225
226
  deliberate: silently forwarding an unknown destructive call is the one failure
226
- worth avoiding most. `synartesis check` names them, so you meet that decision
227
- before your agent does.
227
+ worth avoiding most. The one exception is a tool its own server marks
228
+ read-only: that is read as a read, and `check` lists every tool let through that
229
+ way, so you can see exactly what was trusted. (`trust_annotations: false` on a
230
+ server turns it off, and a pinned server never gets it.) `synartesis check`
231
+ names the rest, so you meet that decision before your agent does.
228
232
 
229
233
  A few calls are reversible only when nothing is in the way — moving a file onto
230
234
  a free path is undone by moving it back, moving it onto an existing file
@@ -235,6 +239,65 @@ than putting half of it back and calling that success.
235
239
 
236
240
  ---
237
241
 
242
+ ## Being asked less, and told when it matters
243
+
244
+ **You hear about a held call when it happens.** On macOS and Linux a desktop
245
+ notification names the server and the tool, never the arguments, since they can
246
+ carry secrets and notification history is kept. `synartesis notify --test`
247
+ checks it reaches you; `SYNARTESIS_NOTIFY=0` turns it off. Windows has none yet,
248
+ and `watch` shows every held call on every platform.
249
+
250
+ **Stop being asked, for a while or for good.**
251
+
252
+ ```bash
253
+ synartesis allow crm.send_email --for 1h # a row in the journal; next call on
254
+ synartesis allow crm.send_email --always # edits the policy; next client restart
255
+ synartesis allow crm.send_email --stop # held again from the next call
256
+ synartesis allow # what is let through right now
257
+ ```
258
+
259
+ `--for` goes up to a day and runs out by itself. `--always` changes only that
260
+ tool's gate, in place, keeping every comment in the file. A tool that cannot be
261
+ undone stays marked that way everywhere it shows up: it is recorded, just no
262
+ longer held. For those you type the tool's name to confirm. In `watch` and the
263
+ console, `A` approves the call in front of you and stops asking about that tool
264
+ for an hour. The desktop card has the same button.
265
+
266
+ **A no reaches the agent.** When you deny a call and the agent tries the same
267
+ call again, it is told who said no, when, and why. It does not see a fresh
268
+ "waiting for approval". `approve` on the denied call takes the denial back.
269
+
270
+ **The agent is never handed the way to approve itself.** Nothing it reads
271
+ contains an `approve` or `allow` command, and both refuse to run without a
272
+ person at a terminal unless given `--unattended`, which is recorded as such.
273
+
274
+ ---
275
+
276
+ ## Hosted servers and browsers
277
+
278
+ **Hosted servers.** A server your client reaches at a `url` with a token in its
279
+ headers is covered directly. `install` moves the header values into the
280
+ client entry's `env`, where the client already keeps secrets, and the policy
281
+ names the variables rather than holding the token. The proxy speaks Streamable
282
+ HTTP, falls back to SSE only where the server says it does not speak the newer
283
+ one, and refuses redirects. A request the server refuses at the door (an
284
+ expired token, say) is recorded as never sent, not as an outcome nobody knows.
285
+ A server that signs you in through a browser instead is covered with
286
+ `install --remote`, through [mcp-remote](https://www.npmjs.com/package/mcp-remote),
287
+ which does the sign-in and keeps the token itself.
288
+
289
+ **Browsers.** Nothing a browser does can be undone, and holding every click
290
+ made Playwright and Chrome DevTools unusable behind this. Their shipped policies
291
+ read reads as reads and record every interaction, arguments included, without
292
+ holding it. Running code the agent wrote, and uploading files from your disk,
293
+ are still held.
294
+
295
+ **Policies that ship:** filesystem, memory, git, github, Playwright, Chrome
296
+ DevTools, and a read-only set for fetch, Brave Search, Exa, Tavily and the AWS
297
+ documentation server. Each was written from the server's real tool list.
298
+
299
+ ---
300
+
238
301
  ## Writing a policy for your own server
239
302
 
240
303
  Four servers ship with a policy. For anything else — a database, a ticketing
@@ -407,6 +470,7 @@ get it if it is not installed.
407
470
  |---|---|
408
471
  | `synartesis` | The screen. Everything below can be done from it |
409
472
  | `install` / `uninstall` / `status` | Cover the clients on this machine, put them back, say what is covered |
473
+ | `install --remote` | Also cover hosted servers that sign in through a browser, via mcp-remote |
410
474
  | `init <server> -- <cmd>` | Introspect a server and draft a manifest |
411
475
  | `check` | Load a manifest and verify it against the servers it names |
412
476
  | `pin` | Print the `pins:` block for the servers you have now |
@@ -415,6 +479,8 @@ get it if it is not installed.
415
479
  | `show <id> --live` | The same, plus what has changed in the world since |
416
480
  | `show <id> --full` | Every argument, snapshot and inverse, nothing elided |
417
481
  | `gates` / `approve <id>` / `deny <id>` | What is waiting, and answering it |
482
+ | `allow <server.tool> --for 1h \| --always \| --stop` | Stop being asked about one tool, for a while or for good |
483
+ | `notify --test` | Send one notification, to see whether they reach you |
418
484
  | `resolve <id> --applied\|--failed` | Settle a call whose outcome nothing established |
419
485
  | `undo <id>` | Reverse a session, newest action first |
420
486
  | `undo <id> --dry-run` | Plan it and change nothing |
@@ -515,6 +581,14 @@ holding a call waiting on a person, or one whose undo halted on a conflict. A
515
581
  pruned session cannot be undone afterwards, which is the whole of the trade.
516
582
  Nothing prunes on a timer.
517
583
 
584
+ **What needing a terminal protects against.** `approve` and `allow` refuse to
585
+ run without a person at a terminal, and nothing an agent reads names them. That
586
+ stops an agent following its instructions, or making a mistake, into approving
587
+ its own calls. It does not stop a process working to defeat it while running as
588
+ you: anything with your permissions can write the journal directly. For a
589
+ boundary the agent cannot cross, deny it those commands in its own permission
590
+ settings.
591
+
518
592
  **Durability.** The journal runs `synchronous = NORMAL`. A crash of the process
519
593
  or of the CLI mid-undo loses nothing; only the machine losing power can cost the
520
594
  tail of the write-ahead log. `SYNARTESIS_SYNC=full` asks for an fsync per commit