@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
@@ -19,12 +19,42 @@
19
19
  # time, but a sponsio-native rule is the right defense — it travels
20
20
  # with the user's config to any host (Cursor, Codex, custom MCP).
21
21
  #
22
+ # A second escalation vector lives one level up: an agent can
23
+ # ``import sponsio; print(sponsio.__file__)`` to locate the
24
+ # installed package, then edit the upstream capability bundle (or
25
+ # the evaluation engine itself) to delete the rule about to fire.
26
+ # This works on **any** user-writable Python install — editable
27
+ # (``pip install -e .``), ``pip install --user``, conda envs,
28
+ # venvs. Only system-wide ``/usr/lib/python*`` installs are safe
29
+ # by filesystem permission alone, and that's the minority of real
30
+ # deployments. This pack closes that vector.
31
+ #
22
32
  # What this pack denies:
23
33
  # - Edit / Write / MultiEdit on the legacy host guard config:
24
34
  # ``~/.sponsio/plugins/_host/sponsio.yaml``
25
35
  # - Edit / Write / MultiEdit on ANY per-plugin / per-MCP-server
26
36
  # library (``~/.sponsio/plugins/<plugin-id>/sponsio.yaml``
27
37
  # where ``<plugin-id>`` does NOT start with ``_host``).
38
+ # - Edit / Write / MultiEdit on the **upstream sponsio package** —
39
+ # specifically the contract bundles under
40
+ # ``sponsio/contracts/**/*.yaml`` and the evaluation-engine
41
+ # Python files (``guard_stdin``, ``config``, ``core``, plus
42
+ # anything under ``runtime/``, ``patterns/``, ``formulas/``,
43
+ # ``tracer/``, ``integrations/``). Without this, an agent can
44
+ # simply ``import sponsio; print(sponsio.__file__)`` to locate
45
+ # the package, then edit a capability bundle (or monkey-patch
46
+ # the loader) to delete the rule about to fire — bypassing the
47
+ # per-bucket protection above.
48
+ # - Bash write operators (``>``, ``>>``, ``tee``, ``sed -i``,
49
+ # ``cp/mv/rm/dd``) targeting any of the above paths — the
50
+ # "shell shortcut" bypass of the structured Edit/Write block.
51
+ # - Bash interpreter invocations (``python``, ``node``, ``ruby``,
52
+ # ``perl``, ``deno``, ``bun``, etc.) targeting any protected
53
+ # path — the "run a script that does the write" bypass.
54
+ # - Write / Edit content that creates a script which itself
55
+ # mutates a protected path — the "stage a file then invoke it"
56
+ # two-step bypass.
57
+ #
28
58
  # The legitimate way to add or update these bundles is the
29
59
  # ``sponsio plugin install`` / ``sponsio plugin scan --apply``
30
60
  # CLI — those write via Python subprocess, not the agent's
@@ -34,6 +64,17 @@
34
64
  # own Edit/Write tools to weaken or rewrite a bundle that's
35
65
  # about to fire against it.
36
66
  #
67
+ # **Sponsio dev exception**: contributors working on the sponsio
68
+ # package itself routinely edit these files. Override in your
69
+ # project-level config:
70
+ #
71
+ # customized:
72
+ # - match: {source: "library:tier1.self-modify"}
73
+ # disabled: true
74
+ #
75
+ # This loses self-modify protection inside the sponsio repo —
76
+ # acceptable for the maintainer workflow, not for end users.
77
+ #
37
78
  # Per-host buckets (``_host_cursor`` / ``_host_claude_code`` / etc.)
38
79
  # carry their own targeted self-modify rules INSIDE their starter
39
80
  # yaml, scoped to that one bucket only. The split keeps the
@@ -56,6 +97,13 @@
56
97
  # ``sponsio onboard`` writes into the user's repo). That file
57
98
  # IS supposed to evolve — onboard / scan / refresh all rewrite
58
99
  # it. Locking it would break the core happy path.
100
+ # - Edits to non-engine sponsio package files (``cli.py``,
101
+ # ``discovery/``, ``reporting/``, ``scoring/``, ``demos/``,
102
+ # ``generation/``, ``models/``). These don't sit on the hook
103
+ # evaluation hot path — modifying them can't disable a rule
104
+ # against the agent's NEXT call. Sponsio contributors edit
105
+ # them frequently; locking them would make the dev workflow
106
+ # unworkable.
59
107
  # - Direct edits via the user's IDE, terminal, or
60
108
  # ``sponsio plugin install`` / ``plugin scan --apply`` CLI —
61
109
  # those don't go through the ``Edit`` / ``Write`` tool surface
@@ -82,11 +130,11 @@ agents:
82
130
  contracts:
83
131
 
84
132
  # =================================================================
85
- # § 1 — No agent-mediated Edit of own host guard config
133
+ # § 1 — No agent-mediated Edit of any sponsio config or engine
86
134
  # =================================================================
87
135
 
88
- - desc: "Host agent must not edit any sponsio guard config (host bucket or per-plugin bundle) — use `sponsio plugin install` / `plugin scan --apply`, or hand-edit"
89
- E:
136
+ - desc: "Host agent must not edit any sponsio guard config or engine file — use `sponsio plugin install` / `plugin scan --apply`, or hand-edit"
137
+ G:
90
138
  pattern: arg_blacklist
91
139
  args:
92
140
  - Edit
@@ -101,10 +149,17 @@ agents:
101
149
  # still manage downstream plugin policy.
102
150
  - - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
103
151
  - "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
152
+ # Upstream sponsio package — contract bundles (rule data)
153
+ # and evaluation-engine .py files. Anchored on
154
+ # ``(^|/)sponsio/...`` so it matches editable installs,
155
+ # site-packages, --user installs, conda/venv all the same.
156
+ - "(^|/)sponsio/contracts/[^/]+/[^/]+\\.yaml$"
157
+ - "(^|/)sponsio/(guard_stdin|config|core)\\.py$"
158
+ - "(^|/)sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py$"
104
159
  source: "library:tier1.self-modify"
105
160
 
106
161
  # =================================================================
107
- # § 2 — No agent-mediated Write of own host guard config
162
+ # § 2 — No agent-mediated Write of any sponsio config or engine
108
163
  # =================================================================
109
164
  #
110
165
  # ``Write`` is the "create or overwrite whole file" tool —
@@ -112,28 +167,173 @@ agents:
112
167
  # rules removed" workaround a pure ``Edit`` block would leave
113
168
  # open.
114
169
 
115
- - desc: "Host agent must not overwrite any sponsio guard config (host bucket or per-plugin bundle) — use the CLI or hand-edit"
116
- E:
170
+ - desc: "Host agent must not overwrite any sponsio guard config or engine file — use the CLI or hand-edit"
171
+ G:
117
172
  pattern: arg_blacklist
118
173
  args:
119
174
  - Write
120
175
  - file_path
121
176
  - - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
122
177
  - "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
178
+ - "(^|/)sponsio/contracts/[^/]+/[^/]+\\.yaml$"
179
+ - "(^|/)sponsio/(guard_stdin|config|core)\\.py$"
180
+ - "(^|/)sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py$"
123
181
  source: "library:tier1.self-modify"
124
182
 
125
183
  # =================================================================
126
- # § 3 — No agent-mediated MultiEdit of own host guard config
184
+ # § 3 — No agent-mediated MultiEdit of any sponsio config or engine
127
185
  # =================================================================
128
186
 
129
- - desc: "Host agent must not multi-edit any sponsio guard config (host bucket or per-plugin bundle) — use the CLI or hand-edit"
130
- E:
187
+ - desc: "Host agent must not multi-edit any sponsio guard config or engine file — use the CLI or hand-edit"
188
+ G:
131
189
  pattern: arg_blacklist
132
190
  args:
133
191
  - MultiEdit
134
192
  - file_path
135
193
  - - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
136
194
  - "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
195
+ - "(^|/)sponsio/contracts/[^/]+/[^/]+\\.yaml$"
196
+ - "(^|/)sponsio/(guard_stdin|config|core)\\.py$"
197
+ - "(^|/)sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py$"
198
+ source: "library:tier1.self-modify"
199
+
200
+ # =================================================================
201
+ # § 4 — No agent-mediated creation of a *script* that writes to
202
+ # a protected path (the "Python file I/O bypass")
203
+ # =================================================================
204
+ #
205
+ # Observed bypass: agent's ``Write`` to the host bucket gets denied
206
+ # by §1–§3, agent recovers by writing a small ``merge_host_*.py``
207
+ # script in the workspace whose content opens the host bucket
208
+ # path in write mode and mutates it. The script's file_path is a
209
+ # normal repo path so §1–§3 don't fire; the Bash invocation
210
+ # ``python merge.py`` doesn't carry the host bucket path on the
211
+ # command line so §6 below also doesn't fire. The signal IS
212
+ # present: the script's *content* mentions a protected path
213
+ # AND uses an open-for-write idiom.
214
+ #
215
+ # The lookahead form ``(?s)(?=.*PATH).*(WRITE_SHAPE)`` requires
216
+ # both signals in the same content blob — pure docs that mention
217
+ # the path stay editable; only files that mention the path AND
218
+ # carry write-shaped code are denied.
219
+ #
220
+ # The PATH alternation covers all four protected families:
221
+ # - host buckets (~/.sponsio/plugins/_host*/...)
222
+ # - per-plugin libs (~/.sponsio/plugins/<id>/...)
223
+ # - upstream contract bundles (sponsio/contracts/.../*.yaml)
224
+ # - upstream engine .py (sponsio/{guard_stdin,config,core}.py
225
+ # and sponsio/{runtime,patterns,formulas,tracer,
226
+ # integrations}/*.py)
227
+ #
228
+ # Tools covered: Write.content, Edit.new_string,
229
+ # MultiEdit.edits[*].new_string (the ``new_string`` projection
230
+ # that grounding extracts from MultiEdit's edit list).
231
+
232
+ - desc: "Host agent must not write a script that mutates a sponsio guard config or engine file — use `sponsio plugin append`/`install`"
233
+ G:
234
+ pattern: arg_blacklist
235
+ args:
236
+ - Write
237
+ - content
238
+ - - "(?s)(?=.*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py)).*(\\.write_text\\s*\\(|\\.write_bytes\\s*\\(|\\bf\\.write\\s*\\(|\\bopen\\s*\\([^)]*['\\\"][aw][\\+bxt]*['\\\"]\\s*\\)|\\bfs\\.(writeFileSync|appendFileSync|createWriteStream|writeFile)\\s*\\(|\\bFile\\.(open|write)\\s*\\(|\\b(IO|File)\\.write\\b)"
239
+ source: "library:tier1.self-modify"
240
+
241
+ - desc: "Host agent must not edit a script that mutates a sponsio guard config or engine file — use `sponsio plugin append`/`install`"
242
+ G:
243
+ pattern: arg_blacklist
244
+ args:
245
+ - Edit
246
+ - new_string
247
+ - - "(?s)(?=.*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py)).*(\\.write_text\\s*\\(|\\.write_bytes\\s*\\(|\\bf\\.write\\s*\\(|\\bopen\\s*\\([^)]*['\\\"][aw][\\+bxt]*['\\\"]\\s*\\)|\\bfs\\.(writeFileSync|appendFileSync|createWriteStream|writeFile)\\s*\\(|\\bFile\\.(open|write)\\s*\\(|\\b(IO|File)\\.write\\b)"
248
+ source: "library:tier1.self-modify"
249
+
250
+ # =================================================================
251
+ # § 5 — No interpreter invocation that targets a protected path
252
+ # =================================================================
253
+ #
254
+ # Catches the inline form (``python -c "...sponsio/contracts/..."``)
255
+ # and the path-as-argument form (``python merge.py
256
+ # ~/.sponsio/plugins/...sponsio.yaml``). Pairs with §4 — that
257
+ # rule blocks the *creation* of bypass scripts, this rule blocks
258
+ # the *invocation* path that doesn't go through a separate file.
259
+ #
260
+ # The interpreter list is the common scripting set; add new ones
261
+ # via a project-local ``customized:`` extending the regex if your
262
+ # environment uses a less common runtime. The trailing path
263
+ # regex is the same shape as §1–§3 / §6 so the privilege boundary
264
+ # stays consistent across tools.
265
+ #
266
+ # Legitimate sponsio CLIs that read the path
267
+ # (``sponsio validate --config <path>``, ``sponsio plugin
268
+ # show <name>``) are not blocked because the leading verb is
269
+ # ``sponsio``, not an interpreter alias. ``sponsio plugin
270
+ # append --target <plugin-id>`` doesn't pass the path at all —
271
+ # it takes the plugin id and resolves the path internally.
272
+
273
+ - desc: "Host agent must not run an interpreter against a sponsio guard config or engine file — use `sponsio plugin append`/`install`"
274
+ G:
275
+ pattern: arg_blacklist
276
+ args:
277
+ - Bash
278
+ - command
279
+ # ``[^|\n]*`` excludes pipe + newline so we don't match
280
+ # across pipelines, but keeps ``;`` permitted because
281
+ # Python's ``-c "..."`` payloads commonly use it as a
282
+ # statement separator (``python -c "import x; x.do()"``).
283
+ - - "\\b(python|python2|python3|node|deno|bun|ruby|perl|php|lua|tclsh|R|Rscript)\\b[^|\\n]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py)"
284
+ source: "library:tier1.self-modify"
285
+
286
+ # =================================================================
287
+ # § 6 — No Bash write operators against a protected path
288
+ # =================================================================
289
+ #
290
+ # The "shell shortcut" bypass: ``echo … > sponsio/contracts/…``,
291
+ # ``sed -i … sponsio/runtime/monitor.py``, ``cp /tmp/replacement
292
+ # sponsio/patterns/library.py``. These don't go through Edit /
293
+ # Write / MultiEdit so §1–§3 don't see them. Each operator
294
+ # gets one regex, applied against the same alternation of
295
+ # protected paths as §1–§3.
296
+ #
297
+ # Operator list: redirect (``>``, ``>>``), explicit writers
298
+ # (``tee``, ``sed -i``), file movers (``cp``, ``mv``, ``rm``,
299
+ # ``dd``). The ``[^|&;]*`` segment between operator and path
300
+ # keeps each match local to its own command (won't cross
301
+ # pipelines or statement separators).
302
+
303
+ - desc: "Host agent must not redirect Bash output (`>` / `>>`) into a sponsio guard config or engine file"
304
+ G:
305
+ pattern: arg_blacklist
306
+ args:
307
+ - Bash
308
+ - command
309
+ - - ">>?\\s*[^|&;]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py)"
310
+ source: "library:tier1.self-modify"
311
+
312
+ - desc: "Host agent must not `tee` into a sponsio guard config or engine file"
313
+ G:
314
+ pattern: arg_blacklist
315
+ args:
316
+ - Bash
317
+ - command
318
+ - - "\\btee\\b\\s+[^|&;]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py)"
319
+ source: "library:tier1.self-modify"
320
+
321
+ - desc: "Host agent must not `sed -i` a sponsio guard config or engine file"
322
+ G:
323
+ pattern: arg_blacklist
324
+ args:
325
+ - Bash
326
+ - command
327
+ - - "\\bsed\\b\\s+-i\\b[^|&;]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py)"
328
+ source: "library:tier1.self-modify"
329
+
330
+ - desc: "Host agent must not `cp` / `mv` / `rm` / `dd` against a sponsio guard config or engine file"
331
+ G:
332
+ pattern: arg_blacklist
333
+ args:
334
+ - Bash
335
+ - command
336
+ - - "\\b(cp|mv|rm|dd)\\b\\s+[^|&;]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations)/[^/]+\\.py)"
137
337
  source: "library:tier1.self-modify"
138
338
 
139
339
  # -----------------------------------------------------------------------------
@@ -150,3 +350,8 @@ agents:
150
350
  # trying to touch this same file is covered by the broader
151
351
  # ``sponsio:incident/subagent-escape`` pack, which denies the
152
352
  # ENTIRE ``~/.sponsio/`` tree to sub-agents.
353
+ #
354
+ # - Upstream package paths are matched WITHOUT the install prefix —
355
+ # ``(^|/)sponsio/contracts/...`` matches editable installs, site-
356
+ # packages, --user installs, conda/venv all the same. The first
357
+ # ``sponsio/`` directory boundary anchors the match cleanly.
@@ -52,7 +52,7 @@ agents:
52
52
  # =================================================================
53
53
 
54
54
  - desc: "Ban recursive deletes of sensitive roots"
55
- E:
55
+ G:
56
56
  pattern: arg_blacklist
57
57
  args:
58
58
  - exec
@@ -72,7 +72,7 @@ agents:
72
72
  source: "library:tier1.shell"
73
73
 
74
74
  - desc: "Ban fork-bomb and obvious denial-of-service patterns"
75
- E:
75
+ G:
76
76
  pattern: arg_blacklist
77
77
  args:
78
78
  - exec
@@ -88,7 +88,7 @@ agents:
88
88
  source: "library:tier1.shell"
89
89
 
90
90
  - desc: "Ban curl-to-shell and wget-to-shell (ClawHavoc ClickFix vector)"
91
- E:
91
+ G:
92
92
  pattern: arg_blacklist
93
93
  args:
94
94
  - exec
@@ -100,7 +100,7 @@ agents:
100
100
  source: "library:tier1.shell"
101
101
 
102
102
  - desc: "Ban outbound reverse-shell / exfiltration primitives"
103
- E:
103
+ G:
104
104
  pattern: arg_blacklist
105
105
  args:
106
106
  - exec
@@ -119,7 +119,7 @@ agents:
119
119
  # a privilege-sensitive command token. Keeps the CVE coverage
120
120
  # while eliminating the biggest day-1 false-positive source.
121
121
  - desc: "Ban shell line-continuation chained into a privileged command (CVE-2026-28460)"
122
- E:
122
+ G:
123
123
  pattern: arg_blacklist
124
124
  args:
125
125
  - exec
@@ -130,7 +130,7 @@ agents:
130
130
 
131
131
  - desc: "Ban undefined-variable-looking patterns that expand to empty in shell"
132
132
  # rm -rf {undefined}/{also_undefined} → rm -rf /
133
- E:
133
+ G:
134
134
  pattern: arg_blacklist
135
135
  args:
136
136
  - exec
@@ -144,33 +144,26 @@ agents:
144
144
  # =================================================================
145
145
 
146
146
  - desc: "Reject exec commands with suspiciously long inline payloads (injection indicator)"
147
- E:
147
+ G:
148
148
  pattern: arg_length_limit
149
149
  args: [exec, command, 2000]
150
150
  source: "library:tier1.shell"
151
151
 
152
- - desc: "Cap exec calls per session"
153
- E:
154
- pattern: rate_limit
155
- args: [exec, 50]
156
- source: "library:tier1.shell"
157
-
158
- # Threshold tuned for shell specifically. Earlier 5 was inherited
159
- # from narrow-tool defaults (apply_patch / merge_pr) where 5
160
- # consecutive same-tool calls is genuinely suspicious. Shell is a
161
- # swiss-army-knife — a normal coding agent doing a refactor will
162
- # routinely run pytest → git status → git diff → git add → git
163
- # commit → git push back-to-back without ever leaving the shell
164
- # tool, which is 6 consecutive calls of legitimate work. 20 still
165
- # trips on the runaway pattern (a genuinely stuck self-rewrite
166
- # loop hits 20 in a few seconds) without false-positiving real
167
- # development workflows. The session-wide rate_limit(exec, 50)
168
- # below remains the harder backstop.
169
- - desc: "No more than 20 consecutive exec calls (runaway loop cap)"
170
- E:
171
- pattern: loop_detection
172
- args: [exec, 20]
173
- source: "library:tier1.shell"
152
+ # NOTE: Previously shipped two session-wide backstops here:
153
+ # 1. `rate_limit(exec, 50)` (later 200) — removed earlier; the
154
+ # cross-session trace store (24h TTL) made a "per-session"
155
+ # cap behave like a rolling 24h cap and false-positive
156
+ # heavy interactive work.
157
+ # 2. `loop_detection(exec, 20)` — also removed. Even at 20
158
+ # consecutive calls the rule false-positived routine
159
+ # coding-agent loops (build/lint/test/regen cycles can hit
160
+ # 20+ shell calls without any of them being destructive),
161
+ # and contributed nothing the targeted §1 arg_blacklists
162
+ # and §3/§4 confirm-gate didn't already cover. Runaway
163
+ # detection at this layer just generated noise without
164
+ # catching real attacks.
165
+ # When per-session trace isolation lands, we can revisit a
166
+ # tighter per-session loop cap that won't roll across sessions.
174
167
 
175
168
  # =================================================================
176
169
  # § 3 — Privileged commands require confirmation
@@ -182,7 +175,7 @@ agents:
182
175
  # =================================================================
183
176
 
184
177
  - desc: "Privileged exec patterns (sudo, chmod 777, dd if=) require preceding human confirmation"
185
- E:
178
+ G:
186
179
  ltl: 'G(called_with(exec, "(sudo\\s|dd\\s+if=|chmod\\s+777|chown\\s+-R|mount\\s)") -> count(confirm_reconfirmed) >= count_with(exec, "(sudo\\s|dd\\s+if=|chmod\\s+777|chown\\s+-R|mount\\s)"))'
187
180
  source: "library:tier1.shell"
188
181
 
@@ -221,7 +214,7 @@ agents:
221
214
 
222
215
  - desc: "Each exec call needs its own confirm_reconfirmed (no confirmation reuse)"
223
216
  A: "called `confirm_reconfirmed`"
224
- E:
217
+ G:
225
218
  ltl: "G(called(exec) -> count(confirm_reconfirmed) >= count(exec))"
226
219
  source: "library:tier1.shell"
227
220
 
@@ -242,5 +235,5 @@ agents:
242
235
  # - The LTL contracts in §3 and §4 depend on the `ltl:` schema field
243
236
  # being supported by the config parser. Until the LTL parser lands,
244
237
  # these can be temporarily commented out; the arg_blacklist contracts
245
- # in §1 and the rate_limit / loop_detection / arg_length_limit
238
+ # in §1 and the arg_length_limit
246
239
  # contracts in §2 work on current master.
@@ -82,7 +82,7 @@ agents:
82
82
  # whether to commit.
83
83
 
84
84
  - desc: "Sub-agents may not commit, push, force-push, or rewrite git history"
85
- E:
85
+ G:
86
86
  pattern: arg_blacklist
87
87
  args:
88
88
  - exec
@@ -111,7 +111,7 @@ agents:
111
111
  # sub-agents shouldn't decide unilaterally.
112
112
 
113
113
  - desc: "Sub-agents may not install packages globally or system-wide"
114
- E:
114
+ G:
115
115
  pattern: arg_blacklist
116
116
  args:
117
117
  - exec
@@ -136,7 +136,7 @@ agents:
136
136
  # arbitrary host is appropriate.
137
137
 
138
138
  - desc: "Sub-agents may not make outbound HTTP / shell-tunneled network calls"
139
- E:
139
+ G:
140
140
  pattern: arg_blacklist
141
141
  args:
142
142
  - exec
@@ -158,7 +158,7 @@ agents:
158
158
  # agent should do it with user visibility.
159
159
 
160
160
  - desc: "Sub-agents may not write to system / user-home paths"
161
- E:
161
+ G:
162
162
  pattern: arg_blacklist
163
163
  args:
164
164
  - write
@@ -171,7 +171,7 @@ agents:
171
171
  source: "library:tier1.subagent"
172
172
 
173
173
  - desc: "Sub-agents may not edit system / user-home paths"
174
- E:
174
+ G:
175
175
  pattern: arg_blacklist
176
176
  args:
177
177
  - edit
@@ -1,18 +1,28 @@
1
1
  # =============================================================================
2
2
  # sponsio/contracts/core/llm_safety.yaml
3
3
  #
4
- # Stochastic output-safety contracts for LLM responses. Default-OFF —
5
- # users opt in by adding this to their config:
4
+ # !! Sponsio Cloud only !! — this pack is composed entirely of LLM-judged
5
+ # stochastic atoms (`injection_free`, `jailbreak_free`, `harmful`,
6
+ # `toxic_free`, `semantic_pii_free`). The OSS engine ships no evaluator
7
+ # for them and will refuse to load this pack with a clear pointer to
8
+ # `pip install sponsio[cloud]`.
9
+ #
10
+ # It still ships in the OSS package so Cloud installs see the same on-disk
11
+ # layout — Cloud's StoEvaluator (auto-discovered via the
12
+ # `sponsio.evaluators` entry-point group) is what actually evaluates
13
+ # these contracts.
14
+ #
15
+ # Once Cloud is installed, opt in from your config:
6
16
  #
7
17
  # include:
8
18
  # - sponsio:core/llm_safety
9
19
  #
10
- # Why opt-in: every contract here is a stochastic atom, which means the
11
- # runtime calls a judge LLM on each evaluation. That's the right default
12
- # for response-quality work but the wrong default for tool-call-only
13
- # agents (no LLM responses to score → judge calls add latency without
14
- # value). Splitting out of ``core/universal`` was the user-driven fix
15
- # for "why is the SRE tool-call demo running prompt-injection scoring".
20
+ # Why opt-in even on Cloud: every contract here triggers a judge LLM call
21
+ # on each evaluation. That's the right default for response-quality work
22
+ # but the wrong default for tool-call-only agents (no LLM responses to
23
+ # score → judge calls add latency without value). Splitting out of
24
+ # `core/universal` was the user-driven fix for "why is the SRE tool-call
25
+ # demo running prompt-injection scoring".
16
26
  #
17
27
  # Source tag convention for this file:
18
28
  # library:tier0.<category> where category ∈
@@ -44,7 +54,7 @@ agents:
44
54
  # 0.95 — prompt-injection slip-throughs cause downstream tool
45
55
  # misuse with full agent privileges. High slip-through cost.
46
56
  - desc: "Every LLM response must be free of prompt-injection compliance"
47
- E:
57
+ G:
48
58
  pattern: injection_free
49
59
  context_scope: full_trace
50
60
  source: "library:tier0.adversarial"
@@ -53,7 +63,7 @@ agents:
53
63
  # 0.90 — jailbreak compliance is bad but mostly self-contained
54
64
  # (response-level harm, not tool-level harm).
55
65
  - desc: "LLM must not comply with jailbreak attempts (DAN, roleplay, hypothetical bypass)"
56
- E:
66
+ G:
57
67
  pattern: jailbreak_free
58
68
  source: "library:tier0.adversarial"
59
69
  beta: 0.9
@@ -63,7 +73,7 @@ agents:
63
73
  # 0.95 — real-world-harm enablement (CSAM, weapons, self-harm
64
74
  # methods) carries the same high slip-through cost as injection.
65
75
  - desc: "Response must not encourage or enable real-world harm"
66
- E:
76
+ G:
67
77
  pattern: harmful
68
78
  source: "library:tier0.harm"
69
79
  beta: 0.95
@@ -71,7 +81,7 @@ agents:
71
81
  # 0.90 — toxic language is corrosive but not safety-critical;
72
82
  # match the jailbreak weight rather than the harm weight.
73
83
  - desc: "Response free of toxic / harassing language"
74
- E:
84
+ G:
75
85
  pattern: toxic_free
76
86
  source: "library:tier0.harm"
77
87
  beta: 0.9
@@ -81,7 +91,7 @@ agents:
81
91
  # 0.95 — PII slip-throughs are regulatory issues (GDPR / CCPA)
82
92
  # not just trust issues. High slip-through cost.
83
93
  - desc: "Response free of contextual PII (names tied to conditions, inferable identity)"
84
- E:
94
+ G:
85
95
  pattern: semantic_pii_free
86
96
  source: "library:tier0.privacy"
87
97
  beta: 0.95
@@ -66,13 +66,12 @@ agents:
66
66
  # § 1 — Trace-aware: secret-emitting commands followed by exfil-shape
67
67
  # network call.
68
68
  #
69
- # Each rule uses the explicit A/E + ``activate_at: first_match`` form:
69
+ # Each rule uses the explicit A/G + ``activate_at: first_match`` form:
70
70
  # the assumption A names the trigger event (e.g. `docker compose
71
71
  # config`), and the enforcement E forbids the exfil shape from the
72
72
  # *first* position where the trigger occurred. Events before
73
73
  # activation are not subject to E — so a `curl` issued before any
74
- # secret-emitting command is unaffected. See
75
- # ``docs/internal/contract-activate-at.md`` for the semantic.
74
+ # secret-emitting command is unaffected.
76
75
  #
77
76
  # We use `arg_field_has` (per-field regex) rather than `called_with`
78
77
  # (full-args regex) to avoid (a) the `{'command': ...}` dict-wrapping
@@ -98,7 +97,7 @@ agents:
98
97
  activate_at: first_match
99
98
  A:
100
99
  ltl: 'F(arg_field_has(Bash, command, "docker[ -]+compose[ -]+config\b|docker[ -]+config\b|docker[ -]+inspect\b"))'
101
- E:
100
+ G:
102
101
  ltl: 'G(!arg_field_has(Bash, command, "(curl|wget|httpie|http)\b.*(?<!\S)(-d|--data[a-z-]*)(?!\S).*(@-(\s|$)|\$\([^)]*\)|`[^`]*`)"))'
103
102
  source: "incident:claude-code-secret-bypass.docker_then_exfil"
104
103
 
@@ -106,7 +105,7 @@ agents:
106
105
  activate_at: first_match
107
106
  A:
108
107
  ltl: 'F(arg_field_has(Bash, command, "\bop[ ]+run\b|\baws-vault\b|\bdirenv\b|\bdotenvx[ ]+run\b|\benvchain\b"))'
109
- E:
108
+ G:
110
109
  ltl: 'G(!arg_field_has(Bash, command, "(curl|wget|httpie|http)\b.*(?<!\S)(-d|--data[a-z-]*)(?!\S).*(@-(\s|$)|\$\([^)]*\)|`[^`]*`)"))'
111
110
  source: "incident:claude-code-secret-bypass.vault_then_exfil"
112
111
 
@@ -120,7 +119,7 @@ agents:
120
119
  # `export -p` (always lists exports)
121
120
  A:
122
121
  ltl: 'F(arg_field_has(Bash, command, "(?<!\S)(printenv|export[ ]+-p)\b|(?<!\S)(env|set)(\s*$|\s*[|>])"))'
123
- E:
122
+ G:
124
123
  ltl: 'G(!arg_field_has(Bash, command, "(curl|wget|httpie|http)\b.*(?<!\S)(-d|--data[a-z-]*)(?!\S).*(@-(\s|$)|\$\([^)]*\)|`[^`]*`)"))'
125
124
  source: "incident:claude-code-secret-bypass.envdump_then_exfil"
126
125
 
@@ -128,7 +127,7 @@ agents:
128
127
  activate_at: first_match
129
128
  A:
130
129
  ltl: 'F(arg_field_has(Bash, command, "git[ ]+(log|show|reflog)\b.*(-p\b|-G\b|--all\b)|git[ ]+grep\b"))'
131
- E:
130
+ G:
132
131
  ltl: 'G(!arg_field_has(Bash, command, "(curl|wget|httpie|http)\b.*(?<!\S)(-d|--data[a-z-]*)(?!\S).*(@-(\s|$)|\$\([^)]*\)|`[^`]*`)"))'
133
132
  source: "incident:claude-code-secret-bypass.githist_then_exfil"
134
133
 
@@ -138,7 +137,7 @@ agents:
138
137
  # =====================================================================
139
138
 
140
139
  - desc: "Reading /proc/<pid>/environ via Bash is the canonical env-leak path"
141
- E:
140
+ G:
142
141
  pattern: arg_blacklist
143
142
  args:
144
143
  - Bash
@@ -147,7 +146,7 @@ agents:
147
146
  source: "incident:claude-code-secret-bypass.proc_environ_bash"
148
147
 
149
148
  - desc: "Reading /proc/<pid>/environ via the Read tool"
150
- E:
149
+ G:
151
150
  pattern: arg_blacklist
152
151
  args:
153
152
  - Read
@@ -156,7 +155,7 @@ agents:
156
155
  source: "incident:claude-code-secret-bypass.proc_environ_read"
157
156
 
158
157
  - desc: "Bare exfil-shape POST: curl/wget with -d/--data carrying @- / \\$(...) / backtick"
159
- E:
158
+ G:
160
159
  pattern: arg_blacklist
161
160
  args:
162
161
  - Bash
@@ -186,7 +185,7 @@ agents:
186
185
  # - Hosts: the egress regex is host-agnostic. Operators wanting
187
186
  # a positive allowlist (`api.openai.com`, `*.amazonaws.com`)
188
187
  # should add an `customized:` rule that further requires the
189
- # URL argument to be off-allowlist. See `docs/contracts.md`
188
+ # URL argument to be off-allowlist. See `docs/concepts/contracts.md`
190
189
  # § Overrides for the wiring.
191
190
  #
192
191
  # - Mode: this pack is enforce-mode safe by design — every rule
@@ -93,7 +93,7 @@ agents:
93
93
  - desc: >-
94
94
  Block destructive Railway control-plane API calls
95
95
  (PocketOS-style 9-second prod wipe via curl + token).
96
- E:
96
+ G:
97
97
  pattern: arg_blacklist
98
98
  args:
99
99
  - exec
@@ -124,7 +124,7 @@ agents:
124
124
  - desc: >-
125
125
  Block Railway CLI destructive verbs (down / volume delete /
126
126
  environment delete / project delete / service delete).
127
- E:
127
+ G:
128
128
  pattern: arg_blacklist
129
129
  args:
130
130
  - exec
@@ -150,7 +150,7 @@ agents:
150
150
  Block ``curl -X DELETE`` against known cloud / PaaS control
151
151
  planes (Railway / Fly / Render / Supabase / Vercel / Heroku /
152
152
  Cloudflare / DigitalOcean / Linode / Hetzner / AWS / GCP / Azure).
153
- E:
153
+ G:
154
154
  pattern: arg_blacklist
155
155
  args:
156
156
  - exec