@sponsio/sdk 0.1.0-alpha.3 → 0.1.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.
Files changed (176) hide show
  1. package/bin/sponsio.cjs +11 -0
  2. package/contracts/capability/credentials.yaml +8 -8
  3. package/contracts/capability/database.yaml +6 -6
  4. package/contracts/capability/filesystem-strict.yaml +4 -4
  5. package/contracts/capability/filesystem.yaml +9 -9
  6. package/contracts/capability/host-config-integrity.yaml +285 -0
  7. package/contracts/capability/self-modify.yaml +214 -9
  8. package/contracts/capability/shell.yaml +25 -32
  9. package/contracts/capability/subagent.yaml +5 -5
  10. package/contracts/core/llm_safety.yaml +23 -13
  11. package/contracts/incident/claude-code-secret-bypass.yaml +10 -11
  12. package/contracts/incident/cursor-railway-wipe.yaml +3 -3
  13. package/contracts/incident/mcp-composition.yaml +12 -12
  14. package/contracts/incident/openclaw.yaml +53 -45
  15. package/contracts/incident/subagent-escape.yaml +4 -4
  16. package/dist/cli/check.d.ts +2 -0
  17. package/dist/cli/check.d.ts.map +1 -0
  18. package/dist/cli/check.js +283 -0
  19. package/dist/cli/check.js.map +1 -0
  20. package/dist/cli/cli.d.ts +25 -0
  21. package/dist/cli/cli.d.ts.map +1 -0
  22. package/dist/cli/cli.js +459 -0
  23. package/dist/cli/cli.js.map +1 -0
  24. package/dist/cli/config.d.ts +88 -0
  25. package/dist/cli/config.d.ts.map +1 -0
  26. package/dist/cli/config.js +243 -0
  27. package/dist/cli/config.js.map +1 -0
  28. package/dist/cli/demo.d.ts +12 -0
  29. package/dist/cli/demo.d.ts.map +1 -0
  30. package/dist/cli/demo.js +350 -0
  31. package/dist/cli/demo.js.map +1 -0
  32. package/dist/cli/doctor.d.ts +11 -0
  33. package/dist/cli/doctor.d.ts.map +1 -0
  34. package/dist/cli/doctor.js +201 -0
  35. package/dist/cli/doctor.js.map +1 -0
  36. package/dist/cli/eval.d.ts +2 -0
  37. package/dist/cli/eval.d.ts.map +1 -0
  38. package/dist/cli/eval.js +361 -0
  39. package/dist/cli/eval.js.map +1 -0
  40. package/dist/cli/explain.d.ts +2 -0
  41. package/dist/cli/explain.d.ts.map +1 -0
  42. package/dist/cli/explain.js +266 -0
  43. package/dist/cli/explain.js.map +1 -0
  44. package/dist/cli/export-sessions.d.ts +2 -0
  45. package/dist/cli/export-sessions.d.ts.map +1 -0
  46. package/dist/cli/export-sessions.js +182 -0
  47. package/dist/cli/export-sessions.js.map +1 -0
  48. package/dist/cli/export.d.ts +2 -0
  49. package/dist/cli/export.d.ts.map +1 -0
  50. package/dist/cli/export.js +222 -0
  51. package/dist/cli/export.js.map +1 -0
  52. package/dist/cli/extractors/common.d.ts +27 -0
  53. package/dist/cli/extractors/common.d.ts.map +1 -0
  54. package/dist/cli/extractors/common.js +107 -0
  55. package/dist/cli/extractors/common.js.map +1 -0
  56. package/dist/cli/extractors/generic.d.ts +37 -0
  57. package/dist/cli/extractors/generic.d.ts.map +1 -0
  58. package/dist/cli/extractors/generic.js +185 -0
  59. package/dist/cli/extractors/generic.js.map +1 -0
  60. package/dist/cli/extractors/langchain.d.ts +18 -0
  61. package/dist/cli/extractors/langchain.d.ts.map +1 -0
  62. package/dist/cli/extractors/langchain.js +86 -0
  63. package/dist/cli/extractors/langchain.js.map +1 -0
  64. package/dist/cli/extractors/vercel.d.ts +17 -0
  65. package/dist/cli/extractors/vercel.d.ts.map +1 -0
  66. package/dist/cli/extractors/vercel.js +46 -0
  67. package/dist/cli/extractors/vercel.js.map +1 -0
  68. package/dist/cli/index.d.ts +38 -0
  69. package/dist/cli/index.d.ts.map +1 -0
  70. package/dist/cli/index.js +146 -0
  71. package/dist/cli/index.js.map +1 -0
  72. package/dist/cli/init.d.ts +2 -0
  73. package/dist/cli/init.d.ts.map +1 -0
  74. package/dist/cli/init.js +177 -0
  75. package/dist/cli/init.js.map +1 -0
  76. package/dist/cli/mode.d.ts +2 -0
  77. package/dist/cli/mode.d.ts.map +1 -0
  78. package/dist/cli/mode.js +71 -0
  79. package/dist/cli/mode.js.map +1 -0
  80. package/dist/cli/onboard.d.ts +88 -0
  81. package/dist/cli/onboard.d.ts.map +1 -0
  82. package/dist/cli/onboard.js +758 -0
  83. package/dist/cli/onboard.js.map +1 -0
  84. package/dist/cli/otlp.d.ts +27 -0
  85. package/dist/cli/otlp.d.ts.map +1 -0
  86. package/dist/cli/otlp.js +79 -0
  87. package/dist/cli/otlp.js.map +1 -0
  88. package/dist/cli/package.json +1 -0
  89. package/dist/cli/packs.d.ts +16 -0
  90. package/dist/cli/packs.d.ts.map +1 -0
  91. package/dist/cli/packs.js +184 -0
  92. package/dist/cli/packs.js.map +1 -0
  93. package/dist/cli/patterns.d.ts +15 -0
  94. package/dist/cli/patterns.d.ts.map +1 -0
  95. package/dist/cli/patterns.js +135 -0
  96. package/dist/cli/patterns.js.map +1 -0
  97. package/dist/cli/prompt.d.ts +2 -0
  98. package/dist/cli/prompt.d.ts.map +1 -0
  99. package/dist/cli/prompt.js +54 -0
  100. package/dist/cli/prompt.js.map +1 -0
  101. package/dist/cli/replay.d.ts +2 -0
  102. package/dist/cli/replay.d.ts.map +1 -0
  103. package/dist/cli/replay.js +196 -0
  104. package/dist/cli/replay.js.map +1 -0
  105. package/dist/cli/report.d.ts +57 -0
  106. package/dist/cli/report.d.ts.map +1 -0
  107. package/dist/cli/report.js +298 -0
  108. package/dist/cli/report.js.map +1 -0
  109. package/dist/cli/scan.d.ts +2 -0
  110. package/dist/cli/scan.d.ts.map +1 -0
  111. package/dist/cli/scan.js +172 -0
  112. package/dist/cli/scan.js.map +1 -0
  113. package/dist/cli/skill.d.ts +15 -0
  114. package/dist/cli/skill.d.ts.map +1 -0
  115. package/dist/cli/skill.js +131 -0
  116. package/dist/cli/skill.js.map +1 -0
  117. package/dist/cli/tokens.d.ts +31 -0
  118. package/dist/cli/tokens.d.ts.map +1 -0
  119. package/dist/cli/tokens.js +73 -0
  120. package/dist/cli/tokens.js.map +1 -0
  121. package/dist/cli/types.d.ts +46 -0
  122. package/dist/cli/types.d.ts.map +1 -0
  123. package/dist/cli/types.js +3 -0
  124. package/dist/cli/types.js.map +1 -0
  125. package/dist/cli/validate.d.ts +19 -0
  126. package/dist/cli/validate.d.ts.map +1 -0
  127. package/dist/cli/validate.js +205 -0
  128. package/dist/cli/validate.js.map +1 -0
  129. package/dist/cli/zod.d.ts +36 -0
  130. package/dist/cli/zod.d.ts.map +1 -0
  131. package/dist/cli/zod.js +226 -0
  132. package/dist/cli/zod.js.map +1 -0
  133. package/dist/contract.d.ts +13 -13
  134. package/dist/contract.d.ts.map +1 -1
  135. package/dist/contract.js +21 -21
  136. package/dist/contract.js.map +1 -1
  137. package/dist/core/config-loader.d.ts +1 -1
  138. package/dist/core/config-loader.js +35 -17
  139. package/dist/core/config-loader.js.map +1 -1
  140. package/dist/core/grounding.d.ts +49 -2
  141. package/dist/core/grounding.d.ts.map +1 -1
  142. package/dist/core/grounding.js +302 -142
  143. package/dist/core/grounding.js.map +1 -1
  144. package/dist/core/nl-parser.d.ts.map +1 -1
  145. package/dist/core/nl-parser.js +74 -2
  146. package/dist/core/nl-parser.js.map +1 -1
  147. package/dist/core/pattern-factory.d.ts +4 -4
  148. package/dist/core/pattern-factory.d.ts.map +1 -1
  149. package/dist/core/pattern-factory.js +46 -4
  150. package/dist/core/pattern-factory.js.map +1 -1
  151. package/dist/core/patterns.d.ts +78 -4
  152. package/dist/core/patterns.d.ts.map +1 -1
  153. package/dist/core/patterns.js +211 -3
  154. package/dist/core/patterns.js.map +1 -1
  155. package/dist/core/spans.d.ts +1 -1
  156. package/dist/core/spans.d.ts.map +1 -1
  157. package/dist/core/spans.js.map +1 -1
  158. package/dist/core/sto.d.ts +32 -62
  159. package/dist/core/sto.d.ts.map +1 -1
  160. package/dist/core/sto.js +32 -90
  161. package/dist/core/sto.js.map +1 -1
  162. package/dist/index.d.ts +77 -32
  163. package/dist/index.d.ts.map +1 -1
  164. package/dist/index.js +172 -133
  165. package/dist/index.js.map +1 -1
  166. package/dist/integrations/openai-agents.d.ts.map +1 -1
  167. package/dist/integrations/openai-agents.js +5 -4
  168. package/dist/integrations/openai-agents.js.map +1 -1
  169. package/dist/render/tokens.d.ts.map +1 -1
  170. package/dist/render/tokens.js +1 -0
  171. package/dist/render/tokens.js.map +1 -1
  172. package/package.json +28 -4
  173. package/prompts/onboard.md +250 -0
  174. package/prompts/refresh.md +118 -0
  175. package/prompts/scan.md +125 -0
  176. package/skills/SKILL.md +502 -0
@@ -0,0 +1,502 @@
1
+ ---
2
+ name: sponsio
3
+ description: Install, observe, tune, enforce, and periodically refresh Sponsio — a runtime contract layer for LLM agents that blocks unsafe tool calls and scores output quality against declared rules. Use when the user wants to set up / add / install Sponsio, add guardrails or runtime safety to an LLM agent, generate or refine a sponsio.yaml, audit tool configurations for risks (data leaks, unguarded writes, missing confirmations), explain or review existing contracts, check what Sponsio would have blocked (`sponsio report`), refresh the contract library from recent traces (`sponsio refresh`), move from observe to enforce mode, or debug why a contract is (or isn't) firing. Triggers on phrases like "set up sponsio", "add sponsio", "install sponsio", "add guardrails", "monitor my agent", "harden my agent", "audit my agent", "generate contracts", "explain my sponsio.yaml", "sponsio report", "refresh contracts", "update my sponsio.yaml from traces", "flip to enforce", "false positive", "why is this rule firing".
4
+ ---
5
+
6
+ # Sponsio — Agent Safety Lifecycle Companion
7
+
8
+ Sponsio is a Python/TypeScript runtime safety layer for LLM agents: it evaluates deterministic contracts against each tool call and can block (enforce) or just log (observe) violations. (LLM-judged stochastic contracts — tone, prompt-injection, semantic PII, scope respect — are a Sponsio Cloud feature available via `pip install sponsio[cloud]`; the OSS engine is det-only.) This skill covers the full lifecycle — first-time setup, contract authoring/review, observe-mode tuning, and flipping to enforce — by orchestrating Sponsio's CLI and explaining its output in plain language.
9
+
10
+ This skill does NOT reimplement Sponsio's logic; it calls the CLI and interprets results.
11
+
12
+ ## When to use this skill
13
+
14
+ Dispatch by what the user is trying to do. Pick ONE workflow and follow it; do not run multiple workflows in one turn.
15
+
16
+ | User is… | → Workflow |
17
+ |---|---|
18
+ | Setting up Sponsio for the first time in a project ("add sponsio", "install sponsio", "add guardrails") | **W1 — Initial setup** |
19
+ | Handing you a codebase and asking "what could go wrong?" / wants a fresh contract file from scratch / has a policy doc to encode | **W2 — Audit & refine** |
20
+ | Has Sponsio running in observe mode and wants to review violations, tune thresholds, silence false positives | **W3 — Tune in observe** |
21
+ | Wants to re-mine contracts from accumulated production traces / periodically maintain the library | **W3b — Refresh from traces** |
22
+ | Ready to ship — wants to move from observe to enforce, needs regression confidence | **W4 — Flip to enforce** |
23
+ | Sponsio errored, a rule isn't firing when it should, a rule is firing when it shouldn't | **W5 — Troubleshoot** |
24
+
25
+ Do NOT trigger for: general LLM-safety discussions not tied to a specific codebase; non-agent code review (linting, correctness).
26
+
27
+ ## Prerequisites (run silently before any workflow)
28
+
29
+ ```bash
30
+ sponsio --version
31
+ ```
32
+
33
+ - Not found → install: `pip install sponsio` (or `pip install -e ".[all]"` from a local clone).
34
+ - For `--llm` inference, check: `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` / `GOOGLE_API_KEY`. Absent → still proceed; AST-based extraction and all of W3/W4/W5 work with zero keys.
35
+
36
+ ---
37
+
38
+ ## W1 — Initial setup
39
+
40
+ Goal: from "project has no Sponsio" to "agent runs under observe mode with a sane contract file", in one command.
41
+
42
+ ### Steps
43
+
44
+ 1. Run the one-shot entry point:
45
+
46
+ ```bash
47
+ sponsio onboard . --apply
48
+ ```
49
+
50
+ `onboard` detects framework (langgraph / langchain / crewai / openai_agents / claude_agent / vercel-ai / no-framework), picks an LLM provider if available, auto-selects contract packs (see "Auto-selected packs" below), writes `sponsio.yaml` in **observe** mode, and with `--apply` patches the agent entry file with a two-line wrap (backup at `.sponsio.bak`). Falls back to printing the patch snippet if the framework isn't auto-patchable.
51
+
52
+ 2. After `onboard` finishes, show the user three things — do not skip any:
53
+ - The generated `sponsio.yaml` (read it back and summarize: packs included, tools renamed, mode).
54
+ - The applied patch (from `report.apply_result.diff`) or the printed snippet.
55
+ - Any `sponsio doctor` warn/fail lines.
56
+
57
+ 3. Explain observe mode explicitly: "Nothing is blocked on day 1. Every contract is still evaluated; violations are logged to `~/.sponsio/sessions/<agent_id>/*.jsonl` and (if a dashboard is configured) pushed there. Use `sponsio report --since 24h` after a day of real traffic to see what would have been blocked."
58
+
59
+ 4. If `sponsio doctor` failed (not warned, failed), stop and surface it — don't let the user run their agent thinking the install is healthy when it isn't.
60
+
61
+ ### Auto-selected packs
62
+
63
+ `sponsio onboard` uses simple, conservative heuristics:
64
+
65
+ | Pack | Auto-included when… | Notes |
66
+ |---|---|---|
67
+ | `sponsio:core/universal` | Always | Empty stub on OSS — kept so existing `include:` lines don't error. The output-quality contracts (injection / jailbreak / toxic / PII / harm) live in `sponsio:core/llm_safety` and require Sponsio Cloud (`pip install sponsio[cloud]`) |
68
+ | `sponsio:core/runaway` | Framework runs a multi-step loop (langgraph/crewai/…) | token budget, delegation depth, loop detection; no LLM calls |
69
+ | `sponsio:capability/shell` | A tool name matches `{bash, shell, exec, execute, execute_command, run_command, run_shell, run_bash, terminal, subprocess}` | Auto-fills `tool_rename:` if the user's tool name isn't the canonical `exec` |
70
+ | `sponsio:capability/filesystem` | A tool name matches `{read, read_file, open_file, write, write_file, edit, edit_file, apply_patch, patch_file, ...}` | Auto-fills `tool_rename:` and `workspace:` |
71
+ | `sponsio:incident/openclaw` | Never auto-included — opt-in only | CVE-derived rules for a specific vendor incident |
72
+
73
+ `sponsio packs` lists all shipped packs with live rule counts and include specs.
74
+
75
+ ### Do NOT
76
+
77
+ - Do NOT edit `sponsio/contracts/*.yaml` inside the installed package — those are the shipped packs; they're read-only. Adjustments go in the user's `sponsio.yaml` via `overrides:` or `contracts:`.
78
+ - Do NOT flip `mode: enforce` during W1. The whole point of observe mode is to find false positives before they break production.
79
+
80
+ ---
81
+
82
+ ## W2 — Audit & refine (from scratch, or deepen an existing yaml)
83
+
84
+ Goal: produce or improve a `sponsio.yaml` from code / policy docs / traces, and explain every contract in plain language.
85
+
86
+ ### Decide which sources to use
87
+
88
+ Sponsio contracts come from four sources, mixable in one yaml:
89
+
90
+ | # | Source | What it is | Command |
91
+ |---|---|---|---|
92
+ | 1 | **Shipped packs** | Pre-built, parameterized rule sets (`sponsio:core/universal`, `sponsio:capability/shell`, …) | Hand-add `include: [sponsio:<spec>]` — or W1's `onboard` does it automatically |
93
+ | 2 | **Extraction** | AST + optional LLM inference from your code / policy docs / execution traces | `sponsio scan <paths> [--llm] [--policy <doc>] [-t <trace-glob>]` |
94
+ | 3 | **User input** | An NL sentence or a structured dict the user writes | Hand-edit `sponsio.yaml`; validate a single NL string with `sponsio validate "<NL>"` |
95
+ | 4 | **Pattern library** | Deterministic parameterized templates (`rate_limit`, `must_precede`, `arg_blacklist`, …) — full list via `sponsio patterns`. Stochastic templates (`injection_free`, `tone`, `llm_judge`, …) live in Sponsio Cloud | `sponsio patterns` to browse; hand-write the YAML entry |
96
+
97
+ Match the user's input to the source(s):
98
+
99
+ - "Explain / review my `sponsio.yaml`" → source 1 and/or others already applied; jump to "Explain contracts" below.
100
+ - "Scan my agent code" → source 2, code-only.
101
+ - "We have a security policy document" → source 2, add `--policy <doc> --llm`.
102
+ - "We already have session logs / OTLP traces" → source 2, add `-t '<glob>'` (trace mining; no LLM needed).
103
+ - "I know the pattern I want but not the syntax" → source 4, then show them the yaml entry.
104
+
105
+ If ambiguous: ask ONE question — "(a) scan your code, (b) extract from a policy document, or (c) mine from traces?"
106
+
107
+ ### Run scan (when extraction is needed)
108
+
109
+ ```bash
110
+ # AST-only (fast, no keys):
111
+ sponsio scan <PATHS> --agent <AGENT> -o ./sponsio.yaml
112
+
113
+ # + LLM inference:
114
+ sponsio scan <PATHS> --agent <AGENT> --llm -o ./sponsio.yaml
115
+
116
+ # + policy doc:
117
+ sponsio scan <PATHS> --policy <POLICY.md> --llm -o ./sponsio.yaml
118
+
119
+ # + trace mining (works on OTLP/JSON, OTLP JSONL, native, Sponsio session logs):
120
+ sponsio scan <PATHS> -t '~/.sponsio/sessions/<agent>/*.jsonl' -o ./sponsio.yaml
121
+ ```
122
+
123
+ Scan auto-validates before writing; only contracts that parse cleanly are saved. Source-tagged with `source: scan` / `source: policy` / `source: trace`. The `source: trace` subset is the one `sponsio refresh` maintains over time (W3b).
124
+
125
+ ### Validate existing yaml (explain-only path)
126
+
127
+ ```bash
128
+ sponsio validate --config ./sponsio.yaml --json
129
+ ```
130
+
131
+ JSON shape per entry: `{nl, ok, type: "det"|"sto"|"unknown", pattern, formula, agent, section}`. `ok: false` means the NL didn't match any pattern — surface those as "ambiguous — needs refinement".
132
+
133
+ ### Explain contracts (use this as the report template)
134
+
135
+ ```markdown
136
+ ## Sponsio contract summary — <agent>
137
+
138
+ **Setup**: <N> contracts total, <sources breakdown>. Mode: <observe|enforce>.
139
+
140
+ ### Active packs
141
+ - `sponsio:core/universal` (empty stub on OSS — judge-based output safety lives in `sponsio:core/llm_safety` and requires Sponsio Cloud)
142
+ - `sponsio:capability/shell` (11 det) — guards on <tool name after rename>
143
+ <...>
144
+
145
+ ### Contracts explained
146
+ Each contract is an assumption → enforcement pair. State the assumption explicitly even when absent ("unconditional"), because the assumption defines **when** the enforcement applies.
147
+
148
+ - **When**: the agent has called `modify_order`
149
+ **Then**: `get_order_details` must have happened earlier.
150
+ **Runtime effect**: if unmet, the call is blocked (or logged, in observe) and the agent is told why.
151
+
152
+ - **When**: unconditional
153
+ **Then**: `send_email` is called at most 5 times per session.
154
+ **Runtime effect**: the 6th call is blocked.
155
+
156
+ ### Ambiguous contracts (optional — only if validate flagged any)
157
+ - `"..."` — didn't match any pattern; rephrase or rewrite as a structured dict.
158
+
159
+ ### Next steps
160
+ 1. Review; prune anything that doesn't fit the agent's real job.
161
+ 2. Run in observe mode for 1–2 days.
162
+ 3. `sponsio report --since 24h` to see what would have been blocked (W3).
163
+ 4. Prune false positives, then flip `mode: enforce` (W4).
164
+ ```
165
+
166
+ Keep it dense. Don't pad with "consult an expert" disclaimers — the user is already using the expert.
167
+
168
+ ---
169
+
170
+ ## W3 — Tune in observe
171
+
172
+ Goal: look at what Sponsio logged during observe mode, decide which violations are real vs false positives, and adjust `sponsio.yaml` accordingly. **Never flip mode during this workflow.**
173
+
174
+ ### Steps
175
+
176
+ 1. Pull the violation report:
177
+
178
+ ```bash
179
+ sponsio report --agent <AGENT> --since 24h
180
+ ```
181
+
182
+ 2. For each violation cluster:
183
+ - **Real violation** → leave the contract as-is. Note it for the user.
184
+ - **False positive** → adjust `sponsio.yaml`. Use this decision tree:
185
+
186
+ | Situation | Edit |
187
+ |---|---|
188
+ | Pack-shipped rule is too strict globally | Add an `overrides: - match: {desc: "..."}` entry with tuned args |
189
+ | Pack-shipped rule doesn't apply to this agent at all | `overrides: - match: {...}, disabled: true` |
190
+ | Rule's threshold is wrong (e.g. rate_limit N) | `overrides: ..., args: [<tool>, <new_N>]` |
191
+ | Rule conflicts with a legitimate workflow that Sponsio couldn't infer | Weaken via an `A:` (assumption) so it only fires in the specific unsafe context |
192
+ | User's own hand-written contract is wrong | Edit `contracts:` directly |
193
+
194
+ Use `desc`, `pattern`, `pack_source`, or `source` as the `match:` key (see the YAML schema below).
195
+
196
+ 3. Do NOT edit a contract you haven't seen a violation for. "It looks strict" isn't a reason to remove it in observe mode — it's the reason to leave it.
197
+
198
+ 4. After each edit, verify the yaml still parses: `sponsio validate --config sponsio.yaml`.
199
+
200
+ ### Using assumptions to relax (the right way)
201
+
202
+ When a rule is correct in principle but too broad in practice, prefer adding an `A:` over disabling:
203
+
204
+ ```yaml
205
+ overrides:
206
+ - match: { desc: "Each exec call needs its own confirm_reconfirmed" }
207
+ A: "called `confirm_reconfirmed`"
208
+ ```
209
+
210
+ With the assumption, the rule stays silent until the integration actually emits the marker — it's vacuous-true without the precondition, enforced once the precondition holds. This is the preferred pattern for "I want this rule eventually but not today".
211
+
212
+ ---
213
+
214
+ ## W3b — Refresh from traces
215
+
216
+ Goal: treat `sponsio.yaml` as a **living library**, not a one-shot output. As real session logs accumulate, re-mine them to discover new patterns and retire stale ones, without clobbering anything the user wrote or tuned.
217
+
218
+ ### When to run
219
+
220
+ - Weekly / sprint boundary — recommended cadence for an agent seeing live traffic.
221
+ - After a material behavior change (new tools, new workflow, new integration).
222
+ - Before flipping to enforce (W4) — catches late-arriving trace-sourced rules that weren't present in the original scan.
223
+
224
+ ### Steps
225
+
226
+ 1. Dry-run first. Always.
227
+
228
+ ```bash
229
+ sponsio refresh --since 7d
230
+ ```
231
+
232
+ Prints a structured diff per agent:
233
+
234
+ ```
235
+ Agent: support_bot
236
+ + new must_precede(validate_payment, charge_card)
237
+ ~ drifted rate_limit(send_email, 5) → args [send_email, 12]
238
+ - stale idempotent(list_users) (not re-observed)
239
+ = 8 unchanged (source: trace, re-observed)
240
+ = 12 preserved (user / scan / policy / overrides — not touched)
241
+ ```
242
+
243
+ 2. Review with the user. For each bucket:
244
+ - `+ new` — "the agent started doing X that your current yaml doesn't cover." User usually wants this.
245
+ - `~ drifted` — threshold moved. If new value is bigger, the rule was too tight; if smaller, production tightened up. User decides.
246
+ - `- stale` — rule hasn't fired in this window. **Not necessarily dead** — could just be rare. Conservative default is `--mode add-only` (never remove), which we recommend for the first few refreshes until the user trusts the window size.
247
+ - `= preserved` — these include every user rule, `source: scan`, `source: policy`, and anything under `overrides:`. The count being non-zero is the load-bearing invariant: refresh **only** ever changes `source: trace` entries.
248
+
249
+ 3. Apply.
250
+
251
+ ```bash
252
+ sponsio refresh --since 7d --apply # default mode: replace-trace
253
+ sponsio refresh --since 7d --apply --mode add-only # conservative: never remove
254
+ ```
255
+
256
+ Writes `sponsio.yaml` and backs the old file up to `sponsio.yaml.sponsio.bak`. **Comments in the YAML are not preserved** — the backup is how users recover any prose annotations they'd inlined. Warn them of this before running.
257
+
258
+ ### Mode selection
259
+
260
+ | Situation | Mode |
261
+ |---|---|
262
+ | First time running refresh, or small trace window | `add-only` (never removes) |
263
+ | Healthy trace volume, stable agent behavior | `replace-trace` (default — recent traces are authoritative) |
264
+ | Window just covers a launch / migration / incident (abnormal traffic) | Skip — artifact traces mislead the miner |
265
+
266
+ ### Do NOT
267
+
268
+ - Do NOT run refresh on traces from an agent still in early iteration. Wait until the workflow is stable; otherwise every sprint invalidates the library.
269
+ - Do NOT use `--apply` without first showing the dry-run diff.
270
+ - Do NOT remove `source: scan` / `source: policy` entries in the yaml by hand just because refresh doesn't touch them — they represent knowledge refresh can't reconstruct (code AST, policy docs). If you think one is wrong, edit `overrides:`, don't delete.
271
+
272
+ ---
273
+
274
+ ## W4 — Flip to enforce
275
+
276
+ Goal: move from observe to enforce with regression confidence — no more logging, actual blocking. **This is a production change**; don't skip the checks.
277
+
278
+ ### Steps
279
+
280
+ 1. **Regression test on real traces.** Pick a representative trace from `~/.sponsio/sessions/<agent>/` and replay:
281
+
282
+ ```bash
283
+ sponsio check --trace <trace.jsonl> --config sponsio.yaml --agent <agent>
284
+ ```
285
+
286
+ Exit 0 = every contract passes on this trace. Non-zero = something would have been blocked. If non-zero, go back to W3.
287
+
288
+ 2. **Flip the mode.** Two options:
289
+
290
+ a. Pin in yaml (permanent):
291
+ ```yaml
292
+ runtime:
293
+ mode: enforce
294
+ ```
295
+
296
+ b. Flip via env var (reversible, no code change):
297
+ ```bash
298
+ SPONSIO_MODE=enforce python -m your_agent
299
+ ```
300
+
301
+ Precedence: explicit ctor arg > env > yaml > default.
302
+
303
+ 3. **Keep the dashboard.** In production, point OTEL export at Sponsio's collector (`POST /api/otel/v1/traces`) or keep using `~/.sponsio/sessions/*.jsonl` + `sponsio report`. Don't run `sponsio serve --dev` in prod — that's a dev tool.
304
+
305
+ 4. Tell the user how to roll back: `SPONSIO_MODE=observe` without redeploying.
306
+
307
+ ---
308
+
309
+ ## W5 — Troubleshoot
310
+
311
+ Common symptoms and their fixes.
312
+
313
+ ### "LLM extraction call failed: cannot import name 'genai' from 'google'"
314
+
315
+ User has `google-generativeai` not `google-genai`. Fix:
316
+ ```bash
317
+ pip install -U 'sponsio[llm]'
318
+ ```
319
+
320
+ ### "ConfigError: include 'sponsio:...': pack must define exactly one agent named '*' (the template)"
321
+
322
+ The pack file's `agents:` uses a literal agent name instead of `"*"`. If it's a user pack, rename. If it's a shipped pack, that's a Sponsio bug — file an issue.
323
+
324
+ ### "ConfigError: Agent '<x>': pattern '<y>' uses the '<workspace>/' placeholder but the agent has no 'workspace:' set."
325
+
326
+ Add `workspace: /path/to/project/root` under the agent in `sponsio.yaml`. The `filesystem` and `openclaw` packs need this.
327
+
328
+ ### "A rule I expected to fire isn't firing."
329
+
330
+ Check in this order:
331
+
332
+ 1. `sponsio validate --config sponsio.yaml --json` — does the contract parse? `ok: false` means it was silently dropped.
333
+ 2. The tool name in the contract matches the actual tool name the agent calls (LangGraph `node_id` vs the `@tool` Python name is a common trap; see `tool_rename:`).
334
+ 3. The rule has an `A:` (assumption) that isn't holding. Look at the trace — is the precondition ever true?
335
+ 4. The rule is a `sto` pattern (`injection_free`, `tone_*`, `semantic_pii_free`, …). The OSS engine ships no evaluator for those — `pip install sponsio[cloud]` to activate the judge pipeline. Without Cloud, OSS rejects the contract loudly at config load.
336
+
337
+ ### "A rule is firing that shouldn't."
338
+
339
+ Jump to W3. Don't disable in a panic — add a targeted `overrides:` entry with a `match:` clause, so the adjustment is traceable.
340
+
341
+ ### "`sponsio onboard` detected the wrong framework."
342
+
343
+ `onboard` doesn't currently take a `--framework` override flag. Fall back to the manual two-step:
344
+
345
+ ```bash
346
+ sponsio init --agent <id>
347
+ sponsio scan <paths> --agent <id> -o sponsio.yaml
348
+ ```
349
+
350
+ Then hand-apply the integration snippet for your framework from the "Integration snippets" section below.
351
+
352
+ ---
353
+
354
+ ## YAML schema (what contracts look like)
355
+
356
+ Every contract is an `(assumption, enforcement)` pair. Both short keys (`A` / `E`) and long keys (`assumption` / `enforcement`) are accepted; mixing both forms of the same field in one entry raises `ConfigError`.
357
+
358
+ ```yaml
359
+ version: "1"
360
+
361
+ agents:
362
+ <agent_id>: # dict keyed by agent_id, NOT a list
363
+ workspace: /path/to/project # required by packs that use <workspace>/
364
+
365
+ include: # pre-built packs (see W1 "Auto-selected packs")
366
+ - sponsio:core/universal
367
+ - sponsio:capability/shell
368
+
369
+ tool_rename: # map the pack's canonical names to the
370
+ exec: run_bash # host's actual tool names
371
+ read: read_file
372
+
373
+ overrides: # tune shipped packs without forking them
374
+ - match: { desc: "Each exec call needs its own confirm_reconfirmed" }
375
+ A: "called `confirm_reconfirmed`"
376
+ - match: { pattern: rate_limit, args: [send_email, 5] }
377
+ args: [send_email, 20]
378
+ - match: { pack_source: sponsio:incident/openclaw, desc: "..." }
379
+ disabled: true
380
+
381
+ contracts: # hand-written + scan-inferred contracts
382
+ # NL form (short keys)
383
+ - A: "called `modify_order`"
384
+ G: "must call `get_order_details` before `modify_order`"
385
+ # Omit A for unconditional
386
+ - G: "tool `send_email` is rate-limited to 5 per session"
387
+ # Structured dict (what scan emits for det patterns)
388
+ - G:
389
+ pattern: must_precede
390
+ args: [check_policy, issue_refund]
391
+ source: scan
392
+
393
+ runtime:
394
+ mode: observe # "observe" | "enforce"
395
+ dashboard: http://localhost:8000 # URL | true | false | null
396
+
397
+ judge: # Sponsio Cloud only — required when any include uses sto patterns
398
+ provider: openai # openai | anthropic | gemini
399
+ model: gpt-4o-mini
400
+ ```
401
+
402
+ Both `A` and `G` accept: scalar NL string, list (AND), or structured dict `{pattern, args, source?}`.
403
+
404
+ Placeholders rewritten at include-time: `<workspace>/` (from agent's `workspace:`), `<agent>` (from agent id).
405
+
406
+ ---
407
+
408
+ ## Pattern reference
409
+
410
+ Full list: `sponsio patterns` (deterministic templates only). LLM-judged stochastic templates are a Sponsio Cloud feature.
411
+
412
+ ### Core Temporal (14 det)
413
+ | Pattern | Meaning |
414
+ |---|---|
415
+ | `must_precede(A, B)` | A must be called before B |
416
+ | `always_followed_by(A, B)` | Every A must eventually be followed by B |
417
+ | `no_reversal(A, B)` | Once A fires, B is permanently forbidden |
418
+ | `requires_permission(tool, perm)` | Agent must hold `perm` before `tool` |
419
+ | `no_data_leak(source, sink)` | Data from source must not reach sink |
420
+ | `mutual_exclusion(A, B)` | At most one of A, B per trace |
421
+ | `rate_limit(tool, N)` | Tool at most N calls per session |
422
+ | `idempotent(tool)` | Tool at most once |
423
+ | `deadline(trigger, action, N)` | Action within N steps of trigger |
424
+ | `must_confirm(action)` | A confirmation tool must precede action |
425
+ | `cooldown(action, N)` | Min N steps between consecutive calls |
426
+ | `segregation_of_duty(A, B)` | Same agent can't perform both |
427
+ | `bounded_retry(action, N)` | At most N retries |
428
+ | `loop_detection(action, N)` | Max N consecutive calls |
429
+
430
+ ### Argument & Path (4 det)
431
+ `arg_blacklist(tool, param, patterns)` · `scope_limit(tool, paths)` · `arg_length_limit(tool, param, max)` · `data_intact(tool, paths)`
432
+
433
+ ### OWASP Agentic (8 det)
434
+ `destructive_action_gate` · `untrusted_source_gate` (returns A,E pair) · `required_steps_completion` · `tool_allowlist` · `dangerous_bash_commands` · `dangerous_sql_verbs` · `irreversible_once` · `confirm_after_source` (returns A,E pair)
435
+
436
+ ### Resource & Delegation (3 det)
437
+ `token_budget(max_tokens)` · `arg_value_range(tool, field, min, max)` · `delegation_depth_limit(max_depth)`
438
+
439
+ ### Soft Evaluators
440
+ LLM-judged stochastic atoms (`tone_*`, `relevance`, `llm_judge`, `injection_free`, `semantic_pii_free`, `scope_respect`, …) ship in Sponsio Cloud (`pip install sponsio[cloud]`). The OSS engine refuses to load them at config time with a clear pointer. Det response-quality patterns (`no_pii` regex, `max_length`, `no_keywords`) remain available in OSS.
441
+
442
+ ---
443
+
444
+ ## Integration snippets (W1 fallback when `--apply` can't patch)
445
+
446
+ All frameworks share the same `sponsio.yaml`; only the wiring at the agent entry point differs.
447
+
448
+ **LangGraph** (Python):
449
+ ```python
450
+ from sponsio.langgraph import Sponsio
451
+ guard = Sponsio(agent_id="support_bot", config="sponsio.yaml")
452
+ graph = guard.wrap_graph(graph)
453
+ ```
454
+
455
+ **OpenAI Agents SDK** (Python):
456
+ ```python
457
+ from sponsio.openai_agents import Sponsio
458
+ guard = Sponsio(agent_id="support_bot", config="sponsio.yaml")
459
+ agent = Agent(tools=guard.wrap(tools))
460
+ ```
461
+
462
+ **Claude Agent SDK / CrewAI**: analogous — `from sponsio.claude_agent import Sponsio` / `from sponsio.crewai import Sponsio`, then `guard.wrap(tools)`.
463
+
464
+ **No framework** (bare LLM calls):
465
+ ```python
466
+ from sponsio import Sponsio
467
+ guard = Sponsio(agent_id="support_bot", config="sponsio.yaml")
468
+ guard.guard_before(tool_name="...", tool_args={...}) # before the call
469
+ guard.guard_after(output="...") # after
470
+ ```
471
+
472
+ **TypeScript** (`@sponsio/core`):
473
+ ```ts
474
+ import { Sponsio } from "@sponsio/core";
475
+ const guard = new Sponsio({ agentId: "support_bot", config: "sponsio.yaml" });
476
+ await guard.guardBefore({ toolName: "...", toolArgs: {...} });
477
+ ```
478
+
479
+ ---
480
+
481
+ ## What this skill does NOT do
482
+
483
+ - Does NOT run the agent. It orchestrates the CLI and explains results.
484
+ - Does NOT guarantee contracts are complete — they're proposals from heuristics + LLM; the user reviews.
485
+ - Does NOT modify code outside the agent entry file (and only in W1 with `--apply`, with a backup).
486
+
487
+ If asked for something out of scope (e.g., "also check my DB schema"), say so and offer the closest thing Sponsio can do.
488
+
489
+ ---
490
+
491
+ ## Public API surface (for Sponsio maintainers — do not break)
492
+
493
+ This skill only uses these. Internal refactors are safe as long as these stay stable.
494
+
495
+ 1. **CLI**: `sponsio onboard [--apply]`, `sponsio scan PATHS [--agent N] [--llm] [--policy P] [-t GLOB] [-o FILE] [--append]`, `sponsio refresh [-c FILE] [-a AGENT] [-t GLOB] [--since DUR] [--mode add-only|replace-trace] [--apply]`, `sponsio validate [--config FILE | "NL string"] [--json]`, `sponsio check --trace FILE --config FILE --agent ID`, `sponsio report --agent ID --since DUR`, `sponsio doctor`, `sponsio patterns`, `sponsio packs`, `sponsio skill install [--tool cursor|claude|codex|both|auto]`. Exit 0 on success.
496
+ 2. **YAML**: top-level `agents:` as dict; each agent has optional `include:` / `tool_rename:` / `overrides:` / `workspace:` and required `contracts:`; top-level `runtime:` and `judge:`.
497
+ 3. **Patterns**: names in the table above keep their semantics. Renaming is a breaking change for this skill.
498
+ 4. **`validate --json` shape**: per-contract `ok` / `type` / `pattern` / `formula` / `agent`.
499
+ 5. **`onboard` JSON report shape**: `out_path` / `tools_count` / `contracts_count` / `mode` / `framework` / `provider` / `doctor_results` / `apply_result.diff`.
500
+ 6. **Session log path**: `~/.sponsio/sessions/<agent_id>/*.jsonl` — `sponsio report` / `sponsio check --trace` both read this.
501
+
502
+ Changes to 1–6 should bump Sponsio's minor version and update this SKILL.md. Changes to `UnifiedExtractor`, `CodeAnalyzer`, pattern Python signatures, or other internals are NOT part of this skill's contract.