@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.
- package/bin/sponsio.cjs +11 -0
- package/contracts/capability/credentials.yaml +8 -8
- package/contracts/capability/database.yaml +6 -6
- package/contracts/capability/filesystem-strict.yaml +4 -4
- package/contracts/capability/filesystem.yaml +9 -9
- package/contracts/capability/host-config-integrity.yaml +285 -0
- package/contracts/capability/self-modify.yaml +214 -9
- package/contracts/capability/shell.yaml +25 -32
- package/contracts/capability/subagent.yaml +5 -5
- package/contracts/core/llm_safety.yaml +23 -13
- package/contracts/incident/claude-code-secret-bypass.yaml +10 -11
- package/contracts/incident/cursor-railway-wipe.yaml +3 -3
- package/contracts/incident/mcp-composition.yaml +12 -12
- package/contracts/incident/openclaw.yaml +53 -45
- package/contracts/incident/subagent-escape.yaml +4 -4
- package/dist/cli/check.d.ts +2 -0
- package/dist/cli/check.d.ts.map +1 -0
- package/dist/cli/check.js +283 -0
- package/dist/cli/check.js.map +1 -0
- package/dist/cli/cli.d.ts +25 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +459 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/config.d.ts +88 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +243 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/demo.d.ts +12 -0
- package/dist/cli/demo.d.ts.map +1 -0
- package/dist/cli/demo.js +350 -0
- package/dist/cli/demo.js.map +1 -0
- package/dist/cli/doctor.d.ts +11 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +201 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/eval.d.ts +2 -0
- package/dist/cli/eval.d.ts.map +1 -0
- package/dist/cli/eval.js +361 -0
- package/dist/cli/eval.js.map +1 -0
- package/dist/cli/explain.d.ts +2 -0
- package/dist/cli/explain.d.ts.map +1 -0
- package/dist/cli/explain.js +266 -0
- package/dist/cli/explain.js.map +1 -0
- package/dist/cli/export-sessions.d.ts +2 -0
- package/dist/cli/export-sessions.d.ts.map +1 -0
- package/dist/cli/export-sessions.js +182 -0
- package/dist/cli/export-sessions.js.map +1 -0
- package/dist/cli/export.d.ts +2 -0
- package/dist/cli/export.d.ts.map +1 -0
- package/dist/cli/export.js +222 -0
- package/dist/cli/export.js.map +1 -0
- package/dist/cli/extractors/common.d.ts +27 -0
- package/dist/cli/extractors/common.d.ts.map +1 -0
- package/dist/cli/extractors/common.js +107 -0
- package/dist/cli/extractors/common.js.map +1 -0
- package/dist/cli/extractors/generic.d.ts +37 -0
- package/dist/cli/extractors/generic.d.ts.map +1 -0
- package/dist/cli/extractors/generic.js +185 -0
- package/dist/cli/extractors/generic.js.map +1 -0
- package/dist/cli/extractors/langchain.d.ts +18 -0
- package/dist/cli/extractors/langchain.d.ts.map +1 -0
- package/dist/cli/extractors/langchain.js +86 -0
- package/dist/cli/extractors/langchain.js.map +1 -0
- package/dist/cli/extractors/vercel.d.ts +17 -0
- package/dist/cli/extractors/vercel.d.ts.map +1 -0
- package/dist/cli/extractors/vercel.js +46 -0
- package/dist/cli/extractors/vercel.js.map +1 -0
- package/dist/cli/index.d.ts +38 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +146 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +177 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/mode.d.ts +2 -0
- package/dist/cli/mode.d.ts.map +1 -0
- package/dist/cli/mode.js +71 -0
- package/dist/cli/mode.js.map +1 -0
- package/dist/cli/onboard.d.ts +88 -0
- package/dist/cli/onboard.d.ts.map +1 -0
- package/dist/cli/onboard.js +758 -0
- package/dist/cli/onboard.js.map +1 -0
- package/dist/cli/otlp.d.ts +27 -0
- package/dist/cli/otlp.d.ts.map +1 -0
- package/dist/cli/otlp.js +79 -0
- package/dist/cli/otlp.js.map +1 -0
- package/dist/cli/package.json +1 -0
- package/dist/cli/packs.d.ts +16 -0
- package/dist/cli/packs.d.ts.map +1 -0
- package/dist/cli/packs.js +184 -0
- package/dist/cli/packs.js.map +1 -0
- package/dist/cli/patterns.d.ts +15 -0
- package/dist/cli/patterns.d.ts.map +1 -0
- package/dist/cli/patterns.js +135 -0
- package/dist/cli/patterns.js.map +1 -0
- package/dist/cli/prompt.d.ts +2 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +54 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/cli/replay.d.ts +2 -0
- package/dist/cli/replay.d.ts.map +1 -0
- package/dist/cli/replay.js +196 -0
- package/dist/cli/replay.js.map +1 -0
- package/dist/cli/report.d.ts +57 -0
- package/dist/cli/report.d.ts.map +1 -0
- package/dist/cli/report.js +298 -0
- package/dist/cli/report.js.map +1 -0
- package/dist/cli/scan.d.ts +2 -0
- package/dist/cli/scan.d.ts.map +1 -0
- package/dist/cli/scan.js +172 -0
- package/dist/cli/scan.js.map +1 -0
- package/dist/cli/skill.d.ts +15 -0
- package/dist/cli/skill.d.ts.map +1 -0
- package/dist/cli/skill.js +131 -0
- package/dist/cli/skill.js.map +1 -0
- package/dist/cli/tokens.d.ts +31 -0
- package/dist/cli/tokens.d.ts.map +1 -0
- package/dist/cli/tokens.js +73 -0
- package/dist/cli/tokens.js.map +1 -0
- package/dist/cli/types.d.ts +46 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +3 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/validate.d.ts +19 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/cli/validate.js +205 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/cli/zod.d.ts +36 -0
- package/dist/cli/zod.d.ts.map +1 -0
- package/dist/cli/zod.js +226 -0
- package/dist/cli/zod.js.map +1 -0
- package/dist/contract.d.ts +13 -13
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +21 -21
- package/dist/contract.js.map +1 -1
- package/dist/core/config-loader.d.ts +1 -1
- package/dist/core/config-loader.js +35 -17
- package/dist/core/config-loader.js.map +1 -1
- package/dist/core/grounding.d.ts +49 -2
- package/dist/core/grounding.d.ts.map +1 -1
- package/dist/core/grounding.js +302 -142
- package/dist/core/grounding.js.map +1 -1
- package/dist/core/nl-parser.d.ts.map +1 -1
- package/dist/core/nl-parser.js +74 -2
- package/dist/core/nl-parser.js.map +1 -1
- package/dist/core/pattern-factory.d.ts +4 -4
- package/dist/core/pattern-factory.d.ts.map +1 -1
- package/dist/core/pattern-factory.js +46 -4
- package/dist/core/pattern-factory.js.map +1 -1
- package/dist/core/patterns.d.ts +78 -4
- package/dist/core/patterns.d.ts.map +1 -1
- package/dist/core/patterns.js +211 -3
- package/dist/core/patterns.js.map +1 -1
- package/dist/core/spans.d.ts +1 -1
- package/dist/core/spans.d.ts.map +1 -1
- package/dist/core/spans.js.map +1 -1
- package/dist/core/sto.d.ts +32 -62
- package/dist/core/sto.d.ts.map +1 -1
- package/dist/core/sto.js +32 -90
- package/dist/core/sto.js.map +1 -1
- package/dist/index.d.ts +77 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +172 -133
- package/dist/index.js.map +1 -1
- package/dist/integrations/openai-agents.d.ts.map +1 -1
- package/dist/integrations/openai-agents.js +5 -4
- package/dist/integrations/openai-agents.js.map +1 -1
- package/dist/render/tokens.d.ts.map +1 -1
- package/dist/render/tokens.js +1 -0
- package/dist/render/tokens.js.map +1 -1
- package/package.json +28 -4
- package/prompts/onboard.md +250 -0
- package/prompts/refresh.md +118 -0
- package/prompts/scan.md +125 -0
- 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.
|
package/prompts/scan.md
ADDED
|
@@ -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.
|