@sponsio/sdk 0.2.0-alpha.4 → 0.2.0-alpha.8
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/contracts/capability/destructive.yaml +213 -0
- package/contracts/capability/factual-claims.yaml +79 -0
- package/contracts/capability/host-config-integrity.yaml +42 -9
- package/contracts/capability/self-modify.yaml +32 -1
- package/contracts/capability/shell.yaml +19 -10
- package/contracts/incident/claude-code-secret-bypass.yaml +46 -14
- package/contracts/incident/cursor-railway-wipe.yaml +60 -4
- package/contracts/incident/openclaw.yaml +9 -63
- package/dist/cli/cli.js +32 -0
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/packs.d.ts.map +1 -1
- package/dist/cli/packs.js +13 -7
- package/dist/cli/packs.js.map +1 -1
- package/dist/cloud/client.d.ts +76 -0
- package/dist/cloud/client.d.ts.map +1 -0
- package/dist/cloud/client.js +198 -0
- package/dist/cloud/client.js.map +1 -0
- package/dist/cloud/evidence.d.ts +108 -0
- package/dist/cloud/evidence.d.ts.map +1 -0
- package/dist/cloud/evidence.js +184 -0
- package/dist/cloud/evidence.js.map +1 -0
- package/dist/cloud/privacy.d.ts +67 -0
- package/dist/cloud/privacy.d.ts.map +1 -0
- package/dist/cloud/privacy.js +170 -0
- package/dist/cloud/privacy.js.map +1 -0
- package/dist/cloud/view-model.d.ts +49 -0
- package/dist/cloud/view-model.d.ts.map +1 -0
- package/dist/cloud/view-model.js +172 -0
- package/dist/cloud/view-model.js.map +1 -0
- package/dist/core/config-loader.js +16 -0
- package/dist/core/config-loader.js.map +1 -1
- package/dist/core/evaluator.d.ts +2 -0
- package/dist/core/evaluator.d.ts.map +1 -1
- package/dist/core/evaluator.js +90 -4
- package/dist/core/evaluator.js.map +1 -1
- package/dist/core/grounding.d.ts +43 -7
- package/dist/core/grounding.d.ts.map +1 -1
- package/dist/core/grounding.js +180 -25
- package/dist/core/grounding.js.map +1 -1
- package/dist/core/nl-parser.d.ts +1 -1
- package/dist/core/nl-parser.d.ts.map +1 -1
- package/dist/core/nl-parser.js +616 -40
- package/dist/core/nl-parser.js.map +1 -1
- package/dist/core/pattern-factory.d.ts.map +1 -1
- package/dist/core/pattern-factory.js +7 -1
- package/dist/core/pattern-factory.js.map +1 -1
- package/dist/core/patterns.d.ts +69 -0
- package/dist/core/patterns.d.ts.map +1 -1
- package/dist/core/patterns.js +133 -10
- package/dist/core/patterns.js.map +1 -1
- package/dist/core/tool-names.d.ts +59 -0
- package/dist/core/tool-names.d.ts.map +1 -0
- package/dist/core/tool-names.js +128 -0
- package/dist/core/tool-names.js.map +1 -0
- package/dist/index.d.ts +144 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +367 -32
- package/dist/index.js.map +1 -1
- package/dist/integrations/claude-agent.d.ts +5 -0
- package/dist/integrations/claude-agent.d.ts.map +1 -1
- package/dist/integrations/claude-agent.js +42 -13
- package/dist/integrations/claude-agent.js.map +1 -1
- package/dist/integrations/google-adk.d.ts +5 -3
- package/dist/integrations/google-adk.d.ts.map +1 -1
- package/dist/integrations/google-adk.js +9 -5
- package/dist/integrations/google-adk.js.map +1 -1
- package/dist/integrations/langchain.d.ts +7 -0
- package/dist/integrations/langchain.d.ts.map +1 -1
- package/dist/integrations/langchain.js +21 -3
- package/dist/integrations/langchain.js.map +1 -1
- package/dist/integrations/openai-agents.d.ts +22 -10
- package/dist/integrations/openai-agents.d.ts.map +1 -1
- package/dist/integrations/openai-agents.js +51 -12
- package/dist/integrations/openai-agents.js.map +1 -1
- package/dist/integrations/openai.d.ts +11 -0
- package/dist/integrations/openai.d.ts.map +1 -1
- package/dist/integrations/openai.js +130 -48
- package/dist/integrations/openai.js.map +1 -1
- package/dist/integrations/vercel-ai.d.ts +20 -9
- package/dist/integrations/vercel-ai.d.ts.map +1 -1
- package/dist/integrations/vercel-ai.js +131 -50
- package/dist/integrations/vercel-ai.js.map +1 -1
- package/package.json +12 -8
- package/prompts/onboard.md +17 -4
- package/contracts/core/llm_safety.yaml +0 -138
|
@@ -8,64 +8,146 @@
|
|
|
8
8
|
* const guard = new Sponsio({ contracts: [...] })
|
|
9
9
|
* const client = wrapOpenAI(new OpenAI(), guard)
|
|
10
10
|
*
|
|
11
|
+
* Covers ``chat.completions.create`` / ``parse`` and, when the client has
|
|
12
|
+
* it, ``responses.create`` / ``parse``. Every tool call the model emits
|
|
13
|
+
* is checked before the caller's tool loop can run it; refused calls are
|
|
14
|
+
* removed from the response and a ``[BLOCKED] <tool>: <reason>`` line is
|
|
15
|
+
* added to the assistant text so the model learns why.
|
|
16
|
+
*
|
|
17
|
+
* ``stream: true`` throws at call time on every wrapped method: tool
|
|
18
|
+
* calls arrive as deltas and could only be checked after the caller has
|
|
19
|
+
* already assembled and run them. Call without ``stream``, or run your
|
|
20
|
+
* own executor through ``guard.guardBefore()`` before each tool call.
|
|
21
|
+
*
|
|
11
22
|
* ``patchOpenAI`` is an alias matching Python's ``patch_openai``
|
|
12
23
|
* factory (same monkey-patch semantics — no wrapped return value
|
|
13
24
|
* required; the client passed in is mutated in place).
|
|
14
25
|
*/
|
|
26
|
+
const STREAM_UNSUPPORTED = "sponsio: the OpenAI guard does not support stream: true. Tool calls " +
|
|
27
|
+
"arrive as deltas and could only be checked after the caller has already " +
|
|
28
|
+
"assembled and run them. Call without stream, or run your own executor " +
|
|
29
|
+
"through guard.guardBefore() before each tool call.";
|
|
15
30
|
export function wrapOpenAI(client, guard) {
|
|
16
31
|
const c = client;
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
const completions = c?.chat?.completions;
|
|
33
|
+
if (!completions || typeof completions.create !== "function") {
|
|
34
|
+
throw new TypeError("wrapOpenAI: expected an OpenAI client with chat.completions.create");
|
|
35
|
+
}
|
|
36
|
+
patchMethod(completions, "create", guard, "chat");
|
|
37
|
+
patchMethod(completions, "parse", guard, "chat");
|
|
38
|
+
if (c.responses) {
|
|
39
|
+
patchMethod(c.responses, "create", guard, "responses");
|
|
40
|
+
patchMethod(c.responses, "parse", guard, "responses");
|
|
41
|
+
}
|
|
42
|
+
return client;
|
|
43
|
+
}
|
|
44
|
+
function patchMethod(target, name, guard, kind) {
|
|
45
|
+
const original = target[name];
|
|
46
|
+
if (typeof original !== "function")
|
|
47
|
+
return;
|
|
48
|
+
const bound = original.bind(target);
|
|
49
|
+
target[name] = async function (...args) {
|
|
50
|
+
const params = args[0];
|
|
51
|
+
if (params && typeof params === "object" && params.stream) {
|
|
52
|
+
throw new Error(STREAM_UNSUPPORTED);
|
|
53
|
+
}
|
|
54
|
+
const response = await bound(...args);
|
|
55
|
+
return kind === "chat"
|
|
56
|
+
? guardChatResponse(response, guard)
|
|
57
|
+
: guardResponsesOutput(response, guard);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Decide one emitted call. Malformed JSON arguments are refused in
|
|
62
|
+
* enforce mode without touching the trace (a call that never runs must
|
|
63
|
+
* not count as a call); in observe mode they pass through and are
|
|
64
|
+
* recorded with a sentinel so the session log shows what arrived.
|
|
65
|
+
*/
|
|
66
|
+
function decide(guard, toolName, rawArgs) {
|
|
67
|
+
let args;
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(rawArgs || "{}");
|
|
70
|
+
args =
|
|
71
|
+
parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
72
|
+
? parsed
|
|
73
|
+
: { input: parsed };
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
if (guard.mode === "enforce") {
|
|
77
|
+
return {
|
|
78
|
+
blocked: true,
|
|
79
|
+
note: `[BLOCKED] ${toolName}: malformed JSON arguments — refusing to forward to the tool loop`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
args = { _sponsio_malformed_args: rawArgs ?? "" };
|
|
83
|
+
}
|
|
84
|
+
const result = guard.guardBefore(toolName, args);
|
|
85
|
+
// ``stopOriginal`` covers a redirect verdict as well as a block: the
|
|
86
|
+
// wrapper has no substitution path, so a redirect strips the call.
|
|
87
|
+
if (result.stopOriginal) {
|
|
88
|
+
return { blocked: true, note: `[BLOCKED] ${toolName}: ${result.message}` };
|
|
89
|
+
}
|
|
90
|
+
return { blocked: false };
|
|
91
|
+
}
|
|
92
|
+
function guardChatResponse(response, guard) {
|
|
93
|
+
for (const choice of response?.choices ?? []) {
|
|
94
|
+
const msg = choice.message;
|
|
95
|
+
if (!msg?.tool_calls)
|
|
96
|
+
continue;
|
|
97
|
+
const kept = [];
|
|
98
|
+
const blocked = [];
|
|
99
|
+
for (const tc of msg.tool_calls) {
|
|
100
|
+
const d = decide(guard, tc.function.name, tc.function.arguments);
|
|
101
|
+
if (d.blocked)
|
|
102
|
+
blocked.push(d.note);
|
|
103
|
+
else
|
|
104
|
+
kept.push(tc);
|
|
105
|
+
}
|
|
106
|
+
msg.tool_calls = kept.length > 0 ? kept : null;
|
|
107
|
+
if (blocked.length > 0 && !msg.tool_calls) {
|
|
108
|
+
// Match Python's ``_filter_blocked_calls``: join with a newline
|
|
109
|
+
// separator but no leading newline before the first message.
|
|
110
|
+
msg.content = (msg.content ?? "") + blocked.join("\n");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return response;
|
|
114
|
+
}
|
|
115
|
+
function guardResponsesOutput(response, guard) {
|
|
116
|
+
const output = response?.output;
|
|
117
|
+
if (!Array.isArray(output))
|
|
118
|
+
return response;
|
|
119
|
+
const kept = [];
|
|
120
|
+
const blocked = [];
|
|
121
|
+
for (const item of output) {
|
|
122
|
+
if (item?.type === "function_call") {
|
|
123
|
+
const d = decide(guard, item.name ?? "", item.arguments);
|
|
124
|
+
if (d.blocked) {
|
|
125
|
+
blocked.push(d.note);
|
|
23
126
|
continue;
|
|
24
|
-
const kept = [];
|
|
25
|
-
const blocked = [];
|
|
26
|
-
for (const tc of msg.tool_calls) {
|
|
27
|
-
let tcArgs = {};
|
|
28
|
-
let parseFailed = false;
|
|
29
|
-
try {
|
|
30
|
-
tcArgs = JSON.parse(tc.function.arguments || "{}");
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
parseFailed = true;
|
|
34
|
-
}
|
|
35
|
-
// Malformed arguments bypass any arg-based contract
|
|
36
|
-
// (``arg_blacklist``, ``arg_length_limit``, ``scope_limit``,
|
|
37
|
-
// ``dangerous_sql_verbs``, ``arg_value_range``) if we treat
|
|
38
|
-
// them as empty. Feed a sentinel so the guard still sees
|
|
39
|
-
// *something* distinctive, and block-by-default so a
|
|
40
|
-
// malformed payload doesn't silently slip through.
|
|
41
|
-
if (parseFailed) {
|
|
42
|
-
guard.guardBefore(tc.function.name, {
|
|
43
|
-
_sponsio_malformed_args: tc.function.arguments ?? "",
|
|
44
|
-
});
|
|
45
|
-
blocked.push(`[BLOCKED] ${tc.function.name}: malformed JSON arguments — ` +
|
|
46
|
-
`refusing to forward to tool loop (set ` +
|
|
47
|
-
`SPONSIO_OPENAI_STRICT_TOOL_ARGS=0 to downgrade once the ` +
|
|
48
|
-
`parity flag ships in TS)`);
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
const result = guard.guardBefore(tc.function.name, tcArgs);
|
|
52
|
-
if (result.blocked) {
|
|
53
|
-
blocked.push(`[BLOCKED] ${tc.function.name}: ${result.message}`);
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
kept.push(tc);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
msg.tool_calls = kept.length > 0 ? kept : null;
|
|
60
|
-
if (blocked.length > 0 && !msg.tool_calls) {
|
|
61
|
-
// Match Python's ``_filter_blocked_calls``: join with a newline
|
|
62
|
-
// separator but no leading newline before the first message.
|
|
63
|
-
msg.content = (msg.content ?? "") + blocked.join("\n");
|
|
64
127
|
}
|
|
65
128
|
}
|
|
129
|
+
kept.push(item);
|
|
130
|
+
}
|
|
131
|
+
if (blocked.length === 0)
|
|
66
132
|
return response;
|
|
67
|
-
|
|
68
|
-
|
|
133
|
+
response.output = kept;
|
|
134
|
+
const note = blocked.join("\n");
|
|
135
|
+
const lastMessage = [...kept].reverse().find((i) => i?.type === "message");
|
|
136
|
+
const textPart = lastMessage?.content
|
|
137
|
+
? [...lastMessage.content].reverse().find((p) => typeof p?.text === "string")
|
|
138
|
+
: undefined;
|
|
139
|
+
if (textPart) {
|
|
140
|
+
textPart.text = `${textPart.text}\n${note}`;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
kept.push({
|
|
144
|
+
type: "message",
|
|
145
|
+
role: "assistant",
|
|
146
|
+
status: "completed",
|
|
147
|
+
content: [{ type: "output_text", text: note, annotations: [] }],
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return response;
|
|
69
151
|
}
|
|
70
152
|
/**
|
|
71
153
|
* Alias for ``wrapOpenAI`` — matches Python's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/integrations/openai.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/integrations/openai.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,19 +10,30 @@
|
|
|
10
10
|
* const model = wrapLanguageModel({ model, middleware: sponsioMiddleware(guard) })
|
|
11
11
|
*
|
|
12
12
|
* Behaviour:
|
|
13
|
-
* - guardBefore
|
|
13
|
+
* - guardBefore runs for every tool call the model emits, on both
|
|
14
|
+
* ``generateText`` (``wrapGenerate``) and ``streamText``
|
|
15
|
+
* (``wrapStream``), before the AI SDK executes the tool.
|
|
14
16
|
* - Allowed calls pass through untouched.
|
|
15
|
-
* - Blocked calls are *dropped*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* - Blocked calls are *dropped* (so the AI SDK never executes them and
|
|
18
|
+
* ``parseToolCall`` doesn't reject the response), and a
|
|
19
|
+
* ``[Sponsio blocked: <reason>]`` note is added to the model's text.
|
|
20
|
+
* If every emitted call was blocked, the finishReason is forced to
|
|
21
|
+
* ``stop`` so the agent loop terminates instead of spinning.
|
|
22
|
+
* - Both provider result shapes are handled: ``ai`` v4
|
|
23
|
+
* (``LanguageModelV1``: ``result.toolCalls[]`` with ``args``) and
|
|
24
|
+
* ``ai`` v5 and later (``LanguageModelV2`` / ``V3``:
|
|
25
|
+
* ``result.content[]`` with ``{ type: "tool-call", input }`` parts).
|
|
26
|
+
* - The model's args arrive JSON-stringified at the language-model
|
|
27
|
+
* layer; the middleware parses defensively before handing them to the
|
|
23
28
|
* guard so the contract evaluator sees a real object.
|
|
24
29
|
*/
|
|
25
30
|
import type { Sponsio } from "../index.js";
|
|
31
|
+
/**
|
|
32
|
+
* The model's arguments as an object. A string that is not JSON is kept
|
|
33
|
+
* under ``_sponsio_malformed_args`` so coarse regex contracts still see
|
|
34
|
+
* it instead of an empty object that passes every argument rule.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseArgs(raw: unknown): Record<string, unknown>;
|
|
26
37
|
export declare function sponsioMiddleware(guard: Sponsio): {
|
|
27
38
|
transformParams: ({ params }: {
|
|
28
39
|
params: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../src/integrations/vercel-ai.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../src/integrations/vercel-ai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAsC3C;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAc/D;AA0CD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO;kCAER;QAAE,MAAM,EAAE,GAAG,CAAA;KAAE,KAAG,OAAO,CAAC,GAAG,CAAC;oCAI/D;QACD,UAAU,EAAE,MAAM,GAAG,CAAC;QACtB,MAAM,EAAE,GAAG,CAAC;KACb,KAAG,OAAO,CAAC,GAAG,CAAC;+BAuDiB;QAAE,QAAQ,EAAE,MAAM,GAAG,CAAA;KAAE,KAAG,OAAO,CAAC,GAAG,CAAC;EA4C1E"}
|
|
@@ -10,16 +10,21 @@
|
|
|
10
10
|
* const model = wrapLanguageModel({ model, middleware: sponsioMiddleware(guard) })
|
|
11
11
|
*
|
|
12
12
|
* Behaviour:
|
|
13
|
-
* - guardBefore
|
|
13
|
+
* - guardBefore runs for every tool call the model emits, on both
|
|
14
|
+
* ``generateText`` (``wrapGenerate``) and ``streamText``
|
|
15
|
+
* (``wrapStream``), before the AI SDK executes the tool.
|
|
14
16
|
* - Allowed calls pass through untouched.
|
|
15
|
-
* - Blocked calls are *dropped*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* - Blocked calls are *dropped* (so the AI SDK never executes them and
|
|
18
|
+
* ``parseToolCall`` doesn't reject the response), and a
|
|
19
|
+
* ``[Sponsio blocked: <reason>]`` note is added to the model's text.
|
|
20
|
+
* If every emitted call was blocked, the finishReason is forced to
|
|
21
|
+
* ``stop`` so the agent loop terminates instead of spinning.
|
|
22
|
+
* - Both provider result shapes are handled: ``ai`` v4
|
|
23
|
+
* (``LanguageModelV1``: ``result.toolCalls[]`` with ``args``) and
|
|
24
|
+
* ``ai`` v5 and later (``LanguageModelV2`` / ``V3``:
|
|
25
|
+
* ``result.content[]`` with ``{ type: "tool-call", input }`` parts).
|
|
26
|
+
* - The model's args arrive JSON-stringified at the language-model
|
|
27
|
+
* layer; the middleware parses defensively before handing them to the
|
|
23
28
|
* guard so the contract evaluator sees a real object.
|
|
24
29
|
*/
|
|
25
30
|
function emitBanner(toolName, reason, agentId) {
|
|
@@ -38,7 +43,12 @@ function emitBanner(toolName, reason, agentId) {
|
|
|
38
43
|
];
|
|
39
44
|
process.stderr.write(lines.join("\n"));
|
|
40
45
|
}
|
|
41
|
-
|
|
46
|
+
/**
|
|
47
|
+
* The model's arguments as an object. A string that is not JSON is kept
|
|
48
|
+
* under ``_sponsio_malformed_args`` so coarse regex contracts still see
|
|
49
|
+
* it instead of an empty object that passes every argument rule.
|
|
50
|
+
*/
|
|
51
|
+
export function parseArgs(raw) {
|
|
42
52
|
if (raw == null)
|
|
43
53
|
return {};
|
|
44
54
|
if (typeof raw === "object" && !Array.isArray(raw))
|
|
@@ -48,55 +58,91 @@ function parseArgs(raw) {
|
|
|
48
58
|
const parsed = JSON.parse(raw);
|
|
49
59
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
50
60
|
? parsed
|
|
51
|
-
: {};
|
|
61
|
+
: { input: parsed };
|
|
52
62
|
}
|
|
53
63
|
catch {
|
|
54
|
-
return {};
|
|
64
|
+
return { _sponsio_malformed_args: raw };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { input: raw };
|
|
68
|
+
}
|
|
69
|
+
/** Compact one-line reason for the appended note; full message stays in the session log. */
|
|
70
|
+
function trimReason(msg) {
|
|
71
|
+
// Strip prefix via indexOf rather than regex — every regex form that
|
|
72
|
+
// matched the pattern ``^[A-Z-]*BLOCKED:\s*[^—]+—\s*`` kept tripping
|
|
73
|
+
// CodeQL js/polynomial-redos (the ``\s*`` and ``[^—]+`` overlap on
|
|
74
|
+
// spaces). indexOf is O(n) with no backtracking.
|
|
75
|
+
let trimmed = msg;
|
|
76
|
+
const blockedIdx = trimmed.indexOf("BLOCKED:");
|
|
77
|
+
if (blockedIdx >= 0 && blockedIdx <= 64) {
|
|
78
|
+
const dashIdx = trimmed.indexOf("—", blockedIdx + "BLOCKED:".length);
|
|
79
|
+
if (dashIdx >= 0) {
|
|
80
|
+
trimmed = trimmed.slice(dashIdx + "—".length).trimStart();
|
|
55
81
|
}
|
|
56
82
|
}
|
|
57
|
-
|
|
83
|
+
const head = trimmed.toLowerCase();
|
|
84
|
+
if (head.startsWith("det constraint violated:")) {
|
|
85
|
+
trimmed = trimmed.slice("det constraint violated:".length).trimStart();
|
|
86
|
+
}
|
|
87
|
+
else if (head.startsWith("violated:")) {
|
|
88
|
+
trimmed = trimmed.slice("violated:".length).trimStart();
|
|
89
|
+
}
|
|
90
|
+
return trimmed.split("\n")[0];
|
|
91
|
+
}
|
|
92
|
+
/** Run one emitted tool call through the guard, before anything executes it. */
|
|
93
|
+
function decide(guard, tc) {
|
|
94
|
+
const check = guard.guardBefore(tc.toolName, parseArgs(tc.input ?? tc.args));
|
|
95
|
+
// ``stopOriginal`` covers a redirect verdict as well as a block: the
|
|
96
|
+
// middleware has no substitution path, so a redirect drops the call.
|
|
97
|
+
if (!check.stopOriginal)
|
|
98
|
+
return { blocked: false };
|
|
99
|
+
const reason = trimReason(check.message ?? `${tc.toolName} blocked by Sponsio`);
|
|
100
|
+
emitBanner(tc.toolName, reason, guard.agentId);
|
|
101
|
+
return { blocked: true, reason };
|
|
102
|
+
}
|
|
103
|
+
function blockNote(reasons) {
|
|
104
|
+
return `[Sponsio blocked: ${reasons.join("; ")}]`;
|
|
58
105
|
}
|
|
59
106
|
export function sponsioMiddleware(guard) {
|
|
60
107
|
return {
|
|
61
108
|
transformParams: async ({ params }) => params,
|
|
62
109
|
wrapGenerate: async ({ doGenerate, }) => {
|
|
63
110
|
const result = await doGenerate();
|
|
111
|
+
// ai v5+: tool calls are ``tool-call`` parts inside ``content``.
|
|
112
|
+
if (Array.isArray(result.content)) {
|
|
113
|
+
const content = [];
|
|
114
|
+
const blockedReasons = [];
|
|
115
|
+
let survivingCalls = 0;
|
|
116
|
+
for (const part of result.content) {
|
|
117
|
+
if (part?.type === "tool-call") {
|
|
118
|
+
const d = decide(guard, part);
|
|
119
|
+
if (d.blocked) {
|
|
120
|
+
blockedReasons.push(`${String(part.toolName)}: ${d.reason}`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
survivingCalls++;
|
|
124
|
+
}
|
|
125
|
+
content.push(part);
|
|
126
|
+
}
|
|
127
|
+
if (blockedReasons.length === 0)
|
|
128
|
+
return result;
|
|
129
|
+
content.push({ type: "text", text: blockNote(blockedReasons) });
|
|
130
|
+
return {
|
|
131
|
+
...result,
|
|
132
|
+
content,
|
|
133
|
+
finishReason: survivingCalls === 0 ? "stop" : result.finishReason,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// ai v4: tool calls are ``result.toolCalls``.
|
|
64
137
|
const calls = result.toolCalls ?? [];
|
|
65
138
|
if (calls.length === 0)
|
|
66
139
|
return result;
|
|
67
140
|
const surviving = [];
|
|
68
141
|
const blockedReasons = [];
|
|
69
142
|
for (const tc of calls) {
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
// prefix and any "det constraint violated:" / "violated:" boilerplate
|
|
74
|
-
// so the appended text is concise; full message stays in the session log.
|
|
75
|
-
const msg = check.message ?? `${tc.toolName} blocked by Sponsio`;
|
|
76
|
-
// Strip prefix via indexOf rather than regex — every regex
|
|
77
|
-
// form that matched the pattern
|
|
78
|
-
// ``^[A-Z-]*BLOCKED:\s*[^—]+—\s*`` kept tripping CodeQL
|
|
79
|
-
// js/polynomial-redos (the ``\s*`` and ``[^—]+`` overlap on
|
|
80
|
-
// spaces). indexOf is O(n) with no backtracking.
|
|
81
|
-
let trimmed = msg;
|
|
82
|
-
const blockedIdx = trimmed.indexOf("BLOCKED:");
|
|
83
|
-
if (blockedIdx >= 0 && blockedIdx <= 64) {
|
|
84
|
-
const dashIdx = trimmed.indexOf("—", blockedIdx + "BLOCKED:".length);
|
|
85
|
-
if (dashIdx >= 0) {
|
|
86
|
-
trimmed = trimmed.slice(dashIdx + "—".length).trimStart();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
// Strip optional "(det constraint )?violated:" boilerplate.
|
|
90
|
-
const head = trimmed.toLowerCase();
|
|
91
|
-
if (head.startsWith("det constraint violated:")) {
|
|
92
|
-
trimmed = trimmed.slice("det constraint violated:".length).trimStart();
|
|
93
|
-
}
|
|
94
|
-
else if (head.startsWith("violated:")) {
|
|
95
|
-
trimmed = trimmed.slice("violated:".length).trimStart();
|
|
96
|
-
}
|
|
97
|
-
const reason = trimmed.split("\n")[0];
|
|
98
|
-
emitBanner(tc.toolName, reason, guard.agentId);
|
|
99
|
-
blockedReasons.push(`${tc.toolName}: ${reason}`);
|
|
143
|
+
const d = decide(guard, tc);
|
|
144
|
+
if (d.blocked) {
|
|
145
|
+
blockedReasons.push(`${tc.toolName}: ${d.reason}`);
|
|
100
146
|
}
|
|
101
147
|
else {
|
|
102
148
|
surviving.push(tc);
|
|
@@ -104,8 +150,8 @@ export function sponsioMiddleware(guard) {
|
|
|
104
150
|
}
|
|
105
151
|
if (blockedReasons.length === 0)
|
|
106
152
|
return result;
|
|
107
|
-
const
|
|
108
|
-
const newText = result.text ? `${result.text}\n\n${
|
|
153
|
+
const note = blockNote(blockedReasons);
|
|
154
|
+
const newText = result.text ? `${result.text}\n\n${note}` : note;
|
|
109
155
|
return {
|
|
110
156
|
...result,
|
|
111
157
|
toolCalls: surviving,
|
|
@@ -114,11 +160,46 @@ export function sponsioMiddleware(guard) {
|
|
|
114
160
|
};
|
|
115
161
|
},
|
|
116
162
|
wrapStream: async ({ doStream }) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
163
|
+
const streamed = await doStream();
|
|
164
|
+
const source = streamed?.stream;
|
|
165
|
+
if (!source || typeof source.pipeThrough !== "function")
|
|
166
|
+
return streamed;
|
|
167
|
+
// ``streamText`` executes a tool when the ``tool-call`` part arrives,
|
|
168
|
+
// so that part is the gate. Earlier ``tool-input-*`` /
|
|
169
|
+
// ``tool-call-delta`` parts only render partial arguments and pass
|
|
170
|
+
// through; nothing runs on them.
|
|
171
|
+
let survivingCalls = 0;
|
|
172
|
+
let blockedAny = false;
|
|
173
|
+
let noteId = 0;
|
|
174
|
+
const gated = source.pipeThrough(new TransformStream({
|
|
175
|
+
transform(part, controller) {
|
|
176
|
+
if (part?.type === "tool-call") {
|
|
177
|
+
const d = decide(guard, part);
|
|
178
|
+
if (d.blocked) {
|
|
179
|
+
blockedAny = true;
|
|
180
|
+
const note = blockNote([`${String(part.toolName)}: ${d.reason}`]);
|
|
181
|
+
if ("input" in part) {
|
|
182
|
+
// v5+ text streams are start / delta / end triples.
|
|
183
|
+
const id = `sponsio-block-${++noteId}`;
|
|
184
|
+
controller.enqueue({ type: "text-start", id });
|
|
185
|
+
controller.enqueue({ type: "text-delta", id, delta: note });
|
|
186
|
+
controller.enqueue({ type: "text-end", id });
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
controller.enqueue({ type: "text-delta", textDelta: note });
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
survivingCalls++;
|
|
194
|
+
}
|
|
195
|
+
if (part?.type === "finish" && blockedAny && survivingCalls === 0) {
|
|
196
|
+
controller.enqueue({ ...part, finishReason: "stop" });
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
controller.enqueue(part);
|
|
200
|
+
},
|
|
201
|
+
}));
|
|
202
|
+
return { ...streamed, stream: gated };
|
|
122
203
|
},
|
|
123
204
|
};
|
|
124
205
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-ai.js","sourceRoot":"","sources":["../../src/integrations/vercel-ai.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vercel-ai.js","sourceRoot":"","sources":["../../src/integrations/vercel-ai.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sponsio/sdk",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
4
|
-
"description": "Runtime contract enforcement for LLM agents
|
|
3
|
+
"version": "0.2.0-alpha.8",
|
|
4
|
+
"description": "Runtime contract enforcement for LLM agents \u2014 native TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
"./langchain": {
|
|
47
47
|
"types": "./dist/integrations/langchain.d.ts",
|
|
48
48
|
"default": "./dist/integrations/langchain.js"
|
|
49
|
+
},
|
|
50
|
+
"./cloud/evidence": {
|
|
51
|
+
"types": "./dist/cloud/evidence.d.ts",
|
|
52
|
+
"import": "./dist/cloud/evidence.js"
|
|
49
53
|
}
|
|
50
54
|
},
|
|
51
55
|
"bin": {
|
|
@@ -63,22 +67,22 @@
|
|
|
63
67
|
],
|
|
64
68
|
"scripts": {
|
|
65
69
|
"build": "tsc && tsc -p tsconfig.cli.json && node -e \"require('fs').writeFileSync('dist/cli/package.json', '{\\\"type\\\":\\\"commonjs\\\"}\\n')\"",
|
|
66
|
-
"test": "node --test dist/
|
|
70
|
+
"test": "node --test dist/",
|
|
67
71
|
"test:cli": "node --test dist/cli/__tests__/*.test.js",
|
|
68
72
|
"test:src": "npx tsx src/__tests__/core.test.ts",
|
|
69
73
|
"test:parity": "npx tsx src/__tests__/parity.test.ts && npx tsx src/__tests__/parity.scenarios.test.ts"
|
|
70
74
|
},
|
|
71
75
|
"dependencies": {
|
|
72
76
|
"fast-glob": "^3.3.0",
|
|
73
|
-
"js-yaml": "^5.2
|
|
77
|
+
"js-yaml": "^5.4.2",
|
|
74
78
|
"ts-morph": "^28.0.0",
|
|
75
|
-
"yaml": "^2.9.
|
|
79
|
+
"yaml": "^2.9.1"
|
|
76
80
|
},
|
|
77
81
|
"devDependencies": {
|
|
78
82
|
"@types/js-yaml": "^4.0.5",
|
|
79
|
-
"@types/node": "^26.
|
|
80
|
-
"esbuild": "^0.28.
|
|
81
|
-
"tsx": "^4.23.
|
|
83
|
+
"@types/node": "^26.6.2",
|
|
84
|
+
"esbuild": "^0.28.2",
|
|
85
|
+
"tsx": "^4.23.15",
|
|
82
86
|
"typescript": "^5.5.0"
|
|
83
87
|
},
|
|
84
88
|
"peerDependencies": {
|
package/prompts/onboard.md
CHANGED
|
@@ -175,10 +175,23 @@ agents:
|
|
|
175
175
|
|
|
176
176
|
## Pattern vocabulary
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
`
|
|
181
|
-
|
|
178
|
+
Run ``sponsio patterns`` and use the FULL list it prints — do not
|
|
179
|
+
rely on a memorized subset. The library spans temporal ordering
|
|
180
|
+
(`must_precede`, `no_reversal`, `always_followed_by`, …), argument &
|
|
181
|
+
path constraints (`arg_blacklist`, `arg_allowlist`, `scope_limit`,
|
|
182
|
+
`arg_value_range`, `arg_length_limit`, …), OWASP presets
|
|
183
|
+
(`dangerous_sql_verbs`, `dangerous_bash_commands`,
|
|
184
|
+
`destructive_action_gate`, `tool_allowlist`, …), rate / resource
|
|
185
|
+
caps (`rate_limit`, `idempotent`, `loop_detection`, …), and data
|
|
186
|
+
flow (`no_data_leak`). Policy docs about SQL safety, data
|
|
187
|
+
isolation, taint, or approval gates almost always need patterns
|
|
188
|
+
outside the arg-constraint family — check the full list before
|
|
189
|
+
concluding a rule can't be expressed.
|
|
190
|
+
|
|
191
|
+
If no pattern fits, two fallbacks (in order): a plain NL rule that
|
|
192
|
+
``sponsio validate`` accepts, or a raw formula over the atomic
|
|
193
|
+
propositions in ``sponsio/tracer/grounding.py`` (`called`,
|
|
194
|
+
`called_with`, `arg_has`, `arg_field_has`, `contains`, `flow`).
|
|
182
195
|
|
|
183
196
|
## Source tagging
|
|
184
197
|
|