@sponsio/sdk 0.1.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/contracts/capability/credentials.yaml +188 -0
  2. package/contracts/capability/database.yaml +141 -0
  3. package/contracts/capability/filesystem-strict.yaml +101 -0
  4. package/contracts/capability/filesystem.yaml +239 -0
  5. package/contracts/capability/self-modify.yaml +152 -0
  6. package/contracts/capability/shell.yaml +246 -0
  7. package/contracts/capability/subagent.yaml +184 -0
  8. package/contracts/core/llm_safety.yaml +130 -0
  9. package/contracts/core/runaway.yaml +43 -0
  10. package/contracts/core/universal.yaml +34 -0
  11. package/contracts/incident/claude-code-secret-bypass.yaml +197 -0
  12. package/contracts/incident/cursor-railway-wipe.yaml +164 -0
  13. package/contracts/incident/mcp-composition.yaml +418 -0
  14. package/contracts/incident/openclaw.yaml +500 -0
  15. package/contracts/incident/subagent-escape.yaml +177 -0
  16. package/contracts/premium/.gitkeep +7 -0
  17. package/dist/contract.d.ts +59 -0
  18. package/dist/contract.d.ts.map +1 -0
  19. package/dist/contract.js +97 -0
  20. package/dist/contract.js.map +1 -0
  21. package/dist/core/config-loader.d.ts +90 -0
  22. package/dist/core/config-loader.d.ts.map +1 -0
  23. package/dist/core/config-loader.js +530 -0
  24. package/dist/core/config-loader.js.map +1 -0
  25. package/dist/core/evaluator.d.ts +15 -0
  26. package/dist/core/evaluator.d.ts.map +1 -0
  27. package/dist/core/evaluator.js +87 -0
  28. package/dist/core/evaluator.js.map +1 -0
  29. package/dist/core/formula.d.ts +103 -0
  30. package/dist/core/formula.d.ts.map +1 -0
  31. package/dist/core/formula.js +167 -0
  32. package/dist/core/formula.js.map +1 -0
  33. package/dist/core/grounding.d.ts +43 -0
  34. package/dist/core/grounding.d.ts.map +1 -0
  35. package/dist/core/grounding.js +269 -0
  36. package/dist/core/grounding.js.map +1 -0
  37. package/dist/core/nl-parser.d.ts +12 -0
  38. package/dist/core/nl-parser.d.ts.map +1 -0
  39. package/dist/core/nl-parser.js +152 -0
  40. package/dist/core/nl-parser.js.map +1 -0
  41. package/dist/core/pack-loader.d.ts +15 -0
  42. package/dist/core/pack-loader.d.ts.map +1 -0
  43. package/dist/core/pack-loader.js +220 -0
  44. package/dist/core/pack-loader.js.map +1 -0
  45. package/dist/core/parser.d.ts +19 -0
  46. package/dist/core/parser.d.ts.map +1 -0
  47. package/dist/core/parser.js +235 -0
  48. package/dist/core/parser.js.map +1 -0
  49. package/dist/core/pattern-factory.d.ts +38 -0
  50. package/dist/core/pattern-factory.d.ts.map +1 -0
  51. package/dist/core/pattern-factory.js +222 -0
  52. package/dist/core/pattern-factory.js.map +1 -0
  53. package/dist/core/patterns.d.ts +67 -0
  54. package/dist/core/patterns.d.ts.map +1 -0
  55. package/dist/core/patterns.js +585 -0
  56. package/dist/core/patterns.js.map +1 -0
  57. package/dist/core/session-log.d.ts +91 -0
  58. package/dist/core/session-log.d.ts.map +1 -0
  59. package/dist/core/session-log.js +196 -0
  60. package/dist/core/session-log.js.map +1 -0
  61. package/dist/core/spans.d.ts +92 -0
  62. package/dist/core/spans.d.ts.map +1 -0
  63. package/dist/core/spans.js +148 -0
  64. package/dist/core/spans.js.map +1 -0
  65. package/dist/core/sto.d.ts +120 -0
  66. package/dist/core/sto.d.ts.map +1 -0
  67. package/dist/core/sto.js +147 -0
  68. package/dist/core/sto.js.map +1 -0
  69. package/dist/index.d.ts +267 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +540 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/integrations/claude-agent.d.ts +29 -0
  74. package/dist/integrations/claude-agent.d.ts.map +1 -0
  75. package/dist/integrations/claude-agent.js +40 -0
  76. package/dist/integrations/claude-agent.js.map +1 -0
  77. package/dist/integrations/google-adk.d.ts +27 -0
  78. package/dist/integrations/google-adk.d.ts.map +1 -0
  79. package/dist/integrations/google-adk.js +81 -0
  80. package/dist/integrations/google-adk.js.map +1 -0
  81. package/dist/integrations/langchain.d.ts +26 -0
  82. package/dist/integrations/langchain.d.ts.map +1 -0
  83. package/dist/integrations/langchain.js +74 -0
  84. package/dist/integrations/langchain.js.map +1 -0
  85. package/dist/integrations/openai-agents.d.ts +45 -0
  86. package/dist/integrations/openai-agents.d.ts.map +1 -0
  87. package/dist/integrations/openai-agents.js +81 -0
  88. package/dist/integrations/openai-agents.js.map +1 -0
  89. package/dist/integrations/openai.d.ts +24 -0
  90. package/dist/integrations/openai.d.ts.map +1 -0
  91. package/dist/integrations/openai.js +77 -0
  92. package/dist/integrations/openai.js.map +1 -0
  93. package/dist/integrations/vercel-ai.d.ts +38 -0
  94. package/dist/integrations/vercel-ai.d.ts.map +1 -0
  95. package/dist/integrations/vercel-ai.js +107 -0
  96. package/dist/integrations/vercel-ai.js.map +1 -0
  97. package/dist/render/components.d.ts +23 -0
  98. package/dist/render/components.d.ts.map +1 -0
  99. package/dist/render/components.js +133 -0
  100. package/dist/render/components.js.map +1 -0
  101. package/dist/render/derive.d.ts +23 -0
  102. package/dist/render/derive.d.ts.map +1 -0
  103. package/dist/render/derive.js +92 -0
  104. package/dist/render/derive.js.map +1 -0
  105. package/dist/render/session-view.d.ts +19 -0
  106. package/dist/render/session-view.d.ts.map +1 -0
  107. package/dist/render/session-view.js +236 -0
  108. package/dist/render/session-view.js.map +1 -0
  109. package/dist/render/tokens.d.ts +44 -0
  110. package/dist/render/tokens.d.ts.map +1 -0
  111. package/dist/render/tokens.js +72 -0
  112. package/dist/render/tokens.js.map +1 -0
  113. package/package.json +83 -0
@@ -0,0 +1,152 @@
1
+ # =============================================================================
2
+ # sponsio/contracts/capability/self-modify.yaml
3
+ #
4
+ # Capability pack — self-modification protection for the host agent.
5
+ # Include in your host library (default ``_host``) so the agent
6
+ # under guard cannot weaken or disable the very contracts about to
7
+ # block its next call.
8
+ #
9
+ # Threat model:
10
+ #
11
+ # An agent that can edit its own ``~/.sponsio/plugins/_host/sponsio.yaml``
12
+ # can append an ``customized:`` block to disable the rule about to
13
+ # fire against it. This is privilege escalation by self-rewriting:
14
+ # the safety mechanism becomes the agent's own debuggable resource.
15
+ # Observed in the wild during the development of this very pack —
16
+ # the host agent (Claude Code in auto mode) attempted to add an
17
+ # ``overrides`` block to disable a Bash rate_limit that had just
18
+ # blocked it. Caught by Claude Code's harness-level meta-deny that
19
+ # time, but a sponsio-native rule is the right defense — it travels
20
+ # with the user's config to any host (Cursor, Codex, custom MCP).
21
+ #
22
+ # What this pack denies:
23
+ # - Edit / Write / MultiEdit on the legacy host guard config:
24
+ # ``~/.sponsio/plugins/_host/sponsio.yaml``
25
+ # - Edit / Write / MultiEdit on ANY per-plugin / per-MCP-server
26
+ # library (``~/.sponsio/plugins/<plugin-id>/sponsio.yaml``
27
+ # where ``<plugin-id>`` does NOT start with ``_host``).
28
+ # The legitimate way to add or update these bundles is the
29
+ # ``sponsio plugin install`` / ``sponsio plugin scan --apply``
30
+ # CLI — those write via Python subprocess, not the agent's
31
+ # Edit/Write tool surface, so they aren't blocked. The user
32
+ # hand-editing the file in their text editor is also fine.
33
+ # What's denied is the IDE coding agent reaching in with its
34
+ # own Edit/Write tools to weaken or rewrite a bundle that's
35
+ # about to fire against it.
36
+ #
37
+ # Per-host buckets (``_host_cursor`` / ``_host_claude_code`` / etc.)
38
+ # carry their own targeted self-modify rules INSIDE their starter
39
+ # yaml, scoped to that one bucket only. The split keeps the
40
+ # legitimate "parent manages child policy" workflow alive — Cursor's
41
+ # main agent can still edit ``_host_cursor_subagent/sponsio.yaml``
42
+ # (tightening child rules from the parent context), and an operator
43
+ # managing both Cursor and Claude Code from one IDE can edit the
44
+ # OTHER host's bucket without being blocked. Each agent only loses
45
+ # write access to ITS OWN bucket — that's the privilege boundary
46
+ # this pack family enforces. The ``[^_]...`` regex below excludes
47
+ # every ``_host*`` bucket from the per-plugin rule precisely so this
48
+ # parent-child workflow stays open.
49
+ #
50
+ # What this pack does NOT deny:
51
+ # - Edits to per-host SUBAGENT buckets (``_host_*_subagent``).
52
+ # Parent tightening child policy is a sanctioned workflow.
53
+ # - Edits to a SIBLING host bucket (an operator on Claude Code
54
+ # editing ``_host_cursor/`` or vice versa).
55
+ # - Edits to the project's own ``sponsio.yaml`` (the one
56
+ # ``sponsio onboard`` writes into the user's repo). That file
57
+ # IS supposed to evolve — onboard / scan / refresh all rewrite
58
+ # it. Locking it would break the core happy path.
59
+ # - Direct edits via the user's IDE, terminal, or
60
+ # ``sponsio plugin install`` / ``plugin scan --apply`` CLI —
61
+ # those don't go through the ``Edit`` / ``Write`` tool surface
62
+ # that this pack inspects. The legitimate path for "I really
63
+ # do want to change this" stays open; only the agent-mediated
64
+ # path is closed.
65
+ #
66
+ # Pair with: ``sponsio:incident/subagent-escape`` for the matching
67
+ # subagent-side rule (sub-agents can't reach UP into the parent's
68
+ # config tree at all).
69
+ #
70
+ # Include via:
71
+ # include:
72
+ # - sponsio:capability/self-modify
73
+ #
74
+ # Source tag convention:
75
+ # library:tier1.self-modify
76
+ # =============================================================================
77
+
78
+ version: "1"
79
+
80
+ agents:
81
+ "*":
82
+ contracts:
83
+
84
+ # =================================================================
85
+ # § 1 — No agent-mediated Edit of own host guard config
86
+ # =================================================================
87
+
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:
90
+ pattern: arg_blacklist
91
+ args:
92
+ - Edit
93
+ - file_path
94
+ # Match every per-host bucket: ``_host`` (legacy),
95
+ # ``_host_cursor`` / ``_host_cursor_subagent``,
96
+ # ``_host_claude_code`` / ``_host_claude_code_subagent``,
97
+ # ``_host_openclaw``, plus any future bucket following the
98
+ # same naming scheme. Per-MCP-server libraries
99
+ # (``github``, ``filesystem``, ``<plugin-id>``) are not
100
+ # ``_host*``-prefixed and stay editable so the host can
101
+ # still manage downstream plugin policy.
102
+ - - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
103
+ - "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
104
+ source: "library:tier1.self-modify"
105
+
106
+ # =================================================================
107
+ # § 2 — No agent-mediated Write of own host guard config
108
+ # =================================================================
109
+ #
110
+ # ``Write`` is the "create or overwrite whole file" tool —
111
+ # blocking it shuts the "delete the file then re-create with
112
+ # rules removed" workaround a pure ``Edit`` block would leave
113
+ # open.
114
+
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:
117
+ pattern: arg_blacklist
118
+ args:
119
+ - Write
120
+ - file_path
121
+ - - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
122
+ - "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
123
+ source: "library:tier1.self-modify"
124
+
125
+ # =================================================================
126
+ # § 3 — No agent-mediated MultiEdit of own host guard config
127
+ # =================================================================
128
+
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:
131
+ pattern: arg_blacklist
132
+ args:
133
+ - MultiEdit
134
+ - file_path
135
+ - - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
136
+ - "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
137
+ source: "library:tier1.self-modify"
138
+
139
+ # -----------------------------------------------------------------------------
140
+ # Integration requirements
141
+ # -----------------------------------------------------------------------------
142
+ #
143
+ # - Path matching is regex on the file_path arg. Hosts whose Edit /
144
+ # Write / MultiEdit tool uses a different param name will need a
145
+ # per-host adjustment (Claude Code uses ``file_path`` — handled).
146
+ #
147
+ # - The ``"*"`` agent scope means these rules apply to every agent
148
+ # in any library that includes this pack. In practice the only
149
+ # include site is ``_host`` (the main session) — a sub-agent
150
+ # trying to touch this same file is covered by the broader
151
+ # ``sponsio:incident/subagent-escape`` pack, which denies the
152
+ # ENTIRE ``~/.sponsio/`` tree to sub-agents.
@@ -0,0 +1,246 @@
1
+ # =============================================================================
2
+ # sponsio/contracts/capability/shell.yaml
3
+ #
4
+ # Capability pack — shell execution. Include when your agent exposes a
5
+ # tool that runs arbitrary shell commands (`exec`, `bash`, `run_command`,
6
+ # `execute`, Claude Code's Bash tool, etc.). Rename the tool in your
7
+ # config if it's not called `exec`.
8
+ #
9
+ # Real-world incidents this pack defends against:
10
+ #
11
+ # - Claude Code Issue #10077 (Oct 2025): agent ran recursive delete,
12
+ # wiped user home directory. The Reddit r/ClaudeAI post garnered
13
+ # 1,500+ upvotes.
14
+ # - Claude Code Issue #49464: agent tried `rm -f ~/` when a file named
15
+ # literally "~" was present. Requested fix: "pattern detection for
16
+ # dangerous commands."
17
+ # - Replit agent (Jul 2025): deleted live production DB during a code
18
+ # freeze, affecting 1,200+ companies. Agent admitted "catastrophic
19
+ # error in judgment."
20
+ # - Ansible + undefined-variable expansion: `rm -rf {foo}/{bar}` → `rm
21
+ # -rf /` on 1,535 production servers (Server Fault / Marco Marsala).
22
+ # - OpenClaw ClawHavoc: ClickFix prereqs running curl|bash in users'
23
+ # terminals via malicious SKILL.md.
24
+ # - OpenClaw CVE-2026-28460: approval allowlist bypassed with shell
25
+ # line-continuation (`\\\n`).
26
+ #
27
+ # Every contract here targets a single tool name (`exec` by default).
28
+ # If your agent calls it something else, do a find-and-replace before
29
+ # including this file.
30
+ #
31
+ # Include via:
32
+ # include:
33
+ # - sponsio:capability/shell
34
+ #
35
+ # Source tag convention:
36
+ # library:tier1.shell
37
+ # =============================================================================
38
+
39
+ version: "1"
40
+
41
+ tools:
42
+ - name: exec
43
+ description: "Shell command execution (placeholder — replace with your tool's real name)"
44
+ params: "command: str"
45
+
46
+ agents:
47
+ "*":
48
+ contracts:
49
+
50
+ # =================================================================
51
+ # § 1 — Destructive command blacklist
52
+ # =================================================================
53
+
54
+ - desc: "Ban recursive deletes of sensitive roots"
55
+ E:
56
+ pattern: arg_blacklist
57
+ args:
58
+ - exec
59
+ - command
60
+ # Targets:
61
+ # rm -rf / (Marsala / Ansible expansion)
62
+ # rm -rf ~ (Claude Code #49464 tilde bug)
63
+ # rm -rf $HOME (env expansion variant)
64
+ # rm -rf /* (glob to root)
65
+ # rm -rf /home/... (direct home path)
66
+ # rm -rf /root/...
67
+ # rm -rf /Users/... (macOS home)
68
+ - - "rm\\s+-[rRf]+\\s+(/|\\$HOME|~)(\\s|;|&|\\||$)"
69
+ - "rm\\s+-[rRf]+\\s+/\\*"
70
+ - "rm\\s+-[rRf]+\\s+/(home|root|Users)"
71
+ - "rm\\s+-[rRf]+\\s+\\$\\{?HOME\\}?"
72
+ source: "library:tier1.shell"
73
+
74
+ - desc: "Ban fork-bomb and obvious denial-of-service patterns"
75
+ E:
76
+ pattern: arg_blacklist
77
+ args:
78
+ - exec
79
+ - command
80
+ # Fork bomb: :(){ :|:& };:
81
+ # dd if=/dev/zero (disk fill)
82
+ # mkfs (filesystem format)
83
+ # shred -vfz (irrecoverable overwrite)
84
+ - - ":\\s*\\(\\)\\s*\\{\\s*:\\s*\\|\\s*:\\s*&\\s*\\}\\s*;\\s*:"
85
+ - "dd\\s+if=/dev/zero"
86
+ - "\\bmkfs\\."
87
+ - "shred\\s+-[vfz]+"
88
+ source: "library:tier1.shell"
89
+
90
+ - desc: "Ban curl-to-shell and wget-to-shell (ClawHavoc ClickFix vector)"
91
+ E:
92
+ pattern: arg_blacklist
93
+ args:
94
+ - exec
95
+ - command
96
+ - - "curl[^|]+\\|\\s*(ba)?sh"
97
+ - "wget[^|]+\\|\\s*(ba)?sh"
98
+ - "curl[^|]+\\|\\s*python"
99
+ - "wget\\s+-O-[^|]*\\|"
100
+ source: "library:tier1.shell"
101
+
102
+ - desc: "Ban outbound reverse-shell / exfiltration primitives"
103
+ E:
104
+ pattern: arg_blacklist
105
+ args:
106
+ - exec
107
+ - command
108
+ - - "nc\\s+-e"
109
+ - "bash\\s+-i\\s*>&\\s*/dev/tcp"
110
+ - "python.*socket\\.socket"
111
+ - "curl[^|]+--upload-file"
112
+ source: "library:tier1.shell"
113
+
114
+ # Line-continuation `\<newline>` is a common evasion for approval
115
+ # allowlists: the approver sees `ls` but the shell executes
116
+ # `ls\<newline>; rm -rf /`. Rather than banning EVERY backslash-
117
+ # newline (multi-line Makefiles, Docker RUN blocks, shell scripts
118
+ # all use it legitimately), we only flag continuation followed by
119
+ # a privilege-sensitive command token. Keeps the CVE coverage
120
+ # while eliminating the biggest day-1 false-positive source.
121
+ - desc: "Ban shell line-continuation chained into a privileged command (CVE-2026-28460)"
122
+ E:
123
+ pattern: arg_blacklist
124
+ args:
125
+ - exec
126
+ - command
127
+ - - "\\\\\\n\\s*(sudo|su\\s|rm\\s+-[rRf]|dd\\s+if=|chmod\\s|chown\\s|mount\\s|curl\\s|wget\\s)"
128
+ - "\\\\\\r?\\n\\s*(sudo|su\\s|rm\\s+-[rRf]|dd\\s+if=|chmod\\s|chown\\s|mount\\s|curl\\s|wget\\s)"
129
+ source: "library:tier1.shell"
130
+
131
+ - desc: "Ban undefined-variable-looking patterns that expand to empty in shell"
132
+ # rm -rf {undefined}/{also_undefined} → rm -rf /
133
+ E:
134
+ pattern: arg_blacklist
135
+ args:
136
+ - exec
137
+ - command
138
+ - - "rm\\s+-[rRf]+\\s+\\{[^}]*\\}/?"
139
+ - "rm\\s+-[rRf]+\\s+\\$\\{[^}]*\\}/?"
140
+ source: "library:tier1.shell"
141
+
142
+ # =================================================================
143
+ # § 2 — Blast-radius limits
144
+ # =================================================================
145
+
146
+ - desc: "Reject exec commands with suspiciously long inline payloads (injection indicator)"
147
+ E:
148
+ pattern: arg_length_limit
149
+ args: [exec, command, 2000]
150
+ source: "library:tier1.shell"
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"
174
+
175
+ # =================================================================
176
+ # § 3 — Privileged commands require confirmation
177
+ #
178
+ # The pattern library's must_confirm gates an entire tool; we want
179
+ # the gate to fire only when the *argument* matches a dangerous
180
+ # pattern. Count-dominance over a confirm_reconfirmed marker is
181
+ # the cleanest LTL form and doesn't require a new pattern.
182
+ # =================================================================
183
+
184
+ - desc: "Privileged exec patterns (sudo, chmod 777, dd if=) require preceding human confirmation"
185
+ E:
186
+ 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
+ source: "library:tier1.shell"
188
+
189
+ # =================================================================
190
+ # § 4 — Approval integrity (CVE-2026-29607 wrapper bypass, general form)
191
+ #
192
+ # A single confirm_reconfirmed cannot authorize two exec actions.
193
+ # count-dominance is a proxy for principal identity until a
194
+ # principal() atom ships.
195
+ #
196
+ # NOTE on batch approvals: this rule enforces a strict 1:1
197
+ # confirmation-to-exec ratio. Some workflows legitimately
198
+ # batch-approve (CI pipelines, planned migrations). If your
199
+ # workflow does, override this rule in your config:
200
+ #
201
+ # customized:
202
+ # - match: {desc: "Each exec call needs its own confirm_reconfirmed (no confirmation reuse)"}
203
+ # disabled: true
204
+ # contracts:
205
+ # - desc: "Batch confirm — one confirm authorizes the next 5 execs"
206
+ # E:
207
+ # ltl: "G(called(exec) -> count(confirm_batch_5) > 0)"
208
+ #
209
+ # The batch marker (e.g. `confirm_batch_5`) must be emitted by
210
+ # your integration when the human signs off on a batch.
211
+ #
212
+ # ASSUMPTION gate: this rule only activates once the agent's
213
+ # integration has ever emitted `confirm_reconfirmed` in the
214
+ # current trace. Agents that haven't wired the marker tool in
215
+ # at all (the common day-1 state) see this rule as vacuously
216
+ # true — otherwise every exec call would violate and drown the
217
+ # observe-mode report in noise. When an integration adopts
218
+ # confirm_reconfirmed, the first emission flips the assumption
219
+ # true for that trace and the 1:1 guarantee begins applying.
220
+ # =================================================================
221
+
222
+ - desc: "Each exec call needs its own confirm_reconfirmed (no confirmation reuse)"
223
+ A: "called `confirm_reconfirmed`"
224
+ E:
225
+ ltl: "G(called(exec) -> count(confirm_reconfirmed) >= count(exec))"
226
+ source: "library:tier1.shell"
227
+
228
+ # -----------------------------------------------------------------------------
229
+ # Integration requirements
230
+ # -----------------------------------------------------------------------------
231
+ #
232
+ # - `exec` is a placeholder tool name. If your agent calls its shell
233
+ # tool something else (Bash, run_command, execute, etc.), rewrite the
234
+ # tool name at include time.
235
+ #
236
+ # - `confirm_reconfirmed` is a marker tool — your integration must emit
237
+ # a `tool_call` event with name="confirm_reconfirmed" whenever a human
238
+ # actually approves an action. Without that emission, any contract
239
+ # using confirm_reconfirmed will fail closed (nothing is ever
240
+ # confirmed → everything requiring confirmation is blocked).
241
+ #
242
+ # - The LTL contracts in §3 and §4 depend on the `ltl:` schema field
243
+ # being supported by the config parser. Until the LTL parser lands,
244
+ # these can be temporarily commented out; the arg_blacklist contracts
245
+ # in §1 and the rate_limit / loop_detection / arg_length_limit
246
+ # contracts in §2 work on current master.
@@ -0,0 +1,184 @@
1
+ # =============================================================================
2
+ # sponsio/contracts/capability/subagent.yaml
3
+ #
4
+ # Capability pack — privilege boundary for Task-spawned sub-agents.
5
+ # Include in your sub-agent host library (default ``_host_subagent``)
6
+ # in addition to the regular shell / database / credentials packs.
7
+ #
8
+ # Rationale:
9
+ #
10
+ # A Claude Code Task-spawned sub-agent receives a *task description*
11
+ # from the main agent, not the user's original prompt. It has no
12
+ # access to the conversation that authorised the task and cannot
13
+ # surface a confirmation prompt back to the user. This means
14
+ # alignment-style "wait, is this what the user actually wanted?"
15
+ # refusals don't fire — sub-agents execute their task with high
16
+ # compliance, even when the task description was over-broad
17
+ # ("set up the dev environment" → may interpret as drop-and-recreate
18
+ # the database).
19
+ #
20
+ # Sponsio's response: the SAME tool call that's allowed for the main
21
+ # agent (where the user can review every action in the conversation
22
+ # transcript) gets denied for sub-agents (where the user has no
23
+ # visibility). This forces side-effect work to flow through the
24
+ # main conversation, where the user can intervene.
25
+ #
26
+ # Threat model boundary:
27
+ #
28
+ # - This pack relies on Claude Code's PreToolUse payload field
29
+ # ``agent_id`` (present iff the call originates from a sub-agent).
30
+ # Sponsio's ``derive_plugin_id`` reads this and routes to
31
+ # ``_host_subagent``; rules in this pack therefore only ever
32
+ # evaluate against sub-agent calls.
33
+ # - It does NOT block sub-agents from doing useful work
34
+ # (read, search, summarize). It blocks side-effects whose
35
+ # authorization should travel back through the main conversation:
36
+ # git mutations, filesystem writes outside scratch space, network
37
+ # calls, destructive shell, package installs.
38
+ #
39
+ # Real-world incidents this pack defends against:
40
+ #
41
+ # - Recurring r/ClaudeAI: sub-agent runs ``npm install -g <pkg>``
42
+ # to fix a broken setup, mutating global state without user
43
+ # approval.
44
+ # - "Refactor agent" pattern: parent dispatches Explore to fix a
45
+ # broken test; Explore commits its fix without showing diff to
46
+ # user. Force-push later overwrites work.
47
+ # - Database setup overreach: parent says "init the db", Explore
48
+ # runs ``psql DROP DATABASE && CREATE DATABASE``, wiping prod.
49
+ #
50
+ # Include via:
51
+ # include:
52
+ # - sponsio:capability/subagent
53
+ #
54
+ # Tool naming:
55
+ # The pack uses canonical ``exec`` for the shell tool. Claude
56
+ # Code's ``_host_subagent.yaml`` adds ``tool_rename: exec: Bash``
57
+ # so the rules apply to Claude Code's first-party Bash.
58
+ #
59
+ # Source tag convention:
60
+ # library:tier1.subagent
61
+ # =============================================================================
62
+
63
+ version: "1"
64
+
65
+ tools:
66
+ - name: exec
67
+ description: "Shell command execution (placeholder — replace with your tool's real name)"
68
+ params: "command: str"
69
+
70
+ agents:
71
+ "*":
72
+ contracts:
73
+
74
+ # =================================================================
75
+ # § 1 — Sub-agents may not commit, push, or rewrite git history
76
+ # =================================================================
77
+ #
78
+ # Forces the work product to flow back through the main
79
+ # conversation where the user sees every diff before it lands
80
+ # in version control. Sub-agent edits remain in the working
81
+ # tree; main agent (with user-visible reasoning) decides
82
+ # whether to commit.
83
+
84
+ - desc: "Sub-agents may not commit, push, force-push, or rewrite git history"
85
+ E:
86
+ pattern: arg_blacklist
87
+ args:
88
+ - exec
89
+ - command
90
+ # ``git\b[^|;&]*?\b<verb>\b`` matches the verb anywhere
91
+ # in the same shell command — defeats the
92
+ # ``git -c core.hooksPath=/dev/null commit`` bypass an
93
+ # earlier brittle ``git\s+commit`` regex missed. The
94
+ # ``[^|;&]*?`` keeps us inside the same command (won't
95
+ # cross ``|``, ``;``, ``&`` so ``git status; cat
96
+ # commit.log`` still passes).
97
+ - - "(?:^|[\\s;&|])git\\b[^|;&]*?\\b(commit|push|rebase|reflog|filter-branch|filter-repo)\\b"
98
+ - "(?:^|[\\s;&|])git\\b[^|;&]*?\\breset\\b[^|;&]*?\\b--hard\\b"
99
+ - "(?:^|[\\s;&|])git\\b[^|;&]*?\\b--force\\b"
100
+ - "(?:^|[\\s;&|])git\\b[^|;&]*?\\b-f\\b"
101
+ - "(?:^|[\\s;&|])git\\b[^|;&]*?\\brm\\b"
102
+ source: "library:tier1.subagent"
103
+
104
+ # =================================================================
105
+ # § 2 — Sub-agents may not install packages globally
106
+ # =================================================================
107
+ #
108
+ # Global package installs leak side-effects beyond the project
109
+ # working tree — they affect the user's whole machine. Main
110
+ # agent can request these explicitly with user visibility;
111
+ # sub-agents shouldn't decide unilaterally.
112
+
113
+ - desc: "Sub-agents may not install packages globally or system-wide"
114
+ E:
115
+ pattern: arg_blacklist
116
+ args:
117
+ - exec
118
+ - command
119
+ - - "(?:^|[\\s;&|])npm\\s+install\\s+(?:-g|--global)\\b"
120
+ - "(?:^|[\\s;&|])yarn\\s+global\\s+add\\b"
121
+ - "(?:^|[\\s;&|])pnpm\\s+add\\s+(?:-g|--global)\\b"
122
+ - "(?:^|[\\s;&|])pip\\s+install\\s+(?:-U\\s+)?[^-]"
123
+ - "(?:^|[\\s;&|])pipx\\s+install\\b"
124
+ - "(?:^|[\\s;&|])brew\\s+install\\b"
125
+ - "(?:^|[\\s;&|])apt(?:-get)?\\s+install\\b"
126
+ - "(?:^|[\\s;&|])sudo\\b"
127
+ source: "library:tier1.subagent"
128
+
129
+ # =================================================================
130
+ # § 3 — Sub-agents may not make outbound network calls
131
+ # =================================================================
132
+ #
133
+ # Curl / wget / ssh-out from a sub-agent context is a common
134
+ # exfiltration / supply-chain vector — the sub-agent doesn't
135
+ # have user-conversation context to know whether contacting an
136
+ # arbitrary host is appropriate.
137
+
138
+ - desc: "Sub-agents may not make outbound HTTP / shell-tunneled network calls"
139
+ E:
140
+ pattern: arg_blacklist
141
+ args:
142
+ - exec
143
+ - command
144
+ - - "(?:^|[\\s;&|])curl\\b"
145
+ - "(?:^|[\\s;&|])wget\\b"
146
+ - "(?:^|[\\s;&|])nc\\s+-"
147
+ - "(?:^|[\\s;&|])ssh\\s+\\S+@"
148
+ - "(?:^|[\\s;&|])scp\\b"
149
+ - "(?:^|[\\s;&|])rsync\\s+\\S+@"
150
+ source: "library:tier1.subagent"
151
+
152
+ # =================================================================
153
+ # § 4 — Sub-agents may not write outside scratch / project paths
154
+ # =================================================================
155
+ #
156
+ # System paths (/etc, /var, ~/, /usr/) are out of scope for any
157
+ # sub-agent task; if the work product needs them, the main
158
+ # agent should do it with user visibility.
159
+
160
+ - desc: "Sub-agents may not write to system / user-home paths"
161
+ E:
162
+ pattern: arg_blacklist
163
+ args:
164
+ - write
165
+ - file_path
166
+ - - "^/(etc|var|usr|opt|root|boot)/"
167
+ - "^/Users/[^/]+/[^/]" # macOS user home (any non-cwd)
168
+ - "^/home/[^/]+/[^/]" # Linux user home
169
+ - "^~/" # tilde-expanded home
170
+ - "^[A-Za-z]:\\\\\\\\Users\\\\\\\\" # Windows user home
171
+ source: "library:tier1.subagent"
172
+
173
+ - desc: "Sub-agents may not edit system / user-home paths"
174
+ E:
175
+ pattern: arg_blacklist
176
+ args:
177
+ - edit
178
+ - file_path
179
+ - - "^/(etc|var|usr|opt|root|boot)/"
180
+ - "^/Users/[^/]+/[^/]"
181
+ - "^/home/[^/]+/[^/]"
182
+ - "^~/"
183
+ - "^[A-Za-z]:\\\\\\\\Users\\\\\\\\"
184
+ source: "library:tier1.subagent"