@synapsor/runner 0.1.8 → 0.1.10
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 +20 -0
- package/README.md +11 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/runner.mjs +468 -29
- package/docs/local-mode.md +4 -3
- package/docs/release-notes.md +9 -0
- package/examples/support-plan-credit/README.md +176 -0
- package/examples/support-plan-credit/contract.synapsor +59 -0
- package/examples/support-plan-credit/docker-compose.yml +12 -0
- package/examples/support-plan-credit/seed/001_seed.sql +92 -0
- package/examples/support-plan-credit/synapsor.contract.json +198 -0
- package/examples/support-plan-credit/synapsor.runner.json +18 -0
- package/package.json +2 -1
package/docs/local-mode.md
CHANGED
|
@@ -345,7 +345,7 @@ For CI or direct verification, use:
|
|
|
345
345
|
corepack pnpm test:mcp-local
|
|
346
346
|
```
|
|
347
347
|
|
|
348
|
-
It launches the official MCP stdio client transport against `synapsor-runner mcp serve`, exercises the Postgres billing, Postgres support,
|
|
348
|
+
It launches the official MCP stdio client transport against `synapsor-runner mcp serve`, exercises the Postgres billing, Postgres support, MySQL orders, and support plan-credit examples, checks that source rows are unchanged before approval, approves locally, generates versioned writeback jobs, applies them, retries idempotently, and then proves stale-row conflict:
|
|
349
349
|
|
|
350
350
|
```text
|
|
351
351
|
The business state changed after the agent saw it, so Synapsor refused to commit.
|
|
@@ -360,14 +360,15 @@ corepack pnpm test:live-apply
|
|
|
360
360
|
Prerequisites:
|
|
361
361
|
|
|
362
362
|
- Docker daemon running;
|
|
363
|
-
- local ports `55433`, `55434`, and `53307` available;
|
|
363
|
+
- local ports `55433`, `55434`, `55438`, and `53307` available;
|
|
364
364
|
- no Synapsor Cloud account, API key, hosted workspace, or production database.
|
|
365
365
|
|
|
366
366
|
Expected output:
|
|
367
367
|
|
|
368
|
-
- disposable Postgres billing, Postgres support, and MySQL orders containers start;
|
|
368
|
+
- disposable Postgres billing, Postgres support, support plan-credit, and MySQL orders containers start;
|
|
369
369
|
- MCP `tools/list` exposes reviewed semantic tools, not raw SQL;
|
|
370
370
|
- proposal calls create exact before/after diffs while source rows remain unchanged;
|
|
371
|
+
- the support plan-credit scenario proves `$25` policy auto-approval, `$100` human approval, and `$1000` bound rejection;
|
|
371
372
|
- local approval happens outside MCP;
|
|
372
373
|
- guarded writeback applies approved jobs and records receipts/replay;
|
|
373
374
|
- idempotent retry returns the existing receipt;
|
package/docs/release-notes.md
CHANGED
|
@@ -12,6 +12,15 @@ for the Synapsor Cloud CLI.
|
|
|
12
12
|
|
|
13
13
|
## Unreleased
|
|
14
14
|
|
|
15
|
+
## 0.1.9
|
|
16
|
+
|
|
17
|
+
### CLI Hygiene
|
|
18
|
+
|
|
19
|
+
- Adds top-level `synapsor-runner --version`, `synapsor-runner -v`, and
|
|
20
|
+
`synapsor-runner version` output.
|
|
21
|
+
|
|
22
|
+
## 0.1.8
|
|
23
|
+
|
|
15
24
|
### DSL / JSON Contract Parity
|
|
16
25
|
|
|
17
26
|
- Adds portable spec fields for capability `returns_hint`, proposal
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Support Plan Credit Example
|
|
2
|
+
|
|
3
|
+
This example shows three enforcement tiers on one support action:
|
|
4
|
+
|
|
5
|
+
- `$25` (`2500` cents): policy-approved, then applied locally through guarded writeback.
|
|
6
|
+
- `$100` (`10000` cents): saved as `pending_review` until a local operator approves.
|
|
7
|
+
- `$1000` (`100000` cents): rejected before proposal creation by the contract bound.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- Docker
|
|
12
|
+
- Node 22.5+
|
|
13
|
+
- Port `55438` available
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
docker compose -f examples/support-plan-credit/docker-compose.yml up -d
|
|
19
|
+
|
|
20
|
+
export PLAN_CREDIT_POSTGRES_READ_URL="postgresql://synapsor_reader:synapsor_reader_password@localhost:55438/synapsor_runner_plan_credit"
|
|
21
|
+
export PLAN_CREDIT_POSTGRES_WRITE_URL="postgresql://synapsor_writer:synapsor_writer_password@localhost:55438/synapsor_runner_plan_credit"
|
|
22
|
+
export SYNAPSOR_TENANT_ID="acme"
|
|
23
|
+
export SYNAPSOR_PRINCIPAL="local_support_agent"
|
|
24
|
+
|
|
25
|
+
synapsor-runner tools preview \
|
|
26
|
+
--config examples/support-plan-credit/synapsor.runner.json \
|
|
27
|
+
--store ./tmp/support-plan-credit/local.db
|
|
28
|
+
|
|
29
|
+
# Run this in a second terminal if you want to attach an MCP client.
|
|
30
|
+
synapsor-runner mcp serve \
|
|
31
|
+
--config examples/support-plan-credit/synapsor.runner.json \
|
|
32
|
+
--store ./tmp/support-plan-credit/local.db
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Walkthrough
|
|
36
|
+
|
|
37
|
+
Inspect the customer first:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
synapsor-runner smoke call support.inspect_customer \
|
|
41
|
+
--config examples/support-plan-credit/synapsor.runner.json \
|
|
42
|
+
--store ./tmp/support-plan-credit/local.db \
|
|
43
|
+
--json '{"customer_id":"CUS-3001"}'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Propose a `$25` credit:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
synapsor-runner propose support.propose_plan_credit \
|
|
50
|
+
--config examples/support-plan-credit/synapsor.runner.json \
|
|
51
|
+
--store ./tmp/support-plan-credit/local.db \
|
|
52
|
+
--json '{"customer_id":"CUS-3001","credit_cents":2500,"reason":"SLA outage ticket SUP-481"}'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`proposals show` displays `policy:support_propose_plan_credit_auto_approval`.
|
|
56
|
+
The proposal is approved, but it is not applied until the local writeback step:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
synapsor-runner proposals show latest --store ./tmp/support-plan-credit/local.db
|
|
60
|
+
synapsor-runner apply latest \
|
|
61
|
+
--config examples/support-plan-credit/synapsor.runner.json \
|
|
62
|
+
--store ./tmp/support-plan-credit/local.db
|
|
63
|
+
synapsor-runner receipts list --proposal <proposal_id> --store ./tmp/support-plan-credit/local.db
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Propose a `$100` credit:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
synapsor-runner propose support.propose_plan_credit \
|
|
70
|
+
--config examples/support-plan-credit/synapsor.runner.json \
|
|
71
|
+
--store ./tmp/support-plan-credit/local.db \
|
|
72
|
+
--json '{"customer_id":"CUS-3001","credit_cents":10000,"reason":"larger support credit"}'
|
|
73
|
+
|
|
74
|
+
synapsor-runner proposals approve latest --yes --store ./tmp/support-plan-credit/local.db
|
|
75
|
+
synapsor-runner apply latest \
|
|
76
|
+
--config examples/support-plan-credit/synapsor.runner.json \
|
|
77
|
+
--store ./tmp/support-plan-credit/local.db
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Propose a `$1000` credit:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
synapsor-runner propose support.propose_plan_credit \
|
|
84
|
+
--config examples/support-plan-credit/synapsor.runner.json \
|
|
85
|
+
--store ./tmp/support-plan-credit/local.db \
|
|
86
|
+
--json '{"customer_id":"CUS-3001","credit_cents":100000,"reason":"too large"}'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The contract bound rejects it before a proposal row exists.
|
|
90
|
+
|
|
91
|
+
Replay and stale-row checks:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
synapsor-runner replay export --proposal latest --store ./tmp/support-plan-credit/local.db --output ./tmp/support-plan-credit/replay.json
|
|
95
|
+
synapsor-runner apply latest --config examples/support-plan-credit/synapsor.runner.json --store ./tmp/support-plan-credit/local.db
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The live smoke test mutates `updated_at` before apply and proves stale proposals
|
|
99
|
+
return `VERSION_CONFLICT` without changing the source row.
|
|
100
|
+
|
|
101
|
+
## What The Agent Never Sees
|
|
102
|
+
|
|
103
|
+
The contract keeps these fields out:
|
|
104
|
+
|
|
105
|
+
- `card_token`
|
|
106
|
+
- `raw_payment_method`
|
|
107
|
+
- `internal_risk_score`
|
|
108
|
+
- `private_notes`
|
|
109
|
+
|
|
110
|
+
`tools preview` should show only:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
support.inspect_customer
|
|
114
|
+
support.propose_plan_credit
|
|
115
|
+
auto-approval: enabled
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
No raw SQL, approval tools, commit tools, write credentials, or model-controlled
|
|
119
|
+
tenant authority are exposed to MCP.
|
|
120
|
+
|
|
121
|
+
## How The Policy Is Governed
|
|
122
|
+
|
|
123
|
+
The auto-approval policy lives in `contract.synapsor`:
|
|
124
|
+
|
|
125
|
+
```sql
|
|
126
|
+
AUTO APPROVE WHEN plan_credit_cents <= 2500
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
That compiles into `synapsor.contract.json` as a reviewed approval policy. It is
|
|
130
|
+
stored in git, validated by `@synapsor/spec`, and included in Cloud push payloads.
|
|
131
|
+
|
|
132
|
+
To disable local policy approval without changing the contract:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"approvals": {
|
|
137
|
+
"disable_auto_approval": true
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Auto-approval never applies the write. It only records an approval row and audit
|
|
143
|
+
event with actor `policy:<policy_name>`.
|
|
144
|
+
|
|
145
|
+
## Cloud
|
|
146
|
+
|
|
147
|
+
Dry-run the Cloud payload:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
synapsor-runner cloud push examples/support-plan-credit/synapsor.contract.json --dry-run
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Real push uses your Cloud env vars:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
export SYNAPSOR_CONTROL_PLANE_URL="https://api.synapsor.ai"
|
|
157
|
+
export SYNAPSOR_RUNNER_TOKEN="syn_wbr_..."
|
|
158
|
+
|
|
159
|
+
synapsor-runner cloud push examples/support-plan-credit/synapsor.contract.json \
|
|
160
|
+
--workspace <workspace_id> \
|
|
161
|
+
--name support-plan-credit
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
After push, Cloud can return the registered version and a runner bundle with
|
|
165
|
+
placeholder env names:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
curl -H "Authorization: Bearer $SYNAPSOR_CLOUD_TOKEN" \
|
|
169
|
+
"$SYNAPSOR_CONTROL_PLANE_URL/v1/control/projects/<workspace_id>/agent-contracts/support-plan-credit/versions/latest"
|
|
170
|
+
|
|
171
|
+
curl -H "Authorization: Bearer $SYNAPSOR_CLOUD_TOKEN" \
|
|
172
|
+
"$SYNAPSOR_CONTROL_PLANE_URL/v1/control/projects/<workspace_id>/agent-contracts/support-plan-credit/runner-bundle" \
|
|
173
|
+
-o support-plan-credit-runner-bundle.json
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
No Cloud account is needed to run this local example.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
CREATE AGENT CONTEXT support_agent_context
|
|
2
|
+
BIND tenant_id FROM ENVIRONMENT SYNAPSOR_TENANT_ID REQUIRED
|
|
3
|
+
BIND principal FROM ENVIRONMENT SYNAPSOR_PRINCIPAL REQUIRED
|
|
4
|
+
TENANT BINDING tenant_id
|
|
5
|
+
PRINCIPAL BINDING principal
|
|
6
|
+
END
|
|
7
|
+
|
|
8
|
+
CREATE CAPABILITY support.inspect_customer
|
|
9
|
+
DESCRIPTION 'Inspect one support customer in the trusted tenant before proposing a plan credit.'
|
|
10
|
+
RETURNS HINT 'Returns reviewed customer fields plus evidence/query-audit handles.'
|
|
11
|
+
USING CONTEXT support_agent_context
|
|
12
|
+
SOURCE local_postgres
|
|
13
|
+
ON public.customers
|
|
14
|
+
PRIMARY KEY id
|
|
15
|
+
TENANT KEY tenant_id
|
|
16
|
+
CONFLICT GUARD updated_at
|
|
17
|
+
LOOKUP customer_id BY id
|
|
18
|
+
ARG customer_id STRING REQUIRED MAX LENGTH 128 DESCRIPTION 'Customer id such as CUS-3001.'
|
|
19
|
+
ALLOW READ id, tenant_id, customer_id, plan, invoice_status, support_ticket_reason, plan_credit_cents, credit_reason, updated_at
|
|
20
|
+
KEEP OUT card_token, raw_payment_method, internal_risk_score, private_notes
|
|
21
|
+
REQUIRE EVIDENCE
|
|
22
|
+
MAX ROWS 1
|
|
23
|
+
END
|
|
24
|
+
|
|
25
|
+
CREATE CAPABILITY support.propose_plan_credit
|
|
26
|
+
DESCRIPTION 'Propose a bounded support plan credit after inspecting customer and ticket evidence.'
|
|
27
|
+
RETURNS HINT 'Returns a proposal id, exact diff, evidence handle, and source_database_changed:false. Policy may approve small credits, but writeback is still separate.'
|
|
28
|
+
USING CONTEXT support_agent_context
|
|
29
|
+
SOURCE local_postgres
|
|
30
|
+
ON public.customers
|
|
31
|
+
PRIMARY KEY id
|
|
32
|
+
TENANT KEY tenant_id
|
|
33
|
+
CONFLICT GUARD updated_at
|
|
34
|
+
LOOKUP customer_id BY id
|
|
35
|
+
ARG customer_id STRING REQUIRED MAX LENGTH 128 DESCRIPTION 'Customer id such as CUS-3001.'
|
|
36
|
+
ARG credit_cents NUMBER REQUIRED MIN 1 DESCRIPTION 'Requested plan credit in cents.'
|
|
37
|
+
ARG reason TEXT REQUIRED MAX LENGTH 500 DESCRIPTION 'Business reason tied to support evidence.'
|
|
38
|
+
ALLOW READ id, tenant_id, customer_id, plan, invoice_status, support_ticket_reason, plan_credit_cents, credit_reason, updated_at
|
|
39
|
+
KEEP OUT card_token, raw_payment_method, internal_risk_score, private_notes
|
|
40
|
+
REQUIRE EVIDENCE
|
|
41
|
+
MAX ROWS 1
|
|
42
|
+
PROPOSE ACTION grant_plan_credit
|
|
43
|
+
ALLOW WRITE plan_credit_cents, credit_reason
|
|
44
|
+
PATCH plan_credit_cents = ARG credit_cents
|
|
45
|
+
PATCH credit_reason = ARG reason
|
|
46
|
+
BOUND plan_credit_cents 1..50000
|
|
47
|
+
APPROVAL ROLE support_reviewer
|
|
48
|
+
AUTO APPROVE WHEN plan_credit_cents <= 2500
|
|
49
|
+
WRITEBACK DIRECT SQL
|
|
50
|
+
END
|
|
51
|
+
|
|
52
|
+
CREATE AGENT WORKFLOW support.plan_credit_request
|
|
53
|
+
USING CONTEXT support_agent_context
|
|
54
|
+
ALLOW CAPABILITY support.inspect_customer
|
|
55
|
+
ALLOW CAPABILITY support.propose_plan_credit
|
|
56
|
+
REQUIRE EVIDENCE
|
|
57
|
+
APPROVAL REQUIRED ROLE support_reviewer
|
|
58
|
+
CHECKPOINT PROPOSAL ONLY
|
|
59
|
+
END
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:16
|
|
4
|
+
container_name: synapsor_runner_plan_credit
|
|
5
|
+
environment:
|
|
6
|
+
POSTGRES_DB: synapsor_runner_plan_credit
|
|
7
|
+
POSTGRES_USER: synapsor_admin
|
|
8
|
+
POSTGRES_PASSWORD: synapsor_admin_password
|
|
9
|
+
ports:
|
|
10
|
+
- "55438:5432"
|
|
11
|
+
volumes:
|
|
12
|
+
- ./seed:/docker-entrypoint-initdb.d:ro
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS public.customers (
|
|
2
|
+
id text PRIMARY KEY,
|
|
3
|
+
tenant_id text NOT NULL,
|
|
4
|
+
customer_id text NOT NULL,
|
|
5
|
+
plan text NOT NULL,
|
|
6
|
+
invoice_status text NOT NULL,
|
|
7
|
+
support_ticket_reason text NOT NULL,
|
|
8
|
+
plan_credit_cents integer NOT NULL DEFAULT 0,
|
|
9
|
+
credit_reason text,
|
|
10
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
11
|
+
card_token text,
|
|
12
|
+
raw_payment_method text,
|
|
13
|
+
internal_risk_score integer,
|
|
14
|
+
private_notes text
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
INSERT INTO public.customers (
|
|
18
|
+
id,
|
|
19
|
+
tenant_id,
|
|
20
|
+
customer_id,
|
|
21
|
+
plan,
|
|
22
|
+
invoice_status,
|
|
23
|
+
support_ticket_reason,
|
|
24
|
+
plan_credit_cents,
|
|
25
|
+
credit_reason,
|
|
26
|
+
updated_at,
|
|
27
|
+
card_token,
|
|
28
|
+
raw_payment_method,
|
|
29
|
+
internal_risk_score,
|
|
30
|
+
private_notes
|
|
31
|
+
)
|
|
32
|
+
VALUES
|
|
33
|
+
(
|
|
34
|
+
'CUS-3001',
|
|
35
|
+
'acme',
|
|
36
|
+
'CUS-3001',
|
|
37
|
+
'enterprise',
|
|
38
|
+
'open_dispute',
|
|
39
|
+
'SLA outage ticket SUP-481 confirmed by support lead',
|
|
40
|
+
0,
|
|
41
|
+
NULL,
|
|
42
|
+
'2026-06-20T14:31:08Z',
|
|
43
|
+
'tok_fake_acme_3001',
|
|
44
|
+
'fake_visa_4242',
|
|
45
|
+
12,
|
|
46
|
+
'fake internal note: do not expose'
|
|
47
|
+
),
|
|
48
|
+
(
|
|
49
|
+
'CUS-9001',
|
|
50
|
+
'otherco',
|
|
51
|
+
'CUS-9001',
|
|
52
|
+
'starter',
|
|
53
|
+
'paid',
|
|
54
|
+
'OtherCo tenant row for spoof checks',
|
|
55
|
+
0,
|
|
56
|
+
NULL,
|
|
57
|
+
'2026-06-20T14:31:08Z',
|
|
58
|
+
'tok_fake_otherco_9001',
|
|
59
|
+
'fake_mastercard_4444',
|
|
60
|
+
77,
|
|
61
|
+
'fake other tenant note'
|
|
62
|
+
)
|
|
63
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
64
|
+
tenant_id = EXCLUDED.tenant_id,
|
|
65
|
+
customer_id = EXCLUDED.customer_id,
|
|
66
|
+
plan = EXCLUDED.plan,
|
|
67
|
+
invoice_status = EXCLUDED.invoice_status,
|
|
68
|
+
support_ticket_reason = EXCLUDED.support_ticket_reason,
|
|
69
|
+
plan_credit_cents = EXCLUDED.plan_credit_cents,
|
|
70
|
+
credit_reason = EXCLUDED.credit_reason,
|
|
71
|
+
updated_at = EXCLUDED.updated_at,
|
|
72
|
+
card_token = EXCLUDED.card_token,
|
|
73
|
+
raw_payment_method = EXCLUDED.raw_payment_method,
|
|
74
|
+
internal_risk_score = EXCLUDED.internal_risk_score,
|
|
75
|
+
private_notes = EXCLUDED.private_notes;
|
|
76
|
+
|
|
77
|
+
DO $$
|
|
78
|
+
BEGIN
|
|
79
|
+
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'synapsor_reader') THEN
|
|
80
|
+
CREATE ROLE synapsor_reader LOGIN PASSWORD 'synapsor_reader_password';
|
|
81
|
+
END IF;
|
|
82
|
+
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'synapsor_writer') THEN
|
|
83
|
+
CREATE ROLE synapsor_writer LOGIN PASSWORD 'synapsor_writer_password';
|
|
84
|
+
END IF;
|
|
85
|
+
END
|
|
86
|
+
$$;
|
|
87
|
+
|
|
88
|
+
GRANT CONNECT ON DATABASE synapsor_runner_plan_credit TO synapsor_reader, synapsor_writer;
|
|
89
|
+
GRANT USAGE ON SCHEMA public TO synapsor_reader, synapsor_writer;
|
|
90
|
+
GRANT CREATE ON SCHEMA public TO synapsor_writer;
|
|
91
|
+
GRANT SELECT ON public.customers TO synapsor_reader, synapsor_writer;
|
|
92
|
+
GRANT UPDATE (plan_credit_cents, credit_reason, updated_at) ON public.customers TO synapsor_writer;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
{
|
|
2
|
+
"capabilities": [
|
|
3
|
+
{
|
|
4
|
+
"args": {
|
|
5
|
+
"customer_id": {
|
|
6
|
+
"description": "Customer id such as CUS-3001.",
|
|
7
|
+
"max_length": 128,
|
|
8
|
+
"required": true,
|
|
9
|
+
"type": "string"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"context": "support_agent_context",
|
|
13
|
+
"description": "Inspect one support customer in the trusted tenant before proposing a plan credit.",
|
|
14
|
+
"evidence": {
|
|
15
|
+
"query_audit": true,
|
|
16
|
+
"required": true
|
|
17
|
+
},
|
|
18
|
+
"kept_out_fields": [
|
|
19
|
+
"card_token",
|
|
20
|
+
"raw_payment_method",
|
|
21
|
+
"internal_risk_score",
|
|
22
|
+
"private_notes"
|
|
23
|
+
],
|
|
24
|
+
"kind": "read",
|
|
25
|
+
"lookup": {
|
|
26
|
+
"id_from_arg": "customer_id"
|
|
27
|
+
},
|
|
28
|
+
"max_rows": 1,
|
|
29
|
+
"name": "support.inspect_customer",
|
|
30
|
+
"returns_hint": "Returns reviewed customer fields plus evidence/query-audit handles.",
|
|
31
|
+
"source": "local_postgres",
|
|
32
|
+
"subject": {
|
|
33
|
+
"conflict_key": "updated_at",
|
|
34
|
+
"primary_key": "id",
|
|
35
|
+
"schema": "public",
|
|
36
|
+
"table": "customers",
|
|
37
|
+
"tenant_key": "tenant_id"
|
|
38
|
+
},
|
|
39
|
+
"visible_fields": [
|
|
40
|
+
"id",
|
|
41
|
+
"tenant_id",
|
|
42
|
+
"customer_id",
|
|
43
|
+
"plan",
|
|
44
|
+
"invoice_status",
|
|
45
|
+
"support_ticket_reason",
|
|
46
|
+
"plan_credit_cents",
|
|
47
|
+
"credit_reason",
|
|
48
|
+
"updated_at"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"args": {
|
|
53
|
+
"credit_cents": {
|
|
54
|
+
"description": "Requested plan credit in cents.",
|
|
55
|
+
"minimum": 1,
|
|
56
|
+
"required": true,
|
|
57
|
+
"type": "number"
|
|
58
|
+
},
|
|
59
|
+
"customer_id": {
|
|
60
|
+
"description": "Customer id such as CUS-3001.",
|
|
61
|
+
"max_length": 128,
|
|
62
|
+
"required": true,
|
|
63
|
+
"type": "string"
|
|
64
|
+
},
|
|
65
|
+
"reason": {
|
|
66
|
+
"description": "Business reason tied to support evidence.",
|
|
67
|
+
"max_length": 500,
|
|
68
|
+
"required": true,
|
|
69
|
+
"type": "string"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"context": "support_agent_context",
|
|
73
|
+
"description": "Propose a bounded support plan credit after inspecting customer and ticket evidence.",
|
|
74
|
+
"evidence": {
|
|
75
|
+
"query_audit": true,
|
|
76
|
+
"required": true
|
|
77
|
+
},
|
|
78
|
+
"kept_out_fields": [
|
|
79
|
+
"card_token",
|
|
80
|
+
"raw_payment_method",
|
|
81
|
+
"internal_risk_score",
|
|
82
|
+
"private_notes"
|
|
83
|
+
],
|
|
84
|
+
"kind": "proposal",
|
|
85
|
+
"lookup": {
|
|
86
|
+
"id_from_arg": "customer_id"
|
|
87
|
+
},
|
|
88
|
+
"max_rows": 1,
|
|
89
|
+
"name": "support.propose_plan_credit",
|
|
90
|
+
"proposal": {
|
|
91
|
+
"action": "grant_plan_credit",
|
|
92
|
+
"allowed_fields": [
|
|
93
|
+
"plan_credit_cents",
|
|
94
|
+
"credit_reason"
|
|
95
|
+
],
|
|
96
|
+
"approval": {
|
|
97
|
+
"mode": "policy",
|
|
98
|
+
"policy": "support_propose_plan_credit_auto_approval",
|
|
99
|
+
"required_role": "support_reviewer"
|
|
100
|
+
},
|
|
101
|
+
"conflict_guard": {
|
|
102
|
+
"column": "updated_at"
|
|
103
|
+
},
|
|
104
|
+
"numeric_bounds": {
|
|
105
|
+
"plan_credit_cents": {
|
|
106
|
+
"maximum": 50000,
|
|
107
|
+
"minimum": 1
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"patch": {
|
|
111
|
+
"credit_reason": {
|
|
112
|
+
"from_arg": "reason"
|
|
113
|
+
},
|
|
114
|
+
"plan_credit_cents": {
|
|
115
|
+
"from_arg": "credit_cents"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"writeback": {
|
|
119
|
+
"mode": "direct_sql"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"returns_hint": "Returns a proposal id, exact diff, evidence handle, and source_database_changed:false. Policy may approve small credits, but writeback is still separate.",
|
|
123
|
+
"source": "local_postgres",
|
|
124
|
+
"subject": {
|
|
125
|
+
"conflict_key": "updated_at",
|
|
126
|
+
"primary_key": "id",
|
|
127
|
+
"schema": "public",
|
|
128
|
+
"table": "customers",
|
|
129
|
+
"tenant_key": "tenant_id"
|
|
130
|
+
},
|
|
131
|
+
"visible_fields": [
|
|
132
|
+
"id",
|
|
133
|
+
"tenant_id",
|
|
134
|
+
"customer_id",
|
|
135
|
+
"plan",
|
|
136
|
+
"invoice_status",
|
|
137
|
+
"support_ticket_reason",
|
|
138
|
+
"plan_credit_cents",
|
|
139
|
+
"credit_reason",
|
|
140
|
+
"updated_at"
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
"contexts": [
|
|
145
|
+
{
|
|
146
|
+
"bindings": [
|
|
147
|
+
{
|
|
148
|
+
"key": "SYNAPSOR_TENANT_ID",
|
|
149
|
+
"name": "tenant_id",
|
|
150
|
+
"required": true,
|
|
151
|
+
"source": "environment"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"key": "SYNAPSOR_PRINCIPAL",
|
|
155
|
+
"name": "principal",
|
|
156
|
+
"required": true,
|
|
157
|
+
"source": "environment"
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
"name": "support_agent_context",
|
|
161
|
+
"principal_binding": "principal",
|
|
162
|
+
"tenant_binding": "tenant_id"
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
"kind": "SynapsorContract",
|
|
166
|
+
"policies": [
|
|
167
|
+
{
|
|
168
|
+
"kind": "approval",
|
|
169
|
+
"mode": "green",
|
|
170
|
+
"name": "support_propose_plan_credit_auto_approval",
|
|
171
|
+
"rules": [
|
|
172
|
+
{
|
|
173
|
+
"field": "plan_credit_cents",
|
|
174
|
+
"max": 2500
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"spec_version": "0.1",
|
|
180
|
+
"workflows": [
|
|
181
|
+
{
|
|
182
|
+
"allowed_capabilities": [
|
|
183
|
+
"support.inspect_customer",
|
|
184
|
+
"support.propose_plan_credit"
|
|
185
|
+
],
|
|
186
|
+
"approval": {
|
|
187
|
+
"required": true,
|
|
188
|
+
"role": "support_reviewer"
|
|
189
|
+
},
|
|
190
|
+
"context": "support_agent_context",
|
|
191
|
+
"name": "support.plan_credit_request",
|
|
192
|
+
"replay": {
|
|
193
|
+
"checkpoint": "proposal_only"
|
|
194
|
+
},
|
|
195
|
+
"required_evidence": true
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"mode": "review",
|
|
4
|
+
"storage": {
|
|
5
|
+
"sqlite_path": "./tmp/support-plan-credit/local.db"
|
|
6
|
+
},
|
|
7
|
+
"contracts": [
|
|
8
|
+
"./synapsor.contract.json"
|
|
9
|
+
],
|
|
10
|
+
"sources": {
|
|
11
|
+
"local_postgres": {
|
|
12
|
+
"engine": "postgres",
|
|
13
|
+
"read_url_env": "PLAN_CREDIT_POSTGRES_READ_URL",
|
|
14
|
+
"write_url_env": "PLAN_CREDIT_POSTGRES_WRITE_URL",
|
|
15
|
+
"statement_timeout_ms": 3000
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synapsor/runner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Stop giving AI agents execute_sql; expose reviewed Postgres/MySQL MCP actions with proposals, approval, writeback, and replay.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"examples/openai-agents-stdio/**",
|
|
26
26
|
"examples/raw-sql-vs-synapsor/**",
|
|
27
27
|
"examples/reference-support-billing-app/**",
|
|
28
|
+
"examples/support-plan-credit/**",
|
|
28
29
|
"examples/support-billing-agent/**",
|
|
29
30
|
"fixtures/**",
|
|
30
31
|
"schemas/**",
|