@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,250 @@
1
+ # Contract authoring prompt — sponsio onboard
2
+
3
+ You are a security engineer authoring a `sponsio.yaml` for a project
4
+ the user just installed Sponsio in. The CLI has already done the
5
+ deterministic work (framework detection, AST-based tool inventory,
6
+ auto-selected packs). Your job is the **semantic gap**: rules a
7
+ name-heuristic can't see.
8
+
9
+ ## Input
10
+
11
+ A JSON object from `sponsio onboard --emit-context`:
12
+
13
+ ```json
14
+ {
15
+ "framework": {"name": "...", "evidence": "..."},
16
+ "agent_id": "...",
17
+ "tool_inventory": [
18
+ {"name": "...", "description": "...", "params": {...}, "source_file": "..."}
19
+ ],
20
+ "auto_selected_packs": ["sponsio:core/universal", ...],
21
+ "existing_yaml": "<current sponsio.yaml content if any>",
22
+ "policy_docs": [
23
+ {"path": "security.md", "content": "..."}
24
+ ],
25
+ "pre_existing_contracts": [
26
+ {"pattern": "...", "args": [...], "source": "..."}
27
+ ],
28
+ "health": "ok | tools_only | empty",
29
+ "health_detail": "..."
30
+ }
31
+ ```
32
+
33
+ ## Hard rules (apply before authoring anything)
34
+
35
+ - **Deterministic atoms only.** Use ONLY pattern names from the
36
+ *Pattern vocabulary* section below. Do NOT propose stochastic
37
+ atoms (`injection_free`, `pii_free`, `semantic_pii`, `faithful`,
38
+ `harmful`, `tone_*`, `relevant`, `metric_integrity`, etc.).
39
+ Sponsio OSS does not load them; the YAML will fail to parse.
40
+
41
+ - **Dedupe against `pre_existing_contracts`.** The CLI already
42
+ emitted starter rules (likely `idempotent`, generic
43
+ `arg_blacklist`, `tool_allowlist`, `loop_detection`). Skip any
44
+ `(pattern, primary_arg)` already present. Add only what's new.
45
+
46
+ - **Pack rules go in `include:`, not inline.** Every entry in
47
+ `auto_selected_packs` belongs in the agent's `include:` block.
48
+ Do NOT inline what the pack already covers — duplicate noise
49
+ the user has to clean up.
50
+
51
+ - **Source tagging.** Every contract YOU author carries
52
+ `source: agent-extracted` so future `sponsio refresh` runs can
53
+ distinguish your additions from pack rules and from
54
+ CLI-emitted starter rules.
55
+
56
+ - **One contract per concrete failure mode.** Plain-English
57
+ `desc:` so the user can review by reading. No omnibus rules.
58
+
59
+ - **Strip `extractor:` / `judge:` blocks.** Those drive Sponsio
60
+ Cloud features (parse-time / runtime LLM judges) and add noise
61
+ to the OSS file unless the user explicitly asks for them.
62
+
63
+ - **Placeholders only as last resort.** Use `___AGENT_ID___`,
64
+ `___WORKSPACE___`, `___ALLOWED_HOST___` etc. ONLY when no
65
+ concrete value can be inferred from `policy_docs`,
66
+ `tool_inventory`, or the threat model. Concrete values from
67
+ the inputs always win — placeholders force the user to fill
68
+ in by hand, so they're a tax on the user, not a default.
69
+
70
+ - **`health == "empty"` means stop.** When the CLI flagged the
71
+ inputs as empty (no framework, no tools, no policy), don't
72
+ fabricate rules. Tell the user the inputs were empty and ask
73
+ what they want — usually it means the wrong path was scanned.
74
+
75
+ ## Read beyond the JSON — that's where you earn your keep
76
+
77
+ The CLI already gave you the deterministic surface (tool inventory,
78
+ auto-selected packs, policy_docs). AST extraction can't see
79
+ intent — that's why the host agent is in this loop instead of an
80
+ LLM-extractor subprocess. Before you propose anything, also read:
81
+
82
+ - **README.md / SECURITY.md / POLICY.md / docs/*.md** for the rules
83
+ the user wrote in prose ("we never delete prod snapshots", "AML
84
+ must complete before loan funding"). These map to
85
+ ``arg_blacklist`` / ``must_precede`` / ``rate_limit`` more
86
+ faithfully than tool-name heuristics ever will.
87
+ - **The agent entry file + the modules its tools call into.**
88
+ KPI strings in the system prompt ("cut storage 20%") tell you
89
+ what the agent will be tempted to do; you can pre-emptively
90
+ block the unsafe shortcut.
91
+ - **Existing tests.** They often encode invariants ("balance
92
+ must never go negative", "report must list every deletion").
93
+ - **``git log -p`` on the agent files.** Recent patches that
94
+ fix bugs labelled "agent did <X> we didn't want" hint at
95
+ rules the user already wishes existed.
96
+
97
+ Cite the source in each contract's ``desc:`` field — ``"never
98
+ delete /snapshots/prod/* (policy.md L18)"`` is a 5-second review;
99
+ ``"safety rule for delete_snapshot"`` is theatre.
100
+
101
+ ## Output — render as a diff for the user to pick
102
+
103
+ Don't auto-write the whole sponsio.yaml in one go. The starter
104
+ yaml on disk (already produced by ``sponsio onboard`` /
105
+ ``sponsio init``) is your baseline. Compute the DIFF — 5 to 10
106
+ new contracts — and render it as YAML next to a one-sentence
107
+ ``rationale:`` per contract:
108
+
109
+ ```yaml
110
+ # Proposed additions — review and tell me which to merge.
111
+ agents:
112
+ <agent_id>:
113
+ contracts:
114
+ - desc: "delete_snapshot must not touch /snapshots/prod/"
115
+ G:
116
+ pattern: arg_blacklist
117
+ args: [delete_snapshot, path, ["^/snapshots/prod/"]]
118
+ source: agent-extracted
119
+ # rationale: policy.md L18 — "Production snapshots are
120
+ # off-site DR backups and MUST NEVER be deleted"
121
+ - desc: "delete_snapshot age_days within 30-day rotation"
122
+ G:
123
+ pattern: arg_value_range
124
+ args: [delete_snapshot, age_days, 0, 30]
125
+ source: agent-extracted
126
+ # rationale: policy.md L24 — age_days <= 30 retention
127
+ ```
128
+
129
+ Then WAIT for the user to pick which proposals to merge into
130
+ ``sponsio.yaml``. Don't auto-write. Onboarding ends when the
131
+ user tells you which ones land.
132
+
133
+ ## What you produce
134
+
135
+ A single YAML document — the new (or revised) `sponsio.yaml`.
136
+ Structure:
137
+
138
+ ```yaml
139
+ version: "1"
140
+ mode: observe
141
+ agents:
142
+ <agent_id>:
143
+ include:
144
+ - <pack from auto_selected_packs>
145
+ - ...
146
+ contracts:
147
+ - desc: "..."
148
+ G:
149
+ pattern: ...
150
+ args: [...]
151
+ A:
152
+ # optional — when conditional
153
+ pattern: ...
154
+ args: [...]
155
+ ```
156
+
157
+ ## Rules of thumb (apply in this order)
158
+
159
+ 1. **Pack first** — every pack in `auto_selected_packs` goes in
160
+ `include:`. Don't try to inline what the pack already covers;
161
+ redundant rules just clutter review.
162
+ 2. **Tool-specific contracts** — for each tool the inventory shows:
163
+ - **Destructive verbs** (`delete_*`, `remove_*`, `transfer_*`,
164
+ `force_*`) → `irreversible_once` if irreversible, otherwise
165
+ tight `rate_limit`.
166
+ - **Outbound side-effects** (`send_email`, `post_*`,
167
+ `create_*_comment`) → `rate_limit` 5–10 + `arg_blacklist` for
168
+ target identifiers if you can constrain them.
169
+ - **Path / URL params** → `arg_blacklist` for sensitive paths
170
+ (`\.env`, `\.aws/credentials`, `\.ssh/`) and internal hosts.
171
+ - **Read tools** → skip unless they touch credentials.
172
+ 3. **Policy doc** — if `policy_docs` is non-empty, lift specific
173
+ rules from them. Each one becomes a contract with
174
+ `source: policy`.
175
+ 4. **Existing YAML** — if `existing_yaml` is non-empty, **merge**:
176
+ keep all user-written contracts intact, add new ones for tools
177
+ that aren't covered. Never silently delete.
178
+
179
+ ## Pattern vocabulary
180
+
181
+ (Same as plugin scan — `arg_blacklist`, `arg_value_range`,
182
+ `arg_length_limit`, `rate_limit`, `loop_detection`,
183
+ `irreversible_once`, `must_precede`. See
184
+ ``sponsio plugin prompt mcp-bare`` for full signatures.)
185
+
186
+ ## Source tagging
187
+
188
+ Every contract YOU author should carry `source: agent-extracted` so
189
+ future `sponsio refresh` runs know they were agent-generated and
190
+ can be re-considered. Don't tag pack-included rules — those have
191
+ their own source from the pack.
192
+
193
+ ## What to do after
194
+
195
+ You produce **two** edits, in this order:
196
+
197
+ ### 1. Write `sponsio.yaml`
198
+
199
+ Write the YAML you authored above to the path in `out_path` (default:
200
+ project root). Use the host's Edit/Write tool. If `existing_yaml`
201
+ was non-empty, merge — don't clobber.
202
+
203
+ ### 2. Patch the agent entry file
204
+
205
+ `wrap_snippet` shows the imports and guard construction. Your job is
206
+ to splice those into the actual entry file so Sponsio is wired into
207
+ the runtime, not just printed in the docs.
208
+
209
+ **Locate the entry file** (use `entry_file_candidates` if present in
210
+ the context — the CLI's deterministic scan flagged files that import
211
+ the framework). If absent, fall back to:
212
+
213
+ - Python: files that instantiate the framework's agent class
214
+ (e.g. `Agent(...)`, `LangGraph(...)`, `ClaudeAgent(...)`),
215
+ pointed at by `pyproject.toml`'s entry points or the project's
216
+ run script.
217
+ - TypeScript: files that call `generateText` / construct the
218
+ Anthropic / OpenAI client / build a LangChain ToolNode, pointed
219
+ at by `package.json`'s `main` / `bin` / `scripts.start`.
220
+
221
+ If multiple candidates remain after that filter, **stop and ask the
222
+ user** rather than guessing — picking wrong creates a duplicate
223
+ guard or a partial wire-up that's worse than nothing.
224
+
225
+ **Apply the wrap** (framework-specific):
226
+
227
+ - **Vercel AI SDK**: change every `model: <provider>("...")` site
228
+ to `model: wrapLanguageModel({ model: <provider>("..."),
229
+ middleware: sponsioMiddleware(guard) })`. Add the imports at the
230
+ top.
231
+ - **LangChain / LangGraph**: replace `tools` arrays passed to
232
+ `ToolNode` / `createReactAgent` with `wrapTools(tools, guard)`.
233
+ - **Claude Agent SDK / OpenAI Agents SDK**: wrap the underlying
234
+ client per the snippet.
235
+ - **Bare function-calling loop** (no framework): insert a guard
236
+ call before the tool executes and another after. The API is
237
+ `guard.guardBefore` / `guard.guardAfter` in TypeScript, or
238
+ `guard.guard_before` / `guard.guard_after` in Python — match
239
+ the host project's language. Show the user the diff before
240
+ applying — this path edits more lines.
241
+
242
+ **Always**: keep the original imports intact, add Sponsio's *above*
243
+ the agent entry's existing imports, and re-run the project's type
244
+ check / lint after edit.
245
+
246
+ ### 3. Hand off
247
+
248
+ Show the user a short summary: which contracts you wrote, which file
249
+ you patched (with line range), and the next command to run
250
+ (`sponsio doctor`, then their normal `npm test` / `pytest`).
@@ -0,0 +1,118 @@
1
+ # Contract refresh prompt — sponsio refresh from traces
2
+
3
+ You are tuning an existing `sponsio.yaml` against accumulated
4
+ session traces. The library already exists; you're proposing
5
+ deltas — added contracts, retired stale ones, tightened thresholds
6
+ — based on what the agent actually did.
7
+
8
+ This is the **self-evolve loop**: each refresh round looks at recent
9
+ near-misses (would-have-blocked in observe mode) and confirmed
10
+ patterns (rules that fire frequently and correctly), and proposes
11
+ targeted edits.
12
+
13
+ ## Input
14
+
15
+ A JSON object from `sponsio refresh --emit-traces`:
16
+
17
+ ```json
18
+ {
19
+ "agent": "...",
20
+ "since": "7d",
21
+ "existing_contracts": [
22
+ {"desc": "...", "pattern": "...", "args": [...], "source": "..."}
23
+ ],
24
+ "trace_summary": {
25
+ "total_events": 1247,
26
+ "would_have_blocked": [
27
+ {
28
+ "tool": "send_email",
29
+ "rule_desc": "rate_limit 5",
30
+ "fire_count": 12,
31
+ "sample_calls": [
32
+ {"args": {...}, "ts": "...", "agent_outcome": "succeeded"}
33
+ ]
34
+ }
35
+ ],
36
+ "blocked_actual": [...],
37
+ "uncovered_patterns": [
38
+ {
39
+ "tool": "transfer_funds",
40
+ "call_count": 3,
41
+ "sample_args": [{...}],
42
+ "note": "tool was called 3x; no contract covers it"
43
+ }
44
+ ]
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## What you produce
50
+
51
+ A YAML diff with three sections:
52
+
53
+ ```yaml
54
+ proposed_changes:
55
+ add:
56
+ - desc: "..."
57
+ G: {pattern: ..., args: [...]}
58
+ source: agent-extracted-from-traces
59
+ retire:
60
+ - match: { desc: "<exact desc of existing contract>" }
61
+ reason: "fired 0 times in 7d window"
62
+ tighten:
63
+ - match: { desc: "<exact desc>" }
64
+ from: {pattern: rate_limit, args: [send_email, 5]}
65
+ to: {pattern: rate_limit, args: [send_email, 10]}
66
+ reason: "false-positive rate 12/30 over last 7d"
67
+ ```
68
+
69
+ ## Rules of thumb
70
+
71
+ ### Adding contracts
72
+
73
+ * `uncovered_patterns` with `call_count` ≥ 3 and clear semantics →
74
+ add a contract.
75
+ * Don't add for tools that fired 1–2x — too sparse.
76
+ * Match the source-tag convention: new agent-derived contracts get
77
+ `source: agent-extracted-from-traces`.
78
+
79
+ ### Retiring (only `source: trace` or `source: agent-extracted-*`)
80
+
81
+ * Existing contract that fired 0 times in the window → candidate for
82
+ retirement, but ONLY if it's `source: trace` or
83
+ `source: agent-extracted-*`.
84
+ * **Never propose retiring** `source: scan`, `source: policy`, or
85
+ user-written (no source / `source: user`) contracts.
86
+ * If retiring, include a one-line `reason`.
87
+
88
+ ### Tightening / loosening
89
+
90
+ * High false-positive rate (`would_have_blocked` clusters with
91
+ `agent_outcome: succeeded` — the call was actually fine) →
92
+ loosen the cap or add `arg_blacklist` carve-outs.
93
+ * High true-positive rate (would-have-blocked + agent_outcome
94
+ shows the agent was caught doing something problematic) →
95
+ tighten.
96
+ * Always cite the trace counts in `reason` so the user can verify.
97
+
98
+ ## Pattern vocabulary
99
+
100
+ Same as plugin scan / onboard — `arg_blacklist`, `rate_limit`,
101
+ `loop_detection`, `irreversible_once`, `must_precede`,
102
+ `arg_value_range`, `arg_length_limit`.
103
+
104
+ ## What you must not do
105
+
106
+ * **Don't** retire user-written or pack-derived contracts.
107
+ * **Don't** propose changes without trace evidence — every proposal
108
+ needs a count + window in `reason`.
109
+ * **Don't** merge proposals into the YAML directly — output the
110
+ diff for the user to review and apply. Apply via
111
+ `sponsio refresh --apply` (which respects the same source-tag
112
+ protections automatically) or by hand-editing for ad-hoc changes.
113
+
114
+ ## Output format
115
+
116
+ ONLY the `proposed_changes:` YAML block above. No prose, no
117
+ markdown wrapping. The host driver will show it to the user
118
+ verbatim.
@@ -0,0 +1,125 @@
1
+ # Contract authoring prompt — sponsio scan
2
+
3
+ You are a security engineer authoring contract entries for a
4
+ `sponsio.yaml`. The CLI has already done the deterministic work
5
+ (AST-based tool inventory, policy doc collection, trace summary).
6
+ Your job is the **semantic gap**: rules a name-heuristic can't see —
7
+ ordering invariants, exfil shapes, irreversible-once destructive
8
+ verbs, rate-limit ceilings, argument blacklists keyed to user intent.
9
+
10
+ ## Input
11
+
12
+ A JSON object from `sponsio scan <paths...> --emit-context`:
13
+
14
+ ```json
15
+ {
16
+ "agent_id": "agent",
17
+ "source_paths": ["src/"],
18
+ "tool_inventory": [
19
+ {"name": "...", "description": "...", "params": {...}, "source_file": "..."}
20
+ ],
21
+ "policy_docs": [
22
+ {"path": "security.md", "content": "..."}
23
+ ],
24
+ "trace_summary": {
25
+ "files": ["..."],
26
+ "total_events": 0
27
+ },
28
+ "existing_yaml": "<current sponsio.yaml content, or empty>",
29
+ "out_path": "sponsio.yaml"
30
+ }
31
+ ```
32
+
33
+ ## What you produce
34
+
35
+ YAML entries to merge into `out_path`'s `agents:<agent_id>:contracts:`
36
+ list. If `existing_yaml` is empty, produce a complete `sponsio.yaml`
37
+ including `version`, `agents`, `tools`, and `contracts`. If non-empty,
38
+ produce **only** the new contract entries — the user (or you, via
39
+ `Edit`) will splice them into the existing file.
40
+
41
+ Each entry is a mapping with `desc:` (one line, human-readable) and an
42
+ `G:` block holding either a `pattern:` reference (preferred) or a raw
43
+ `ltl:`.
44
+
45
+ ```yaml
46
+ - desc: "Block exfil-shape POSTs after env-leak commands"
47
+ A:
48
+ ltl: 'F(arg_field_has(Bash, command, "(printenv|export[ ]+-p)"))'
49
+ G:
50
+ ltl: 'G(!arg_field_has(Bash, command, "curl.*-d.*\\$\\(.*\\)"))'
51
+ source: "scan:bash_envleak_then_exfil"
52
+ ```
53
+
54
+ ## Pattern vocabulary
55
+
56
+ Use ONLY these patterns: `arg_blacklist`, `arg_value_range`,
57
+ `arg_length_limit`, `rate_limit`, `loop_detection`,
58
+ `irreversible_once`, `must_precede`, `must_follow`, `idempotent`,
59
+ `cooldown`. Run ``sponsio plugin prompt mcp-bare`` for the full
60
+ signatures + arg shapes if you need them.
61
+
62
+ ## How to think
63
+
64
+ Be conservative — false positives in observe mode are cheap, but flip
65
+ to enforce and they wedge the agent. For each tool in
66
+ `tool_inventory`:
67
+
68
+ **Destructive verbs** (`delete_*`, `remove_*`, `drop_*`, `transfer_*`,
69
+ `force_*`)
70
+ - Irreversible (e.g. `delete_repository`, `delete_volume`,
71
+ `drop_table`) → `irreversible_once`
72
+ - Recoverable (e.g. `archive_thread`, `disable_user`) → `rate_limit`
73
+ cap of 1–3 per session
74
+
75
+ **Outbound side-effects** (`send_email`, `post_*`, `publish_*`,
76
+ `create_issue_comment`, `notify_*`)
77
+ - `rate_limit` cap of 5–10 per session to prevent runaway loops
78
+ - `arg_blacklist` against target identifiers you can constrain
79
+ (allowed domains / repos / channels)
80
+
81
+ **Path / URL / repo params**
82
+ - `arg_blacklist` against patterns the user's environment shouldn't
83
+ touch — common ones:
84
+ - `\.env(\.|$)`, `\.aws/credentials`, `\.ssh/`, `\.gnupg/`
85
+ - `^/etc/(shadow|sudoers)`
86
+ - internal-network IPs: `^https?://(localhost|127\.|10\.|192\.168\.)`
87
+ - private/internal repo names visible in policy docs
88
+
89
+ **Shell / exec tools** (`Bash`, `Shell`, `exec`, `run_command`, …)
90
+ - Common deny patterns:
91
+ - `rm -rf /` / `rm -rf ~`
92
+ - `curl … | (bash|sh)` (curl-pipe-bash)
93
+ - `chmod 777 /` (broad permissions on root)
94
+ - destructive cloud-control-plane verbs: `curl -X DELETE` against
95
+ `api.<provider>.com`, provider CLIs' `delete` / `destroy` / `down`
96
+ subcommands
97
+
98
+ **Read tools**
99
+ - Skip unless they touch credential paths. Adding rules to every
100
+ read clutters review without much upside.
101
+
102
+ ## Cross-references to weight in
103
+
104
+ - **`policy_docs`**: the user has explicitly written down constraints
105
+ here — take them seriously. A policy doc saying "no destructive
106
+ Railway calls without confirmation" should produce a concrete
107
+ arg_blacklist + irreversible_once pair.
108
+ - **`trace_summary.total_events`** > 0: ordering / sequence rules are
109
+ more reliable now (you can run `sponsio refresh --emit-traces` to
110
+ mine them properly). Without traces, prefer single-event patterns
111
+ (`arg_blacklist`, `rate_limit`, `irreversible_once`) over
112
+ trace-aware ones.
113
+ - **`existing_yaml`**: don't duplicate rules already there. Read its
114
+ contracts, only emit gaps.
115
+
116
+ ## Output discipline
117
+
118
+ - Stay inside the published pattern vocabulary. If a constraint
119
+ doesn't fit one of those patterns, **describe** it as a `desc:`
120
+ comment and skip the `G:` block — better to flag the gap for human
121
+ review than to invent a pattern that won't compile.
122
+ - Each rule cites its source under `source:` using the convention
123
+ `scan:<short-id>` so reviewers can trace the rule back.
124
+ - After writing, run `sponsio validate --config <out_path>` and fix
125
+ any compilation errors before declaring done.