@synapsor/runner 1.5.0 → 1.5.3
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 +92 -3
- package/README.md +96 -109
- package/dist/authoring.d.ts +23 -0
- package/dist/authoring.d.ts.map +1 -0
- package/dist/authoring.mjs +1318 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -5
- package/dist/local-ui.d.ts +14 -0
- package/dist/local-ui.d.ts.map +1 -1
- package/dist/runner.mjs +10845 -5598
- package/dist/runtime.mjs +29 -1
- package/dist/shadow.d.ts +36 -0
- package/dist/shadow.d.ts.map +1 -0
- package/dist/shadow.mjs +5262 -0
- package/docs/README.md +25 -2
- package/docs/alternatives.md +122 -0
- package/docs/capability-authoring.md +43 -0
- package/docs/client-recipes.md +218 -0
- package/docs/contract-testing.md +9 -7
- package/docs/cursor-plugin.md +78 -0
- package/docs/effect-regression.md +39 -2
- package/docs/fresh-developer-usability.md +110 -0
- package/docs/getting-started-own-database.md +96 -2
- package/docs/host-compatibility.md +59 -0
- package/docs/local-mode.md +6 -5
- package/docs/mcp-audit.md +166 -2
- package/docs/mcp-client-setup.md +4 -0
- package/docs/mcp-clients.md +33 -4
- package/docs/oss-vs-cloud.md +3 -0
- package/docs/release-notes.md +90 -4
- package/docs/security-boundary.md +20 -5
- package/docs/shadow-studies.md +47 -4
- package/docs/troubleshooting-first-run.md +46 -0
- package/examples/support-billing-agent/README.md +18 -0
- package/examples/support-billing-agent/app/README.md +6 -0
- package/examples/support-billing-agent/app/contract.ts +28 -0
- package/examples/support-billing-agent/app/effect-adapter.mjs +23 -0
- package/examples/support-billing-agent/app/record-shadow-outcomes.mjs +44 -0
- package/examples/support-billing-agent/scripts/run-evaluation.sh +6 -5
- package/examples/support-plan-credit/README.md +48 -2
- package/examples/support-plan-credit/mcp-client-examples/README.md +23 -0
- package/examples/support-plan-credit/mcp-client-examples/claude-code.sh +34 -0
- package/examples/support-plan-credit/mcp-client-examples/codex.config.toml +24 -0
- package/examples/support-plan-credit/mcp-client-examples/generic-stdio.mjs +35 -0
- package/examples/support-plan-credit/mcp-client-examples/generic-streamable-http.mjs +31 -0
- package/examples/support-plan-credit/mcp-client-examples/google-adk.py +40 -0
- package/examples/support-plan-credit/mcp-client-examples/langchain.mjs +29 -0
- package/examples/support-plan-credit/mcp-client-examples/llamaindex.py +36 -0
- package/examples/support-plan-credit/mcp-client-examples/openai-agents-stdio.ts +1 -1
- package/examples/support-plan-credit/mcp-client-examples/openai-agents-streamable-http.ts +1 -1
- package/examples/support-plan-credit/mcp-client-examples/vscode.mcp.json +20 -0
- package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.contract-tests.generated.json +221 -0
- package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.ts +34 -0
- package/fixtures/mcp-audit/README.md +14 -0
- package/fixtures/mcp-audit/cursor-bypass-config.json +38 -0
- package/fixtures/mcp-audit/dangerous-tools-list.json +33 -0
- package/fixtures/mcp-audit/reviewed-proposal-tools-list.json +60 -0
- package/package.json +18 -3
- package/schemas/mcp-audit-report.schema.json +100 -1
- package/schemas/synapsor.contract-tests.schema.json +1 -1
package/docs/release-notes.md
CHANGED
|
@@ -10,7 +10,93 @@ npx -y -p @synapsor/runner synapsor-runner demo --quick
|
|
|
10
10
|
The OSS runner command is `synapsor-runner`. The `synapsor` command is reserved
|
|
11
11
|
for the Synapsor Cloud CLI.
|
|
12
12
|
|
|
13
|
-
## 1.5.
|
|
13
|
+
## 1.5.3 (prepared, not published)
|
|
14
|
+
|
|
15
|
+
### Intent to Safe Action
|
|
16
|
+
|
|
17
|
+
- `start --action <name> --description <intent>` creates one inert TypeScript
|
|
18
|
+
Safe Action scaffold from an existing reviewed read boundary. Project-scoped
|
|
19
|
+
instructions let a coding agent complete and validate only the draft.
|
|
20
|
+
- `action validate` statically parses the restricted object and emits a
|
|
21
|
+
digest-addressed disabled canonical draft, explanation, and deterministic
|
|
22
|
+
allow/deny/effect tests. It does not import adopter code, activate the action,
|
|
23
|
+
or alter the active MCP tool catalog.
|
|
24
|
+
- The secured Workbench runs one real source-unchanged staging Data PR before
|
|
25
|
+
activation and requires `ACTIVATE` plus the complete digest. Cloud-linked
|
|
26
|
+
projects use governed Cloud activation. Existing proposals remain bound to
|
|
27
|
+
the exact active contract digest they were created under.
|
|
28
|
+
- The package includes a current-format Cursor plugin with
|
|
29
|
+
`/synapsor-protect`, diagnostics, deterministic package verification, and
|
|
30
|
+
project-safe MCP wiring. Cursor host claims remain evidence-labeled; a stable
|
|
31
|
+
UI pass and Marketplace submission are owner gates.
|
|
32
|
+
- MCP audit now renders a model-authority map and opt-in bypass evidence as
|
|
33
|
+
text, JSON, Markdown, or SARIF. A checked-in GitHub Actions workflow and Safe
|
|
34
|
+
Action team CI are deterministic and non-mutating.
|
|
35
|
+
- Proposal-only integration recipes cover Claude Code, Codex, VS Code, OpenAI
|
|
36
|
+
Agents, LangChain/LangGraph, Google ADK, LlamaIndex, and generic MCP clients.
|
|
37
|
+
- The first two README screens, public website, article, discovery routes, and
|
|
38
|
+
36-second evidence-backed demo now lead with the own-project Data PR path.
|
|
39
|
+
|
|
40
|
+
Prepared package version: `@synapsor/runner@1.5.3`. Spec, DSL, and Cloud CLI
|
|
41
|
+
packages are unchanged. Nothing has been published by this repository change.
|
|
42
|
+
|
|
43
|
+
## 1.5.2 (prepared, not published)
|
|
44
|
+
|
|
45
|
+
### First safe action in an existing application
|
|
46
|
+
|
|
47
|
+
- `start --from-env DATABASE_URL` and
|
|
48
|
+
`try --prove --from-env DATABASE_URL` now converge on one own-data
|
|
49
|
+
onboarding path. It inspects a staging database read-only, asks the developer
|
|
50
|
+
to choose trusted scope and one reviewed action, emits canonical files, and
|
|
51
|
+
never substitutes synthetic data after an own-data failure.
|
|
52
|
+
- The localhost workbench presents Project, Data source, Trust scope, Action,
|
|
53
|
+
Agent, Test, and Review plus an exact proposal Data PR. Test is complete only
|
|
54
|
+
after a scoped tool call records query audit, not after config validation.
|
|
55
|
+
- Project-scoped Cursor install/status/uninstall previews and owns only its MCP
|
|
56
|
+
entry, preserves other entries, and launches the exact Runner version.
|
|
57
|
+
Approval, apply, revert, credentials, and trusted identity stay outside MCP.
|
|
58
|
+
- Local activation reports measure proof, onboarding, Cursor, first read, and
|
|
59
|
+
first proposal without telemetry or business identifiers. Product time
|
|
60
|
+
excludes initial package download; cold `npx` time is reported separately
|
|
61
|
+
as an observed environment-specific measurement.
|
|
62
|
+
- Optional TypeScript authoring emits the same canonical Spec contract. Shadow
|
|
63
|
+
trust progression and provider-neutral effect regression remain
|
|
64
|
+
non-activating and preserve deterministic/external-model provenance.
|
|
65
|
+
- MCP audit candidates can open directly in the secured workbench, and the
|
|
66
|
+
release includes the host-compatibility matrix and support/billing reference
|
|
67
|
+
workflow.
|
|
68
|
+
- The package now requires Node 22.13.0 or newer, the first Node 22 release
|
|
69
|
+
where Runner's `node:sqlite` dependency is available without an experimental
|
|
70
|
+
runtime flag. Older Node versions fail immediately with an actionable error.
|
|
71
|
+
|
|
72
|
+
Prepared package version: `@synapsor/runner@1.5.2`. Spec, DSL, and Cloud CLI
|
|
73
|
+
packages are unchanged. Nothing has been published by this repository change.
|
|
74
|
+
|
|
75
|
+
## 1.5.1 (prepared, not published)
|
|
76
|
+
|
|
77
|
+
### Safe disposable state ownership
|
|
78
|
+
|
|
79
|
+
- `try --state-dir` no longer treats the supplied directory as disposable.
|
|
80
|
+
The supplied path is a caller-owned container and Runner uses a marked
|
|
81
|
+
managed child beneath it.
|
|
82
|
+
- Cleanup removes only known direct try-state files. Unrelated files remain in
|
|
83
|
+
place, and roots, home/cwd/repository paths, traversal, symlink escapes,
|
|
84
|
+
unmarked lookalikes, and linked managed files fail closed.
|
|
85
|
+
- An atomic state lease prevents concurrent runs from corrupting each other and
|
|
86
|
+
permits recovery from a valid lease whose process is no longer alive.
|
|
87
|
+
- `demo inspect --state-dir` resolves the same managed child. The default
|
|
88
|
+
`.synapsor/try` path safely adopts only the recognized legacy file layout.
|
|
89
|
+
- Explicit `--force` replacement of generated schema and MCP-audit candidate
|
|
90
|
+
directories now rejects protected paths, symlinked ancestors, and linked or
|
|
91
|
+
invalid ownership markers.
|
|
92
|
+
- The embedded `try` data source is consistently identified as synthetic.
|
|
93
|
+
- The packaged YAML parser is updated to `2.8.3` to include the upstream fix
|
|
94
|
+
for deeply nested collection denial of service.
|
|
95
|
+
|
|
96
|
+
Prepared package version: `@synapsor/runner@1.5.1`. Spec, DSL, and Cloud CLI
|
|
97
|
+
packages are unchanged. Nothing has been published by this repository change.
|
|
98
|
+
|
|
99
|
+
## 1.5.0 (published 2026-07-20)
|
|
14
100
|
|
|
15
101
|
### Complete guarded-action developer proof
|
|
16
102
|
|
|
@@ -45,8 +131,8 @@ for the Synapsor Cloud CLI.
|
|
|
45
131
|
immediately second, and distinguishes application scope, PostgreSQL RLS, and
|
|
46
132
|
tenant-bound isolation without overstating any of them.
|
|
47
133
|
|
|
48
|
-
|
|
49
|
-
packages
|
|
134
|
+
Published package version: `@synapsor/runner@1.5.0`. Spec, DSL, and Cloud CLI
|
|
135
|
+
packages were unchanged.
|
|
50
136
|
|
|
51
137
|
## 1.4.123 (2026-07-17)
|
|
52
138
|
|
|
@@ -744,7 +830,7 @@ not change and must not be republished for this release.
|
|
|
744
830
|
CDC, managed runner fleets, compliance exports, production SLA, or C++ DBMS
|
|
745
831
|
internals.
|
|
746
832
|
- The local store is single-node SQLite for local/dev/staging usage.
|
|
747
|
-
- Node >= 22.
|
|
833
|
+
- Node >= 22.13.0 is required because the local ledger uses Node's unflagged
|
|
748
834
|
`node:sqlite` runtime. Use a supported Node runtime or the Docker-backed
|
|
749
835
|
source demo path.
|
|
750
836
|
|
|
@@ -36,6 +36,7 @@ The model does not receive:
|
|
|
36
36
|
- a generic `execute_sql` tool;
|
|
37
37
|
- arbitrary table, schema, or column names;
|
|
38
38
|
- database URLs or credentials;
|
|
39
|
+
- contract activation tools;
|
|
39
40
|
- approval tools;
|
|
40
41
|
- commit/writeback tools;
|
|
41
42
|
- trusted tenant or principal authority as ordinary model arguments.
|
|
@@ -84,6 +85,18 @@ explicit host-level administrative boundary.
|
|
|
84
85
|
Proposal tools read the current row through the read credential, store evidence
|
|
85
86
|
and an exact before/after diff, and leave the source database unchanged.
|
|
86
87
|
|
|
88
|
+
Code-first Safe Actions do not create authority when a file is edited. Runner
|
|
89
|
+
parses the restricted TypeScript object without importing or executing it,
|
|
90
|
+
compiles it into a digest-addressed disabled canonical draft, and keeps the
|
|
91
|
+
active contract pointer unchanged. Activation is available only in the
|
|
92
|
+
session-token/CSRF-protected localhost Workbench after a matching real staging
|
|
93
|
+
proposal proves `source_database_changed:false`; the operator must confirm the
|
|
94
|
+
complete digest. A proposal remains pinned to the active contract digest it was
|
|
95
|
+
created under, so later activation cannot reinterpret an old approval. Cloud-
|
|
96
|
+
linked projects route activation through Cloud governance instead of the local
|
|
97
|
+
control. No model-facing MCP tool can validate-and-activate, approve, apply, or
|
|
98
|
+
revert.
|
|
99
|
+
|
|
87
100
|
The local proposal store rejects obvious credential material before persistence:
|
|
88
101
|
database URLs, bearer tokens, Synapsor runner tokens, private-key blocks, and
|
|
89
102
|
secret-like fields such as password, token, API key, private key, cookie,
|
|
@@ -145,11 +158,13 @@ database identifiers such as `../private`, `id/../../tenant_id`, or
|
|
|
145
158
|
`status; DROP TABLE tickets` before adapter execution. Local CLI file paths
|
|
146
159
|
remain explicit user-provided paths; they are not model-facing authority.
|
|
147
160
|
|
|
148
|
-
Local review can happen through the CLI or `synapsor-runner ui`. The UI is a
|
|
149
|
-
review surface with a per-run session token and CSRF protection for
|
|
150
|
-
approve/reject
|
|
151
|
-
|
|
152
|
-
|
|
161
|
+
Local review can happen through the CLI or `synapsor-runner ui`. The UI is a
|
|
162
|
+
localhost review surface with a per-run session token and CSRF protection for
|
|
163
|
+
approve/reject and Safe Action draft controls. Activation additionally requires
|
|
164
|
+
a matching source-unchanged preview plus the complete digest. These operator
|
|
165
|
+
controls are not MCP tools. The UI does not expose raw SQL, database URLs, or
|
|
166
|
+
write credentials, and it does not let a model widen reviewed tables, columns,
|
|
167
|
+
scope, policy, or executor authority.
|
|
153
168
|
|
|
154
169
|
Contract lint and tests are review aids rather than a proof of complete
|
|
155
170
|
security. Capability breadth can still drift as narrow tools accumulate;
|
package/docs/shadow-studies.md
CHANGED
|
@@ -142,6 +142,41 @@ synapsor-runner shadow outcome import \
|
|
|
142
142
|
--store ./.synapsor/local.db
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
+
For the normal application path, record the trusted outcome directly instead
|
|
146
|
+
of assembling JSONL. `@synapsor/runner/shadow` writes through the same scope,
|
|
147
|
+
effect-shape, and secret checks as the import command:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
import { createShadowOutcomeRecorder } from "@synapsor/runner/shadow";
|
|
151
|
+
|
|
152
|
+
const recorder = createShadowOutcomeRecorder({
|
|
153
|
+
storePath: "./.synapsor/local.db",
|
|
154
|
+
studyId: "sst_support_pilot",
|
|
155
|
+
actor: "support_application",
|
|
156
|
+
source: "support-audit-log",
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
recorder.record({
|
|
161
|
+
requestId: "request_123",
|
|
162
|
+
tenantId: "acme",
|
|
163
|
+
businessObject: "invoice",
|
|
164
|
+
objectId: "INV-3001",
|
|
165
|
+
disposition: "applied",
|
|
166
|
+
actualEffect: {
|
|
167
|
+
before: { late_fee_cents: 5500 },
|
|
168
|
+
after: { late_fee_cents: 0 },
|
|
169
|
+
patch: { late_fee_cents: 0 },
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
} finally {
|
|
173
|
+
recorder.close();
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The application owns the truth of this outcome. The helper does not read or
|
|
178
|
+
mutate the source database and cannot approve a shadow proposal.
|
|
179
|
+
|
|
145
180
|
## Review The Report
|
|
146
181
|
|
|
147
182
|
```bash
|
|
@@ -161,6 +196,16 @@ denial, unable-to-propose, stale/conflict, unmatched, and unsafe-scope cases.
|
|
|
161
196
|
It also includes amount distributions, capability/reason breakdowns, and
|
|
162
197
|
risk-ranked disagreements.
|
|
163
198
|
|
|
199
|
+
The report also shows the explicit trust progression:
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
Observe -> Compare -> Manual review -> Suggested bounded policy
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
It reports an insufficient sample size until the minimum comparable evidence
|
|
206
|
+
exists. A suggested policy remains an inactive review artifact; the report
|
|
207
|
+
never widens or activates the contract.
|
|
208
|
+
|
|
164
209
|
Close a completed study:
|
|
165
210
|
|
|
166
211
|
```bash
|
|
@@ -187,10 +232,8 @@ synapsor-runner shadow case import \
|
|
|
187
232
|
--input ./examples/support-billing-agent/shadow-study/cases.jsonl \
|
|
188
233
|
--store ./.synapsor/shadow-reference.db
|
|
189
234
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
--input ./examples/support-billing-agent/shadow-study/outcomes.jsonl \
|
|
193
|
-
--store ./.synapsor/shadow-reference.db
|
|
235
|
+
node ./examples/support-billing-agent/app/record-shadow-outcomes.mjs \
|
|
236
|
+
./.synapsor/shadow-reference.db sst_support_reference
|
|
194
237
|
|
|
195
238
|
synapsor-runner shadow report \
|
|
196
239
|
--study sst_support_reference \
|
|
@@ -12,6 +12,52 @@ Use JSON for automation:
|
|
|
12
12
|
npx -y -p @synapsor/runner synapsor-runner doctor --first-run --json
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Safe Action Draft Does Not Appear As A Tool
|
|
16
|
+
|
|
17
|
+
This is expected before activation. `start --action`, agent edits, `action
|
|
18
|
+
validate`, and `action watch` can create or refresh only a disabled draft. They
|
|
19
|
+
must not alter the active model-facing tools.
|
|
20
|
+
|
|
21
|
+
Check status without exposing credentials:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
synapsor-runner action status --json
|
|
25
|
+
synapsor-runner tools preview \
|
|
26
|
+
--config ./synapsor.runner.json \
|
|
27
|
+
--store ./.synapsor/local.db
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Open the secured Workbench, run the real source-unchanged staging Data PR
|
|
31
|
+
preview, review the complete digest, and activate it there. There is
|
|
32
|
+
intentionally no activation CLI command to hand to a coding agent.
|
|
33
|
+
|
|
34
|
+
## Activated Tool Does Not Appear In Cursor
|
|
35
|
+
|
|
36
|
+
Some MCP hosts do not refresh `tools/list` for a running stdio session. First
|
|
37
|
+
confirm that Runner's active tool surface changed:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
synapsor-runner action status --json
|
|
41
|
+
synapsor-runner mcp status cursor --project --check-launch
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then reconnect or restart the project MCP server as directed by the Workbench.
|
|
45
|
+
Do not work around a stale host session by adding approval, apply, activation,
|
|
46
|
+
credentials, tenant values, or raw SQL to the Cursor configuration.
|
|
47
|
+
|
|
48
|
+
## Safe Action Validation Reports Review Placeholders
|
|
49
|
+
|
|
50
|
+
The composer fails closed while any `__REVIEW_*__` authority placeholder or
|
|
51
|
+
dynamic TypeScript expression remains. Review the reported field and source,
|
|
52
|
+
then make the authority explicit in the restricted `defineCapability({...})`
|
|
53
|
+
object. Runner will not infer trusted tenant/principal bindings, hidden fields,
|
|
54
|
+
write columns, bounds, conflict guards, approval, or executor authority from
|
|
55
|
+
application code.
|
|
56
|
+
|
|
57
|
+
Use the generated explanation and test manifest after validation. Do not edit
|
|
58
|
+
digest-addressed files under `.synapsor/drafts/` or `.synapsor/active/`; edit
|
|
59
|
+
the TypeScript source and validate again.
|
|
60
|
+
|
|
15
61
|
## Smoke Proposal Missing From Another Runner
|
|
16
62
|
|
|
17
63
|
What happened:
|
|
@@ -63,6 +63,21 @@ The demo proves:
|
|
|
63
63
|
- the shadow report compares cases with authoritative human outcomes before the
|
|
64
64
|
effect fixture checks the expected business change.
|
|
65
65
|
|
|
66
|
+
The app-owned integration files make those two evaluation paths reusable:
|
|
67
|
+
|
|
68
|
+
- `app/contract.ts` is the code-first TypeScript form of the reviewed late-fee
|
|
69
|
+
action and compiles to the same canonical spec as JSON/DSL;
|
|
70
|
+
- `app/record-shadow-outcomes.mjs` records authoritative app outcomes through
|
|
71
|
+
`@synapsor/runner/shadow` without approval or source mutation;
|
|
72
|
+
- `app/effect-adapter.mjs` emits one deterministic, propose-only result through
|
|
73
|
+
the provider-neutral command adapter;
|
|
74
|
+
- `.github/workflows/effect-regression.yml` runs the effect check and uploads
|
|
75
|
+
JUnit output for relevant pull requests.
|
|
76
|
+
|
|
77
|
+
This repository result is deterministic application evidence, not an external
|
|
78
|
+
model benchmark. Use `--result-origin external-model` only when your adapter
|
|
79
|
+
actually invokes a model.
|
|
80
|
+
|
|
66
81
|
RLS is defense in depth, not a claim that a fully compromised Runner process is
|
|
67
82
|
contained. A process that can choose arbitrary trusted context or replace the
|
|
68
83
|
database credential remains outside this guarantee. Production deployments
|
|
@@ -107,6 +122,9 @@ Relevant files:
|
|
|
107
122
|
- `scripts/run-demo.sh`
|
|
108
123
|
- `scripts/run-evaluation.sh`
|
|
109
124
|
- `app/README.md`
|
|
125
|
+
- `app/contract.ts`
|
|
126
|
+
- `app/record-shadow-outcomes.mjs`
|
|
127
|
+
- `app/effect-adapter.mjs`
|
|
110
128
|
- `shadow-study/cases.jsonl` and `shadow-study/outcomes.jsonl`: deterministic
|
|
111
129
|
true-shadow reference data for agent-versus-authoritative-outcome reports.
|
|
112
130
|
|
|
@@ -11,3 +11,9 @@ writeback with tenant, primary-key, allowed-column, idempotency, and
|
|
|
11
11
|
For richer app actions, such as inserting a credit ledger row and emitting an
|
|
12
12
|
event, use the app-owned handler examples in `../app-owned-writeback/` and
|
|
13
13
|
`../mcp-postgres-billing-app-handler/`.
|
|
14
|
+
|
|
15
|
+
`contract.ts` demonstrates the optional typed authoring frontend without
|
|
16
|
+
changing the canonical contract. `record-shadow-outcomes.mjs` is the normal-app
|
|
17
|
+
bridge for authoritative human outcomes. `effect-adapter.mjs` is a deterministic
|
|
18
|
+
propose-only CI adapter; it receives a bounded fixture path and emits one
|
|
19
|
+
canonical result. Neither helper approves, applies, or mutates the source.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineCapability } from "@synapsor/runner/authoring";
|
|
2
|
+
|
|
3
|
+
export const waiveLateFee = defineCapability({
|
|
4
|
+
name: "billing.propose_late_fee_waiver",
|
|
5
|
+
kind: "proposal",
|
|
6
|
+
context: "local_operator",
|
|
7
|
+
source: "app_postgres",
|
|
8
|
+
subject: { resource: "billing.invoice", primary_key: "id", tenant_key: "tenant_id" },
|
|
9
|
+
args: {
|
|
10
|
+
invoice_id: { type: "string", required: true, max_length: 128 },
|
|
11
|
+
reason: { type: "string", required: true, max_length: 500 },
|
|
12
|
+
},
|
|
13
|
+
lookup: { id_from_arg: "invoice_id" },
|
|
14
|
+
visible_fields: ["id", "tenant_id", "late_fee_cents", "waiver_reason", "updated_at"],
|
|
15
|
+
kept_out_fields: ["card_token", "internal_risk_note"],
|
|
16
|
+
evidence: { required: true, query_audit: true },
|
|
17
|
+
max_rows: 1,
|
|
18
|
+
proposal: {
|
|
19
|
+
action: "waive_late_fee",
|
|
20
|
+
operation: { kind: "update" },
|
|
21
|
+
allowed_fields: ["late_fee_cents", "waiver_reason"],
|
|
22
|
+
patch: { late_fee_cents: { fixed: 0 }, waiver_reason: { from_arg: "reason" } },
|
|
23
|
+
numeric_bounds: { late_fee_cents: { minimum: 0, maximum: 10000 } },
|
|
24
|
+
conflict_guard: { column: "updated_at" },
|
|
25
|
+
approval: { mode: "human", required_role: "billing_lead" },
|
|
26
|
+
writeback: { mode: "direct_sql" },
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
|
|
3
|
+
if (process.env.SYNAPSOR_EFFECT_MODE !== "propose_only") {
|
|
4
|
+
throw new Error("reference effect adapter requires SYNAPSOR_EFFECT_MODE=propose_only");
|
|
5
|
+
}
|
|
6
|
+
if (process.env.DATABASE_URL || process.env.SYNAPSOR_DATABASE_WRITE_URL) {
|
|
7
|
+
throw new Error("reference effect adapter must not receive database credentials");
|
|
8
|
+
}
|
|
9
|
+
const fixturePath = process.env.SYNAPSOR_EFFECT_FIXTURE_PATH;
|
|
10
|
+
if (!fixturePath) throw new Error("SYNAPSOR_EFFECT_FIXTURE_PATH is required");
|
|
11
|
+
const fixture = JSON.parse(fs.readFileSync(fixturePath, "utf8"));
|
|
12
|
+
const expected = fixture.expected;
|
|
13
|
+
|
|
14
|
+
process.stdout.write(`${JSON.stringify({
|
|
15
|
+
schema_version: "synapsor.effect-result.v1",
|
|
16
|
+
fixture_id: fixture.fixture_id,
|
|
17
|
+
capability_calls: expected.capability_calls.map((name) => ({ name })),
|
|
18
|
+
trusted_context: expected.trusted_context,
|
|
19
|
+
proposal: expected.proposal,
|
|
20
|
+
observed_fields: Object.keys(expected.proposal.diff ?? {}).sort(),
|
|
21
|
+
evidence: { mode: "fixture", new_source_reads: false },
|
|
22
|
+
source_database_changed: false,
|
|
23
|
+
})}\n`);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createShadowOutcomeRecorder } from "@synapsor/runner/shadow";
|
|
2
|
+
|
|
3
|
+
const [storePath, studyId] = process.argv.slice(2);
|
|
4
|
+
if (!storePath || !studyId) {
|
|
5
|
+
throw new Error("usage: node record-shadow-outcomes.mjs <store-path> <study-id>");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const recorder = createShadowOutcomeRecorder({
|
|
9
|
+
storePath,
|
|
10
|
+
studyId,
|
|
11
|
+
actor: "support_reference_app",
|
|
12
|
+
source: "support-app-audit",
|
|
13
|
+
});
|
|
14
|
+
try {
|
|
15
|
+
recorder.record({
|
|
16
|
+
requestId: "req-waiver-exact",
|
|
17
|
+
tenantId: "acme",
|
|
18
|
+
businessObject: "invoice",
|
|
19
|
+
objectId: "INV-3001",
|
|
20
|
+
disposition: "applied",
|
|
21
|
+
actualEffect: {
|
|
22
|
+
before: { late_fee_cents: 5500, waiver_reason: null },
|
|
23
|
+
after: { late_fee_cents: 0, waiver_reason: "courtesy waiver" },
|
|
24
|
+
patch: { late_fee_cents: 0, waiver_reason: "courtesy waiver" },
|
|
25
|
+
},
|
|
26
|
+
occurredAt: "2026-07-19T11:00:00.000Z",
|
|
27
|
+
reference: "ticket:SUP-184",
|
|
28
|
+
reason: "customer qualified",
|
|
29
|
+
});
|
|
30
|
+
recorder.record({
|
|
31
|
+
requestId: "req-waiver-rejected",
|
|
32
|
+
tenantId: "acme",
|
|
33
|
+
businessObject: "invoice",
|
|
34
|
+
objectId: "INV-REJECTED",
|
|
35
|
+
disposition: "rejected_no_action",
|
|
36
|
+
occurredAt: "2026-07-19T11:01:00.000Z",
|
|
37
|
+
reference: "ticket:SUP-REJECTED",
|
|
38
|
+
reason: "customer already received a courtesy waiver",
|
|
39
|
+
});
|
|
40
|
+
} finally {
|
|
41
|
+
recorder.close();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
process.stdout.write("Recorded 2 authoritative outcomes through @synapsor/runner/shadow.\n");
|
|
@@ -23,10 +23,9 @@ corepack pnpm runner shadow case import \
|
|
|
23
23
|
--input "$ROOT_DIR/examples/support-billing-agent/shadow-study/cases.jsonl" \
|
|
24
24
|
--store "$STORE_PATH" >/dev/null
|
|
25
25
|
|
|
26
|
-
corepack pnpm runner
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
--store "$STORE_PATH" >/dev/null
|
|
26
|
+
corepack pnpm build:runner-package >/dev/null
|
|
27
|
+
node "$ROOT_DIR/examples/support-billing-agent/app/record-shadow-outcomes.mjs" \
|
|
28
|
+
"$STORE_PATH" sst_support_reference >/dev/null
|
|
30
29
|
|
|
31
30
|
corepack pnpm runner shadow report \
|
|
32
31
|
--study sst_support_reference \
|
|
@@ -54,7 +53,9 @@ NODE
|
|
|
54
53
|
|
|
55
54
|
corepack pnpm runner effect run \
|
|
56
55
|
--dataset "$ROOT_DIR/fixtures/effects/dataset.json" \
|
|
57
|
-
--
|
|
56
|
+
--adapter node \
|
|
57
|
+
--adapter-arg "$ROOT_DIR/examples/support-billing-agent/app/effect-adapter.mjs" \
|
|
58
|
+
--result-origin deterministic-application \
|
|
58
59
|
--format json \
|
|
59
60
|
--out "$EFFECT_PATH" >/dev/null
|
|
60
61
|
|
|
@@ -15,7 +15,7 @@ policy-tier demonstration.
|
|
|
15
15
|
## Prerequisites
|
|
16
16
|
|
|
17
17
|
- Docker
|
|
18
|
-
- Node 22.
|
|
18
|
+
- Node 22.13+
|
|
19
19
|
- Port `55438` available
|
|
20
20
|
|
|
21
21
|
Install the stable CLI once for the commands below:
|
|
@@ -50,6 +50,42 @@ network connection or connecting to a database. From a source checkout,
|
|
|
50
50
|
`corepack pnpm verify:adoption` runs this boundary check plus the no-database
|
|
51
51
|
quick demo and MCP config validation.
|
|
52
52
|
|
|
53
|
+
## Clone And Validate The Same Safe Action
|
|
54
|
+
|
|
55
|
+
The checked-in code-first action is
|
|
56
|
+
[`synapsor/actions/support.propose_plan_credit.ts`](synapsor/actions/support.propose_plan_credit.ts).
|
|
57
|
+
It is a reviewable frontend for the same canonical JSON contract, not another
|
|
58
|
+
runtime format. Editing it cannot change active tools.
|
|
59
|
+
|
|
60
|
+
After cloning the repository and installing dependencies, another developer can
|
|
61
|
+
run the deterministic team gate without a database or Cloud account:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
corepack pnpm install --frozen-lockfile
|
|
65
|
+
corepack pnpm verify:safe-action-team
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The verifier copies this example to a disposable directory, compiles the action
|
|
69
|
+
only as a disabled draft, compares the regenerated boundary tests with the
|
|
70
|
+
checked-in file, proves the config and active contract are unchanged, and emits
|
|
71
|
+
text, JSON, and JUnit reports under `tmp/safe-action-team-ci/`. Its static gate
|
|
72
|
+
covers the exact proposal effect, trusted scope, evidence requirement, approval
|
|
73
|
+
policy, operator separation, kept-out fields, argument bounds, and conflict
|
|
74
|
+
guard. It performs no approval, activation, source read, or source mutation.
|
|
75
|
+
|
|
76
|
+
The same gate runs in
|
|
77
|
+
[`safe-action-ci.yml`](https://github.com/Synapsor/Synapsor-Runner/blob/main/.github/workflows/safe-action-ci.yml).
|
|
78
|
+
CI adds a disposable PostgreSQL service and runs the generated allowed-effect,
|
|
79
|
+
cross-tenant-denial, and source-unchanged assertions. It uses an amount above
|
|
80
|
+
the auto-approval threshold, performs no approval or apply, and requires source
|
|
81
|
+
rows and source receipt state to remain equivalent before and after the live
|
|
82
|
+
suite.
|
|
83
|
+
For a real staging preview, each developer supplies their own
|
|
84
|
+
`PLAN_CREDIT_POSTGRES_READ_URL`, `PLAN_CREDIT_POSTGRES_WRITE_URL`,
|
|
85
|
+
`SYNAPSOR_TENANT_ID`, and `SYNAPSOR_PRINCIPAL` outside Git, then follows the
|
|
86
|
+
Quick Start below. Cursor receives the same proposal-only tools; activation,
|
|
87
|
+
approval, and apply remain outside the agent.
|
|
88
|
+
|
|
53
89
|
## Quick Start
|
|
54
90
|
|
|
55
91
|
```bash
|
|
@@ -199,9 +235,19 @@ shape without guessing which state should change.
|
|
|
199
235
|
Copy the matching template from [`mcp-client-examples/`](mcp-client-examples/):
|
|
200
236
|
|
|
201
237
|
- Claude Desktop: `claude-desktop.json`
|
|
238
|
+
- Claude Code: `claude-code.sh`
|
|
239
|
+
- Codex: `codex.config.toml`
|
|
202
240
|
- Cursor project/global: `cursor-project.mcp.json`, `cursor-global.mcp.json`
|
|
241
|
+
- VS Code: `vscode.mcp.json`
|
|
203
242
|
- OpenAI Agents SDK: stdio and Streamable HTTP TypeScript examples
|
|
204
|
-
-
|
|
243
|
+
- LangChain/LangGraph: `langchain.mjs`
|
|
244
|
+
- Google ADK: `google-adk.py`
|
|
245
|
+
- LlamaIndex: `llamaindex.py`
|
|
246
|
+
- generic MCP: stdio and Streamable HTTP JSON and JavaScript examples
|
|
247
|
+
|
|
248
|
+
See [Client And Framework Recipes](../../docs/client-recipes.md) for current
|
|
249
|
+
official references, exact proposal input, evidence labels, and the mechanical
|
|
250
|
+
verification gate.
|
|
205
251
|
|
|
206
252
|
For Streamable HTTP, run:
|
|
207
253
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Support Plan Credit MCP Recipes
|
|
2
|
+
|
|
3
|
+
These files connect different MCP hosts and frameworks to the same reviewed
|
|
4
|
+
Runner tools:
|
|
5
|
+
|
|
6
|
+
- `support.inspect_customer`
|
|
7
|
+
- `support.propose_plan_credit`
|
|
8
|
+
|
|
9
|
+
Every proposal example uses:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"customer_id": "CUS-3001",
|
|
14
|
+
"credit_cents": 2500,
|
|
15
|
+
"reason": "SLA outage ticket SUP-481"
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The expected call result has `source_database_changed: false`. Human review,
|
|
20
|
+
approval, and apply stay outside every model-facing recipe.
|
|
21
|
+
|
|
22
|
+
See [Client And Framework Recipes](../../../docs/client-recipes.md) for setup,
|
|
23
|
+
evidence labels, official references, and verification commands.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Run from the repository root. Credentials and trusted scope are inherited
|
|
5
|
+
# from the shell that starts Claude Code; they are not written to .mcp.json.
|
|
6
|
+
claude mcp add-json --scope project synapsor "$(cat <<'JSON'
|
|
7
|
+
{
|
|
8
|
+
"type": "stdio",
|
|
9
|
+
"command": "npx",
|
|
10
|
+
"args": [
|
|
11
|
+
"-y",
|
|
12
|
+
"-p",
|
|
13
|
+
"@synapsor/runner",
|
|
14
|
+
"synapsor-runner",
|
|
15
|
+
"mcp",
|
|
16
|
+
"serve",
|
|
17
|
+
"--config",
|
|
18
|
+
"./examples/support-plan-credit/synapsor.runner.json",
|
|
19
|
+
"--store",
|
|
20
|
+
"./tmp/support-plan-credit/local.db"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
JSON
|
|
24
|
+
)"
|
|
25
|
+
|
|
26
|
+
claude mcp get synapsor
|
|
27
|
+
|
|
28
|
+
cat <<'PROMPT'
|
|
29
|
+
List the Synapsor MCP tools. Confirm there is no SQL, approval, apply, commit,
|
|
30
|
+
activation, or revert tool. Then call support.propose_plan_credit with
|
|
31
|
+
customer_id CUS-3001, credit_cents 2500, and reason "SLA outage ticket
|
|
32
|
+
SUP-481". Show the exact proposal effect and confirm source_database_changed is
|
|
33
|
+
false. Stop for human review outside Claude Code.
|
|
34
|
+
PROMPT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Merge this table into a trusted project's .codex/config.toml and start Codex
|
|
2
|
+
# from the repository root. Credentials and trusted scope remain inherited
|
|
3
|
+
# environment variables; do not add their values here.
|
|
4
|
+
[mcp_servers.synapsor]
|
|
5
|
+
command = "npx"
|
|
6
|
+
args = [
|
|
7
|
+
"-y",
|
|
8
|
+
"-p",
|
|
9
|
+
"@synapsor/runner",
|
|
10
|
+
"synapsor-runner",
|
|
11
|
+
"mcp",
|
|
12
|
+
"serve",
|
|
13
|
+
"--config",
|
|
14
|
+
"./examples/support-plan-credit/synapsor.runner.json",
|
|
15
|
+
"--store",
|
|
16
|
+
"./tmp/support-plan-credit/local.db",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
# Prompt:
|
|
20
|
+
# List the Synapsor tools, then call support.propose_plan_credit with
|
|
21
|
+
# customer_id CUS-3001, credit_cents 2500, and reason "SLA outage ticket
|
|
22
|
+
# SUP-481". Confirm source_database_changed is false and stop for human review
|
|
23
|
+
# outside Codex. Never request an approval, apply, commit, activation, or revert
|
|
24
|
+
# MCP tool.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
+
|
|
4
|
+
const transport = new StdioClientTransport({
|
|
5
|
+
command: "npx",
|
|
6
|
+
args: [
|
|
7
|
+
"-y", "-p", "@synapsor/runner", "synapsor-runner", "mcp", "serve",
|
|
8
|
+
"--config", "./examples/support-plan-credit/synapsor.runner.json",
|
|
9
|
+
"--store", "./tmp/support-plan-credit/local.db",
|
|
10
|
+
],
|
|
11
|
+
env: process.env,
|
|
12
|
+
stderr: "inherit",
|
|
13
|
+
});
|
|
14
|
+
const client = new Client({ name: "synapsor-generic-stdio", version: "1.0.0" });
|
|
15
|
+
|
|
16
|
+
await client.connect(transport);
|
|
17
|
+
try {
|
|
18
|
+
const listed = await client.listTools();
|
|
19
|
+
const names = listed.tools.map((tool) => tool.name);
|
|
20
|
+
const forbidden = names.filter((name) => /sql|approve|apply|commit|activate|revert/i.test(name));
|
|
21
|
+
if (forbidden.length) throw new Error(`unsafe model-facing tools: ${forbidden.join(", ")}`);
|
|
22
|
+
|
|
23
|
+
const result = await client.callTool({
|
|
24
|
+
name: "support.propose_plan_credit",
|
|
25
|
+
arguments: {
|
|
26
|
+
customer_id: "CUS-3001",
|
|
27
|
+
credit_cents: 2500,
|
|
28
|
+
reason: "SLA outage ticket SUP-481",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
console.log(JSON.stringify(result, null, 2));
|
|
32
|
+
console.error("Proposal only. Review and apply outside the model-facing MCP client.");
|
|
33
|
+
} finally {
|
|
34
|
+
await client.close();
|
|
35
|
+
}
|