@synapsor/runner 1.4.123 → 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 +144 -19
- package/README.md +132 -135
- 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 +18427 -7084
- package/dist/runtime.d.ts +50 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.mjs +15094 -0
- 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 +44 -5
- package/docs/alternatives.md +122 -0
- package/docs/capability-authoring.md +44 -1
- package/docs/client-recipes.md +218 -0
- package/docs/contract-testing.md +9 -7
- package/docs/cursor-plugin.md +78 -0
- package/docs/database-enforced-scope.md +273 -0
- package/docs/dependency-license-inventory.md +6 -2
- package/docs/doctor.md +17 -0
- package/docs/effect-regression.md +179 -0
- package/docs/fresh-developer-usability.md +110 -0
- package/docs/getting-started-own-database.md +103 -2
- package/docs/host-compatibility.md +59 -0
- package/docs/local-mode.md +12 -7
- package/docs/mcp-apps.md +188 -0
- package/docs/mcp-audit.md +220 -2
- package/docs/mcp-client-setup.md +20 -4
- package/docs/mcp-clients.md +41 -4
- package/docs/oss-vs-cloud.md +3 -0
- package/docs/production.md +8 -0
- package/docs/release-notes.md +138 -14
- package/docs/release-policy.md +9 -5
- package/docs/runner-config-reference.md +25 -0
- package/docs/schema-api-candidates.md +68 -0
- package/docs/security-boundary.md +30 -5
- package/docs/shadow-studies.md +241 -0
- package/docs/troubleshooting-first-run.md +46 -0
- package/examples/mcp-postgres-billing-app-handler/app-handler.mjs +9 -1
- package/examples/mcp-postgres-billing-app-handler/schema.sql +11 -0
- package/examples/mcp-postgres-billing-app-handler/synapsor-handler.mjs +3 -0
- package/examples/raw-sql-vs-synapsor/Makefile +2 -2
- package/examples/raw-sql-vs-synapsor/README.md +6 -4
- package/examples/support-billing-agent/Makefile +5 -1
- package/examples/support-billing-agent/README.md +57 -5
- 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/db/schema.sql +88 -1
- package/examples/support-billing-agent/db/seed.sql +15 -15
- package/examples/support-billing-agent/scripts/run-demo.sh +4 -0
- package/examples/support-billing-agent/scripts/run-evaluation.sh +63 -0
- package/examples/support-billing-agent/shadow-study/cases.jsonl +6 -0
- package/examples/support-billing-agent/shadow-study/outcomes.jsonl +2 -0
- package/examples/support-billing-agent/synapsor.runner.json +17 -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/effects/changed/eff_support_late_fee_v1.result.json +58 -0
- package/fixtures/effects/dataset.json +8 -0
- package/fixtures/effects/results/eff_support_late_fee_v1.result.json +59 -0
- package/fixtures/effects/support-late-fee.effect.json +71 -0
- package/fixtures/generators/drizzle/malicious.ts +11 -0
- package/fixtures/generators/drizzle/schema.ts +12 -0
- package/fixtures/generators/openapi/external-ref.yaml +15 -0
- package/fixtures/generators/openapi/openapi.yaml +81 -0
- package/fixtures/generators/prisma/schema.prisma +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 +29 -3
- package/schemas/effect-dataset.schema.json +19 -0
- package/schemas/effect-fixture.schema.json +191 -0
- package/schemas/effect-result.schema.json +52 -0
- package/schemas/mcp-audit-candidates.schema.json +41 -0
- package/schemas/mcp-audit-report.schema.json +203 -0
- package/schemas/schema-candidate-review.schema.json +167 -0
- package/schemas/schema-candidates.schema.json +49 -0
- package/schemas/synapsor.contract-tests.schema.json +1 -1
- package/schemas/synapsor.runner.schema.json +51 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
3
|
+
|
|
4
|
+
const token = process.env.SYNAPSOR_RUNNER_HTTP_TOKEN;
|
|
5
|
+
if (!token) throw new Error("set SYNAPSOR_RUNNER_HTTP_TOKEN in the launching environment");
|
|
6
|
+
|
|
7
|
+
const transport = new StreamableHTTPClientTransport(new URL("http://127.0.0.1:8766/mcp"), {
|
|
8
|
+
requestInit: { headers: { Authorization: `Bearer ${token}` } },
|
|
9
|
+
});
|
|
10
|
+
const client = new Client({ name: "synapsor-generic-http", version: "1.0.0" });
|
|
11
|
+
|
|
12
|
+
await client.connect(transport);
|
|
13
|
+
try {
|
|
14
|
+
const listed = await client.listTools();
|
|
15
|
+
const names = listed.tools.map((tool) => tool.name);
|
|
16
|
+
const forbidden = names.filter((name) => /sql|approve|apply|commit|activate|revert/i.test(name));
|
|
17
|
+
if (forbidden.length) throw new Error(`unsafe model-facing tools: ${forbidden.join(", ")}`);
|
|
18
|
+
|
|
19
|
+
const result = await client.callTool({
|
|
20
|
+
name: "support.propose_plan_credit",
|
|
21
|
+
arguments: {
|
|
22
|
+
customer_id: "CUS-3001",
|
|
23
|
+
credit_cents: 2500,
|
|
24
|
+
reason: "SLA outage ticket SUP-481",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
console.log(JSON.stringify(result, null, 2));
|
|
28
|
+
console.error("Proposal only. Review and apply outside the model-facing MCP client.");
|
|
29
|
+
} finally {
|
|
30
|
+
await client.close();
|
|
31
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from google.adk.agents import LlmAgent
|
|
4
|
+
from google.adk.tools.mcp_tool import McpToolset
|
|
5
|
+
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
|
|
6
|
+
from mcp import StdioServerParameters
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
root_agent = LlmAgent(
|
|
10
|
+
model=os.environ["GOOGLE_ADK_MODEL"],
|
|
11
|
+
name="synapsor_support_agent",
|
|
12
|
+
instruction=(
|
|
13
|
+
"List your Synapsor tools. Call support.propose_plan_credit with "
|
|
14
|
+
"customer_id CUS-3001, credit_cents 2500, and reason "
|
|
15
|
+
"'SLA outage ticket SUP-481'. Confirm source_database_changed is false "
|
|
16
|
+
"and stop for human review outside the model-facing agent."
|
|
17
|
+
),
|
|
18
|
+
tools=[
|
|
19
|
+
McpToolset(
|
|
20
|
+
connection_params=StdioConnectionParams(
|
|
21
|
+
server_params=StdioServerParameters(
|
|
22
|
+
command="npx",
|
|
23
|
+
args=[
|
|
24
|
+
"-y", "-p", "@synapsor/runner", "synapsor-runner",
|
|
25
|
+
"mcp", "serve", "--config",
|
|
26
|
+
"./examples/support-plan-credit/synapsor.runner.json",
|
|
27
|
+
"--store", "./tmp/support-plan-credit/local.db",
|
|
28
|
+
],
|
|
29
|
+
)
|
|
30
|
+
),
|
|
31
|
+
tool_filter=[
|
|
32
|
+
"support.inspect_customer",
|
|
33
|
+
"support.propose_plan_credit",
|
|
34
|
+
],
|
|
35
|
+
)
|
|
36
|
+
],
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Run root_agent with your normal ADK runner. Approval, apply, commit,
|
|
40
|
+
# activation, and revert are intentionally not MCP tools.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
|
|
2
|
+
|
|
3
|
+
const client = new MultiServerMCPClient({
|
|
4
|
+
synapsor: {
|
|
5
|
+
transport: "stdio",
|
|
6
|
+
command: "npx",
|
|
7
|
+
args: [
|
|
8
|
+
"-y", "-p", "@synapsor/runner", "synapsor-runner", "mcp", "serve",
|
|
9
|
+
"--config", "./examples/support-plan-credit/synapsor.runner.json",
|
|
10
|
+
"--store", "./tmp/support-plan-credit/local.db",
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const tools = await client.getTools();
|
|
16
|
+
const names = tools.map((tool) => tool.name);
|
|
17
|
+
const forbidden = names.filter((name) => /sql|approve|apply|commit|activate|revert/i.test(name));
|
|
18
|
+
if (forbidden.length) throw new Error(`unsafe model-facing tools: ${forbidden.join(", ")}`);
|
|
19
|
+
|
|
20
|
+
const proposal = tools.find((tool) => tool.name === "support.propose_plan_credit");
|
|
21
|
+
if (!proposal) throw new Error(`proposal tool missing; available: ${names.join(", ")}`);
|
|
22
|
+
|
|
23
|
+
const result = await proposal.invoke({
|
|
24
|
+
customer_id: "CUS-3001",
|
|
25
|
+
credit_cents: 2500,
|
|
26
|
+
reason: "SLA outage ticket SUP-481",
|
|
27
|
+
});
|
|
28
|
+
console.log(result);
|
|
29
|
+
console.error("Proposal only. Review and apply outside the model-facing LangChain/LangGraph agent.");
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from llama_index.tools.mcp import BasicMCPClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def main() -> None:
|
|
7
|
+
client = BasicMCPClient(
|
|
8
|
+
"npx",
|
|
9
|
+
args=[
|
|
10
|
+
"-y", "-p", "@synapsor/runner", "synapsor-runner", "mcp", "serve",
|
|
11
|
+
"--config", "./examples/support-plan-credit/synapsor.runner.json",
|
|
12
|
+
"--store", "./tmp/support-plan-credit/local.db",
|
|
13
|
+
],
|
|
14
|
+
)
|
|
15
|
+
tools = await client.list_tools()
|
|
16
|
+
names = [tool.name for tool in tools.tools]
|
|
17
|
+
forbidden = [name for name in names if any(part in name.lower() for part in (
|
|
18
|
+
"sql", "approve", "apply", "commit", "activate", "revert"
|
|
19
|
+
))]
|
|
20
|
+
if forbidden:
|
|
21
|
+
raise RuntimeError(f"unsafe model-facing tools: {forbidden}")
|
|
22
|
+
|
|
23
|
+
result = await client.call_tool(
|
|
24
|
+
"support.propose_plan_credit",
|
|
25
|
+
{
|
|
26
|
+
"customer_id": "CUS-3001",
|
|
27
|
+
"credit_cents": 2500,
|
|
28
|
+
"reason": "SLA outage ticket SUP-481",
|
|
29
|
+
},
|
|
30
|
+
)
|
|
31
|
+
print(result)
|
|
32
|
+
print("Proposal only. Review and apply outside the model-facing LlamaIndex agent.")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
asyncio.run(main())
|
|
@@ -9,7 +9,7 @@ await synapsor.connect();
|
|
|
9
9
|
try {
|
|
10
10
|
const agent = new Agent({
|
|
11
11
|
name: "Support operations agent",
|
|
12
|
-
instructions: "Use only Synapsor business tools. Inspect evidence before proposing a plan credit.",
|
|
12
|
+
instructions: "Use only the listed Synapsor business tools. Inspect evidence before proposing a plan credit. Never request approval or apply authority; stop for human review outside the agent.",
|
|
13
13
|
mcpServers: [synapsor],
|
|
14
14
|
});
|
|
15
15
|
const result = await run(agent, "Inspect customer CUS-3001 and propose a $25 plan credit for SLA outage ticket SUP-481.");
|
|
@@ -10,7 +10,7 @@ await synapsor.connect();
|
|
|
10
10
|
try {
|
|
11
11
|
const agent = new Agent({
|
|
12
12
|
name: "Support operations agent",
|
|
13
|
-
instructions: "Use only Synapsor business tools. Inspect evidence before proposing a plan credit.",
|
|
13
|
+
instructions: "Use only the listed Synapsor business tools. Inspect evidence before proposing a plan credit. Never request approval or apply authority; stop for human review outside the agent.",
|
|
14
14
|
mcpServers: [synapsor],
|
|
15
15
|
});
|
|
16
16
|
const result = await run(agent, "Inspect customer CUS-3001 and propose a $25 plan credit for SLA outage ticket SUP-481.");
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"servers": {
|
|
3
|
+
"synapsor": {
|
|
4
|
+
"type": "stdio",
|
|
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
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://schemas.synapsor.ai/synapsor.contract-tests.schema.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"name": "support.propose_plan_credit generated contract boundary",
|
|
5
|
+
"tests": [
|
|
6
|
+
{
|
|
7
|
+
"id": "support.propose_plan_credit-operator-boundary",
|
|
8
|
+
"kind": "operator_boundary",
|
|
9
|
+
"capability": "support.propose_plan_credit"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "support.propose_plan_credit-proposal-effect",
|
|
13
|
+
"kind": "proposal_effect",
|
|
14
|
+
"capability": "support.propose_plan_credit",
|
|
15
|
+
"expected": {
|
|
16
|
+
"action": "grant_plan_credit",
|
|
17
|
+
"operation": {
|
|
18
|
+
"kind": "update"
|
|
19
|
+
},
|
|
20
|
+
"allowed_fields": [
|
|
21
|
+
"plan_credit_cents",
|
|
22
|
+
"credit_reason"
|
|
23
|
+
],
|
|
24
|
+
"patch": {
|
|
25
|
+
"plan_credit_cents": {
|
|
26
|
+
"from_arg": "credit_cents"
|
|
27
|
+
},
|
|
28
|
+
"credit_reason": {
|
|
29
|
+
"from_arg": "reason"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"numeric_bounds": {
|
|
33
|
+
"plan_credit_cents": {
|
|
34
|
+
"minimum": 1,
|
|
35
|
+
"maximum": 50000
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"transition_guards": {},
|
|
39
|
+
"conflict_guard": {
|
|
40
|
+
"column": "updated_at"
|
|
41
|
+
},
|
|
42
|
+
"approval": {
|
|
43
|
+
"mode": "policy",
|
|
44
|
+
"policy": "support_propose_plan_credit_auto_approval",
|
|
45
|
+
"required_role": "support_reviewer"
|
|
46
|
+
},
|
|
47
|
+
"writeback": {
|
|
48
|
+
"mode": "direct_sql"
|
|
49
|
+
},
|
|
50
|
+
"max_rows": 1
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "support.propose_plan_credit-kept-out-fields",
|
|
55
|
+
"kind": "hide_fields",
|
|
56
|
+
"capability": "support.propose_plan_credit",
|
|
57
|
+
"args": {
|
|
58
|
+
"customer_id": "replace-customer_id",
|
|
59
|
+
"credit_cents": 1,
|
|
60
|
+
"reason": "replace-reason"
|
|
61
|
+
},
|
|
62
|
+
"trusted_context": {
|
|
63
|
+
"tenant_id": "replace-with-test-tenant",
|
|
64
|
+
"principal": "contract_test"
|
|
65
|
+
},
|
|
66
|
+
"fields": [
|
|
67
|
+
"card_token",
|
|
68
|
+
"raw_payment_method",
|
|
69
|
+
"internal_risk_score",
|
|
70
|
+
"private_notes"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "support.propose_plan_credit-conflict-guard",
|
|
75
|
+
"kind": "conflict_guard",
|
|
76
|
+
"capability": "support.propose_plan_credit",
|
|
77
|
+
"expected": {
|
|
78
|
+
"column": "updated_at"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "support.propose_plan_credit-trusted-scope",
|
|
83
|
+
"kind": "trusted_scope",
|
|
84
|
+
"capability": "support.propose_plan_credit",
|
|
85
|
+
"expected": {
|
|
86
|
+
"context": "support_agent_context",
|
|
87
|
+
"tenant_key": "tenant_id",
|
|
88
|
+
"tenant_binding": "tenant_id",
|
|
89
|
+
"tenant_authority": {
|
|
90
|
+
"source": "environment",
|
|
91
|
+
"key": "SYNAPSOR_TENANT_ID",
|
|
92
|
+
"required": true
|
|
93
|
+
},
|
|
94
|
+
"principal_binding": "principal",
|
|
95
|
+
"principal_authority": {
|
|
96
|
+
"source": "environment",
|
|
97
|
+
"key": "SYNAPSOR_PRINCIPAL",
|
|
98
|
+
"required": true
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"id": "support.propose_plan_credit-evidence-required",
|
|
104
|
+
"kind": "evidence_requirement",
|
|
105
|
+
"capability": "support.propose_plan_credit",
|
|
106
|
+
"expected": {
|
|
107
|
+
"required": true,
|
|
108
|
+
"query_audit": true
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "support.propose_plan_credit-approval-boundary",
|
|
113
|
+
"kind": "approval_boundary",
|
|
114
|
+
"capability": "support.propose_plan_credit",
|
|
115
|
+
"expected": {
|
|
116
|
+
"approval": {
|
|
117
|
+
"mode": "policy",
|
|
118
|
+
"policy": "support_propose_plan_credit_auto_approval",
|
|
119
|
+
"required_role": "support_reviewer"
|
|
120
|
+
},
|
|
121
|
+
"policy": {
|
|
122
|
+
"kind": "approval",
|
|
123
|
+
"limits": [
|
|
124
|
+
{
|
|
125
|
+
"kind": "count",
|
|
126
|
+
"max": 20,
|
|
127
|
+
"period": "day",
|
|
128
|
+
"scope": "tenant_policy"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"field": "plan_credit_cents",
|
|
132
|
+
"kind": "total",
|
|
133
|
+
"max": 100000,
|
|
134
|
+
"period": "day",
|
|
135
|
+
"scope": "tenant_policy"
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
"mode": "green",
|
|
139
|
+
"name": "support_propose_plan_credit_auto_approval",
|
|
140
|
+
"rules": [
|
|
141
|
+
{
|
|
142
|
+
"field": "plan_credit_cents",
|
|
143
|
+
"max": 2500
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"id": "support.propose_plan_credit-allowed-effect",
|
|
151
|
+
"kind": "tool_allow",
|
|
152
|
+
"capability": "support.propose_plan_credit",
|
|
153
|
+
"args": {
|
|
154
|
+
"customer_id": "replace-customer_id",
|
|
155
|
+
"credit_cents": 1,
|
|
156
|
+
"reason": "replace-reason"
|
|
157
|
+
},
|
|
158
|
+
"trusted_context": {
|
|
159
|
+
"tenant_id": "replace-with-test-tenant",
|
|
160
|
+
"principal": "contract_test"
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"id": "support.propose_plan_credit-other-tenant-denied",
|
|
165
|
+
"kind": "tool_deny",
|
|
166
|
+
"capability": "support.propose_plan_credit",
|
|
167
|
+
"args": {
|
|
168
|
+
"customer_id": "replace-with-other-tenant-customer_id",
|
|
169
|
+
"credit_cents": 1,
|
|
170
|
+
"reason": "replace-reason"
|
|
171
|
+
},
|
|
172
|
+
"trusted_context": {
|
|
173
|
+
"tenant_id": "replace-with-test-tenant",
|
|
174
|
+
"principal": "contract_test"
|
|
175
|
+
},
|
|
176
|
+
"expected_code": "NOT_FOUND_IN_TENANT"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"id": "support.propose_plan_credit-source-unchanged",
|
|
180
|
+
"kind": "source_unchanged_before_approval",
|
|
181
|
+
"capability": "support.propose_plan_credit",
|
|
182
|
+
"args": {
|
|
183
|
+
"customer_id": "replace-customer_id",
|
|
184
|
+
"credit_cents": 1,
|
|
185
|
+
"reason": "replace-reason"
|
|
186
|
+
},
|
|
187
|
+
"trusted_context": {
|
|
188
|
+
"tenant_id": "replace-with-test-tenant",
|
|
189
|
+
"principal": "contract_test"
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"id": "support.propose_plan_credit-customer_id-constraints",
|
|
194
|
+
"kind": "argument_constraint",
|
|
195
|
+
"capability": "support.propose_plan_credit",
|
|
196
|
+
"argument": "customer_id",
|
|
197
|
+
"expected": {
|
|
198
|
+
"max_length": 128
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"id": "support.propose_plan_credit-credit_cents-constraints",
|
|
203
|
+
"kind": "argument_constraint",
|
|
204
|
+
"capability": "support.propose_plan_credit",
|
|
205
|
+
"argument": "credit_cents",
|
|
206
|
+
"expected": {
|
|
207
|
+
"minimum": 1,
|
|
208
|
+
"maximum": 50000
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"id": "support.propose_plan_credit-reason-constraints",
|
|
213
|
+
"kind": "argument_constraint",
|
|
214
|
+
"capability": "support.propose_plan_credit",
|
|
215
|
+
"argument": "reason",
|
|
216
|
+
"expected": {
|
|
217
|
+
"max_length": 500
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
]
|
|
221
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineCapability } from "@synapsor/runner/authoring";
|
|
2
|
+
|
|
3
|
+
// Disabled authoring source. Editing this file never changes active Runner tools.
|
|
4
|
+
export default defineCapability({
|
|
5
|
+
name: "support.propose_plan_credit",
|
|
6
|
+
description: "Propose one bounded support plan credit after reviewing customer evidence.",
|
|
7
|
+
kind: "proposal",
|
|
8
|
+
context: "support_agent_context",
|
|
9
|
+
source: "local_postgres",
|
|
10
|
+
subject: {
|
|
11
|
+
schema: "public", table: "customers", primary_key: "id",
|
|
12
|
+
tenant_key: "tenant_id", conflict_key: "updated_at",
|
|
13
|
+
},
|
|
14
|
+
args: {
|
|
15
|
+
customer_id: { type: "string", required: true, max_length: 128 },
|
|
16
|
+
credit_cents: { type: "number", required: true, minimum: 1, maximum: 50000 },
|
|
17
|
+
reason: { type: "string", required: true, max_length: 500 },
|
|
18
|
+
},
|
|
19
|
+
lookup: { id_from_arg: "customer_id" },
|
|
20
|
+
visible_fields: ["id", "tenant_id", "customer_id", "plan", "invoice_status", "support_ticket_reason", "plan_credit_cents", "credit_reason", "updated_at"],
|
|
21
|
+
kept_out_fields: ["card_token", "raw_payment_method", "internal_risk_score", "private_notes"],
|
|
22
|
+
evidence: { required: true, query_audit: true },
|
|
23
|
+
max_rows: 1,
|
|
24
|
+
proposal: {
|
|
25
|
+
action: "grant_plan_credit",
|
|
26
|
+
operation: { kind: "update" },
|
|
27
|
+
allowed_fields: ["plan_credit_cents", "credit_reason"],
|
|
28
|
+
patch: { plan_credit_cents: { from_arg: "credit_cents" }, credit_reason: { from_arg: "reason" } },
|
|
29
|
+
numeric_bounds: { plan_credit_cents: { minimum: 1, maximum: 50000 } },
|
|
30
|
+
conflict_guard: { column: "updated_at" },
|
|
31
|
+
approval: { mode: "policy", policy: "support_propose_plan_credit_auto_approval", required_role: "support_reviewer" },
|
|
32
|
+
writeback: { mode: "direct_sql" },
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../schemas/effect-result.schema.json",
|
|
3
|
+
"schema_version": "synapsor.effect-result.v1",
|
|
4
|
+
"fixture_id": "eff_support_late_fee_v1",
|
|
5
|
+
"capability_calls": [
|
|
6
|
+
{
|
|
7
|
+
"name": "billing.inspect_invoice",
|
|
8
|
+
"args": {
|
|
9
|
+
"invoice_id": "INV-3001"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "billing.propose_late_fee_waiver",
|
|
14
|
+
"args": {
|
|
15
|
+
"invoice_id": "INV-3001",
|
|
16
|
+
"waiver_reason": "Courtesy waiver supported by SUP-184"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"trusted_context": {
|
|
21
|
+
"tenant_id": "acme",
|
|
22
|
+
"principal": "support-agent-reference",
|
|
23
|
+
"provenance": "static_dev"
|
|
24
|
+
},
|
|
25
|
+
"proposal": {
|
|
26
|
+
"result_category": "proposal_created",
|
|
27
|
+
"capability": "billing.propose_late_fee_waiver",
|
|
28
|
+
"contract_version": "0.1.0",
|
|
29
|
+
"target": {
|
|
30
|
+
"business_object": "invoice",
|
|
31
|
+
"object_id": "INV-3001"
|
|
32
|
+
},
|
|
33
|
+
"diff": {
|
|
34
|
+
"late_fee_cents": {
|
|
35
|
+
"before": 5500,
|
|
36
|
+
"proposed": 2500
|
|
37
|
+
},
|
|
38
|
+
"waiver_reason": {
|
|
39
|
+
"before": null,
|
|
40
|
+
"proposed": "Partial waiver"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"policy": {
|
|
44
|
+
"decision": "auto_approved"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"observed_fields": [
|
|
48
|
+
"invoice_status",
|
|
49
|
+
"late_fee_cents",
|
|
50
|
+
"support_ticket",
|
|
51
|
+
"waiver_reason"
|
|
52
|
+
],
|
|
53
|
+
"evidence": {
|
|
54
|
+
"mode": "fixture",
|
|
55
|
+
"new_source_reads": false
|
|
56
|
+
},
|
|
57
|
+
"source_database_changed": false
|
|
58
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../schemas/effect-result.schema.json",
|
|
3
|
+
"schema_version": "synapsor.effect-result.v1",
|
|
4
|
+
"fixture_id": "eff_support_late_fee_v1",
|
|
5
|
+
"capability_calls": [
|
|
6
|
+
{
|
|
7
|
+
"name": "billing.inspect_invoice",
|
|
8
|
+
"args": {
|
|
9
|
+
"invoice_id": "INV-3001"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "billing.propose_late_fee_waiver",
|
|
14
|
+
"args": {
|
|
15
|
+
"invoice_id": "INV-3001",
|
|
16
|
+
"waiver_reason": "Courtesy waiver supported by SUP-184"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"trusted_context": {
|
|
21
|
+
"tenant_id": "acme",
|
|
22
|
+
"principal": "support-agent-reference",
|
|
23
|
+
"provenance": "static_dev"
|
|
24
|
+
},
|
|
25
|
+
"proposal": {
|
|
26
|
+
"result_category": "proposal_created",
|
|
27
|
+
"capability": "billing.propose_late_fee_waiver",
|
|
28
|
+
"contract_version": "0.1.0",
|
|
29
|
+
"target": {
|
|
30
|
+
"business_object": "invoice",
|
|
31
|
+
"object_id": "INV-3001"
|
|
32
|
+
},
|
|
33
|
+
"diff": {
|
|
34
|
+
"late_fee_cents": {
|
|
35
|
+
"before": 5500,
|
|
36
|
+
"proposed": 0
|
|
37
|
+
},
|
|
38
|
+
"waiver_reason": {
|
|
39
|
+
"before": null,
|
|
40
|
+
"proposed": "Courtesy waiver supported by SUP-184"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"policy": {
|
|
44
|
+
"decision": "pending_review",
|
|
45
|
+
"required_role": "billing_lead"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"observed_fields": [
|
|
49
|
+
"invoice_status",
|
|
50
|
+
"late_fee_cents",
|
|
51
|
+
"support_ticket",
|
|
52
|
+
"waiver_reason"
|
|
53
|
+
],
|
|
54
|
+
"evidence": {
|
|
55
|
+
"mode": "fixture",
|
|
56
|
+
"new_source_reads": false
|
|
57
|
+
},
|
|
58
|
+
"source_database_changed": false
|
|
59
|
+
}
|