@intutic/gate 1.10.1
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/LICENSE +21 -0
- package/README.md +261 -0
- package/dist/actions.d.ts +86 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +239 -0
- package/dist/actions.js.map +1 -0
- package/dist/client.d.ts +94 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +207 -0
- package/dist/client.js.map +1 -0
- package/dist/dsh.d.ts +162 -0
- package/dist/dsh.d.ts.map +1 -0
- package/dist/dsh.js +189 -0
- package/dist/dsh.js.map +1 -0
- package/dist/errors.d.ts +41 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +53 -0
- package/dist/errors.js.map +1 -0
- package/dist/eve.d.ts +425 -0
- package/dist/eve.d.ts.map +1 -0
- package/dist/eve.js +378 -0
- package/dist/eve.js.map +1 -0
- package/dist/gate.d.ts +113 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +274 -0
- package/dist/gate.js.map +1 -0
- package/dist/harness.d.ts +482 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +504 -0
- package/dist/harness.js.map +1 -0
- package/dist/headers.d.ts +26 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +30 -0
- package/dist/headers.js.map +1 -0
- package/dist/imagecheck.d.ts +93 -0
- package/dist/imagecheck.d.ts.map +1 -0
- package/dist/imagecheck.js +286 -0
- package/dist/imagecheck.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/managedAgents.d.ts +375 -0
- package/dist/managedAgents.d.ts.map +1 -0
- package/dist/managedAgents.js +446 -0
- package/dist/managedAgents.js.map +1 -0
- package/dist/mastra.d.ts +175 -0
- package/dist/mastra.d.ts.map +1 -0
- package/dist/mastra.js +161 -0
- package/dist/mastra.js.map +1 -0
- package/dist/openai.d.ts +392 -0
- package/dist/openai.d.ts.map +1 -0
- package/dist/openai.js +658 -0
- package/dist/openai.js.map +1 -0
- package/dist/shlex.d.ts +16 -0
- package/dist/shlex.d.ts.map +1 -0
- package/dist/shlex.js +78 -0
- package/dist/shlex.js.map +1 -0
- package/dist/snapshot.d.ts +111 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +243 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/soprules.d.ts +88 -0
- package/dist/soprules.d.ts.map +1 -0
- package/dist/soprules.js +176 -0
- package/dist/soprules.js.map +1 -0
- package/dist/trueforge.d.ts +191 -0
- package/dist/trueforge.d.ts.map +1 -0
- package/dist/trueforge.js +184 -0
- package/dist/trueforge.js.map +1 -0
- package/dist/vercel.d.ts +174 -0
- package/dist/vercel.d.ts.map +1 -0
- package/dist/vercel.js +157 -0
- package/dist/vercel.js.map +1 -0
- package/dist/workflow.d.ts +209 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +254 -0
- package/dist/workflow.js.map +1 -0
- package/dist/wrapTools.d.ts +68 -0
- package/dist/wrapTools.d.ts.map +1 -0
- package/dist/wrapTools.js +95 -0
- package/dist/wrapTools.js.map +1 -0
- package/package.json +123 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Managed Agents (public beta since 2026-04-08) — a session
|
|
3
|
+
* confirmation responder, plus guidance for the two execution surfaces this
|
|
4
|
+
* adapter does NOT cover the same way.
|
|
5
|
+
*
|
|
6
|
+
* `@intutic/gate/managed-agents` — the TypeScript twin of the Python SDK's
|
|
7
|
+
* `intutic_clawde.gate.adapters.managed_agents`. See that module's doc for
|
|
8
|
+
* the full research trail; this header covers only what differs for the
|
|
9
|
+
* TypeScript SDK.
|
|
10
|
+
*
|
|
11
|
+
* Managed Agents is architecturally different from every other adapter in
|
|
12
|
+
* this package: the customer's process does not run the tool-call loop.
|
|
13
|
+
* Anthropic hosts a "session" that emits an event stream, and the customer's
|
|
14
|
+
* backend answers it. There is no local function call to wrap the way
|
|
15
|
+
* `wrapTools()` wraps a Mastra tool.
|
|
16
|
+
*
|
|
17
|
+
* ## The veto mechanism — CONFIRMED against a real install
|
|
18
|
+
*
|
|
19
|
+
* `@anthropic-ai/sdk@0.117.1` (the latest published release at the time this
|
|
20
|
+
* adapter was built) was installed as a devDependency of this package and its
|
|
21
|
+
* shipped `.d.ts` read directly (not inferred from docs alone; the Python
|
|
22
|
+
* twin was checked the same way against `anthropic==0.122.0`, its own
|
|
23
|
+
* latest):
|
|
24
|
+
*
|
|
25
|
+
* * `resources/beta/sessions/events.d.ts` — every request this module
|
|
26
|
+
* issues (`.list()`, `.send()`, `.stream()`) carries
|
|
27
|
+
* `anthropic-beta: managed-agents-2026-04-01` automatically (the real
|
|
28
|
+
* client sets it); {@link MANAGED_AGENTS_BETA} documents the value for a
|
|
29
|
+
* caller building a raw request.
|
|
30
|
+
* * `BetaManagedAgentsUserToolConfirmationEventParams` — the wire shape
|
|
31
|
+
* this module builds: `{ type: 'user.tool_confirmation', tool_use_id,
|
|
32
|
+
* result: 'allow' | 'deny', deny_message?, session_thread_id? }`.
|
|
33
|
+
* * `BetaManagedAgentsAgentToolUseEvent` and
|
|
34
|
+
* `BetaManagedAgentsAgentMCPToolUseEvent` both carry
|
|
35
|
+
* `evaluated_permission?: 'allow' | 'ask' | 'deny'`. A call the server
|
|
36
|
+
* evaluated to `'ask'` (e.g. because its `permission_policy` is
|
|
37
|
+
* `always_ask` — Anthropic's docs mark MCP toolsets as `always_ask` by
|
|
38
|
+
* default; the SDK types do not encode a default for built-in
|
|
39
|
+
* agent-toolset tools, so check current docs) PAUSES the session —
|
|
40
|
+
* Anthropic's own docs: "Denied tools do not run." A `'deny'` verdict
|
|
41
|
+
* means the SERVER already denied the call (no confirmation is expected
|
|
42
|
+
* or accepted); `'allow'` or unset means the call never paused at all.
|
|
43
|
+
* **This is the real, documented pre-execution veto this module answers
|
|
44
|
+
* — it is not observe-only.**
|
|
45
|
+
* * `lib/tools/SessionToolRunner.d.ts` is Anthropic's OWN reference
|
|
46
|
+
* dispatcher: it executes `agent.tool_use` / `agent.custom_tool_use`
|
|
47
|
+
* calls and posts results, but it does **not** decide `allow`/`deny` for
|
|
48
|
+
* an `ask`-gated call itself — it holds until *something* sends the
|
|
49
|
+
* matching `user.tool_confirmation` event. That "something" is what this
|
|
50
|
+
* module provides: {@link confirmationForEvent} /
|
|
51
|
+
* {@link IntuticSessionConfirmer} turn a paused `agent.tool_use` /
|
|
52
|
+
* `agent.mcp_tool_use` event into a `Gate.guard()`-driven verdict.
|
|
53
|
+
* * `lib/environments/worker.d.ts`'s `EnvironmentWorker` (the self-hosted
|
|
54
|
+
* sandbox driver) is built on `SessionToolRunner` for built-in
|
|
55
|
+
* agent-toolset tools (bash, edit, read, write, glob, grep, web_fetch,
|
|
56
|
+
* web_search). **Self-hosted built-in tools therefore pause and resolve
|
|
57
|
+
* through the IDENTICAL `agent.tool_use` / `evaluated_permission` /
|
|
58
|
+
* `user.tool_confirmation` mechanism as hosted ones** — this adapter
|
|
59
|
+
* covers them the same way. What differs for self-hosted is only WHERE
|
|
60
|
+
* the tool body executes (the customer's own worker process), not how
|
|
61
|
+
* the pre-execution veto works.
|
|
62
|
+
* * `agent.mcp_tool_use` is explicitly excluded from `SessionToolRunner`'s
|
|
63
|
+
* own dispatch (its own doc comment: "Server-side `agent.mcp_tool_use`
|
|
64
|
+
* calls are intentionally excluded — the runner does not handle them" —
|
|
65
|
+
* MCP tools run server-side at Anthropic). But the confirmation gate is a
|
|
66
|
+
* SEPARATE mechanism from execution: `BetaManagedAgentsAgentMCPToolUseEvent`
|
|
67
|
+
* carries `evaluated_permission` exactly like the built-in event does, so
|
|
68
|
+
* this module answers MCP pauses too, even though nothing in the official
|
|
69
|
+
* SDK dispatches their execution locally.
|
|
70
|
+
*
|
|
71
|
+
* ## What this module does NOT cover the same way — custom tools
|
|
72
|
+
*
|
|
73
|
+
* `BetaManagedAgentsAgentCustomToolUseEvent` has **no** `evaluated_permission`
|
|
74
|
+
* field and no `permission_policy` concept at all: a custom tool always
|
|
75
|
+
* executes wherever the client that owns its name is listening, with no
|
|
76
|
+
* pause. It is answered with `user.custom_tool_result`
|
|
77
|
+
* (`custom_tool_use_id` + `content` + `is_error`), not
|
|
78
|
+
* `user.tool_confirmation`. This is the "customer's own process runs the
|
|
79
|
+
* call" surface the phase brief anticipated — the natural integration point
|
|
80
|
+
* IS this package's `wrapTools()`/`wrapTool()`, exactly as the brief
|
|
81
|
+
* guessed. BUT verify the shape, because the generic helper does not apply
|
|
82
|
+
* as-is: `BetaRunnableTool` (`lib/tools/BetaRunnableTool.d.ts`, what
|
|
83
|
+
* `SessionToolRunner` dispatches against) exposes a `run(args, context)`
|
|
84
|
+
* method, not the `execute(input)` method `wrapTools()`'s object branch looks
|
|
85
|
+
* for (`wrapTools.ts` only recognises `.execute`) — handing it a
|
|
86
|
+
* `BetaRunnableTool` throws a `TypeError` rather than silently skipping
|
|
87
|
+
* governance, but it still means the generic helper needs a `.run`-aware
|
|
88
|
+
* sibling rather than "just works". {@link wrapManagedAgentsCustomTool} /
|
|
89
|
+
* {@link wrapManagedAgentsCustomTools} are that sibling — same throw-based
|
|
90
|
+
* refusal contract, applied to `.run` instead of `.execute`.
|
|
91
|
+
*
|
|
92
|
+
* ## What this module does NOT cover at all — the sandbox tool BODY
|
|
93
|
+
*
|
|
94
|
+
* The self-hosted `EnvironmentWorker` gates tool PAUSES exactly like the
|
|
95
|
+
* hosted path (see above) — but the tool's actual execution runs inside the
|
|
96
|
+
* customer's own worker process, outside this adapter's reach, same as every
|
|
97
|
+
* other Intutic adapter's posture toward a framework's built-in tool bodies
|
|
98
|
+
* (see openai.ts's module doc for the precedent). This module governs
|
|
99
|
+
* WHETHER the call runs; it does not — and cannot — inspect or modify what
|
|
100
|
+
* the tool body does once allowed.
|
|
101
|
+
*
|
|
102
|
+
* ## Webhook vs. streamed/polled delivery — both are real, verified live
|
|
103
|
+
*
|
|
104
|
+
* `resources/beta/webhooks.d.ts`: `client.beta.webhooks.unwrap(body, {
|
|
105
|
+
* headers, key })` verifies the HMAC signature and returns a typed event
|
|
106
|
+
* (synchronously — no network call); for a pause it is
|
|
107
|
+
* `session.requires_action`, which carries only `id` (the session id),
|
|
108
|
+
* `organization_id`, `workspace_id` — NOT the tool call itself. A webhook is
|
|
109
|
+
* therefore a NOTIFICATION to re-poll, not a self-contained payload —
|
|
110
|
+
* {@link IntuticSessionConfirmer.handleWebhook} does exactly that: re-lists
|
|
111
|
+
* the session's unanswered events and answers them. `events.stream()` /
|
|
112
|
+
* `events.list()` are the poll/stream alternative this module also supports
|
|
113
|
+
* directly via {@link IntuticSessionConfirmer.poll} /
|
|
114
|
+
* {@link IntuticSessionConfirmer.watch}.
|
|
115
|
+
*
|
|
116
|
+
* ## Fail-closed posture
|
|
117
|
+
*
|
|
118
|
+
* Matches every other adapter in this package: no gate configured, or an
|
|
119
|
+
* unexpected exception out of `Gate.guard()`, both produce a `'deny'` verdict
|
|
120
|
+
* — never a silently-allowed, unevaluated call. Only `IntuticGateRefusal` is
|
|
121
|
+
* distinguished (its own message becomes `deny_message`); anything else is
|
|
122
|
+
* wrapped in a generic "gate crashed" `deny_message`.
|
|
123
|
+
*
|
|
124
|
+
* ## What this module does NOT import
|
|
125
|
+
*
|
|
126
|
+
* Deliberately no runtime dependency on `@anthropic-ai/sdk` — the types below
|
|
127
|
+
* are narrow structural copies of only the shapes this adapter touches, same
|
|
128
|
+
* policy as openai.ts / mastra.ts / vercel.ts (see openai.ts's module doc for
|
|
129
|
+
* the full rationale). The real package is a devDependency used by
|
|
130
|
+
* `__tests__/managedAgents.test.ts` to type-check these structural types
|
|
131
|
+
* against the real shipped ones.
|
|
132
|
+
*
|
|
133
|
+
* @module
|
|
134
|
+
*/
|
|
135
|
+
import { active as activeGate } from './gate.js';
|
|
136
|
+
import { IntuticGateRefusal } from './errors.js';
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Structural copies of the @anthropic-ai/sdk shapes this adapter touches.
|
|
139
|
+
// Confirmed field-for-field against @anthropic-ai/sdk@0.117.1's shipped
|
|
140
|
+
// resources/beta/sessions/events.d.ts (see module doc). This package does
|
|
141
|
+
// not depend on the SDK at runtime, so the shapes are declared here rather
|
|
142
|
+
// than imported.
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
/** The beta header every Sessions-API request needs. The real SDK sets this
|
|
145
|
+
* on every `.list()`/`.send()`/`.stream()` call automatically — this
|
|
146
|
+
* constant exists for callers building their own request against the
|
|
147
|
+
* endpoint directly. */
|
|
148
|
+
export const MANAGED_AGENTS_BETA = 'managed-agents-2026-04-01';
|
|
149
|
+
const CONFIRMABLE_EVENT_TYPES = new Set(['agent.tool_use', 'agent.mcp_tool_use']);
|
|
150
|
+
const TERMINAL_EVENT_TYPES = new Set(['session.status_terminated', 'session.deleted']);
|
|
151
|
+
/** Read a field off any session event by key, regardless of which member of
|
|
152
|
+
* {@link ManagedAgentsSessionEventLike} it structurally is — e.g.
|
|
153
|
+
* `tool_use_id`, which only `user.tool_confirmation` events carry. Mirrors
|
|
154
|
+
* the Python adapter's `_field` helper. */
|
|
155
|
+
function eventField(event, key) {
|
|
156
|
+
return event[key];
|
|
157
|
+
}
|
|
158
|
+
function resolveGate(explicit, what) {
|
|
159
|
+
const g = explicit ?? activeGate();
|
|
160
|
+
if (g === null) {
|
|
161
|
+
throw new Error('No gate configured: call install(new Gate(...)) from @intutic/gate, or pass ' +
|
|
162
|
+
`{ gate } to ${what}. Refusing to run the tool unguarded.`);
|
|
163
|
+
}
|
|
164
|
+
return g;
|
|
165
|
+
}
|
|
166
|
+
function crashMessage(err) {
|
|
167
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
168
|
+
return `[Intutic Governance] BLOCKED: gate crashed (${detail}) — failing closed rather than allowing an unevaluated call.`;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Build the `user.tool_confirmation` params answering `event`, or `null` if
|
|
172
|
+
* `event` needs no answer from this module.
|
|
173
|
+
*
|
|
174
|
+
* `null` covers three cases, all correct to send nothing for: the event
|
|
175
|
+
* isn't `agent.tool_use`/`agent.mcp_tool_use` (including
|
|
176
|
+
* `agent.custom_tool_use` — see the module doc); its `evaluated_permission`
|
|
177
|
+
* is `'allow'` or unset (the call never paused); or it is already `'deny'`
|
|
178
|
+
* (the SERVER already resolved it — sending a confirmation for an id the
|
|
179
|
+
* server considers closed would just error).
|
|
180
|
+
*
|
|
181
|
+
* Anything else — `'ask'`, or a permission value this module doesn't
|
|
182
|
+
* recognise — is treated as a pause needing a verdict, matching
|
|
183
|
+
* `SessionToolRunner`'s own fail-closed handling of unrecognised wire values
|
|
184
|
+
* (hold/deny rather than dispatch).
|
|
185
|
+
*
|
|
186
|
+
* ASYNC: `Gate.guard()` is async in this package (unlike the Python SDK's
|
|
187
|
+
* synchronous twin) — every caller must `await` this.
|
|
188
|
+
*/
|
|
189
|
+
export async function confirmationForEvent(event, opts = {}) {
|
|
190
|
+
if (!CONFIRMABLE_EVENT_TYPES.has(event.type))
|
|
191
|
+
return null;
|
|
192
|
+
const typed = event;
|
|
193
|
+
const permission = typed.evaluated_permission ?? null;
|
|
194
|
+
if (permission === null || permission === 'allow' || permission === 'deny')
|
|
195
|
+
return null;
|
|
196
|
+
const eventId = typed.id;
|
|
197
|
+
if (!eventId)
|
|
198
|
+
return null;
|
|
199
|
+
const toolName = typed.name || 'tool';
|
|
200
|
+
const toolInput = { ...typed.input };
|
|
201
|
+
if (typed.type === 'agent.mcp_tool_use' && typed.mcp_server_name) {
|
|
202
|
+
if (!('mcp_server_name' in toolInput)) {
|
|
203
|
+
toolInput.mcp_server_name = typed.mcp_server_name;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const result = {
|
|
207
|
+
type: 'user.tool_confirmation',
|
|
208
|
+
tool_use_id: eventId,
|
|
209
|
+
result: 'allow',
|
|
210
|
+
};
|
|
211
|
+
if (typed.session_thread_id) {
|
|
212
|
+
result.session_thread_id = typed.session_thread_id;
|
|
213
|
+
}
|
|
214
|
+
let g;
|
|
215
|
+
try {
|
|
216
|
+
g = resolveGate(opts.gate, 'confirmationForEvent()');
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
result.result = 'deny';
|
|
220
|
+
result.deny_message = err instanceof Error ? `[Intutic Governance] BLOCKED: ${err.message}` : String(err);
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
await g.guard(toolName, toolInput);
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
result.result = 'deny';
|
|
228
|
+
result.deny_message = err instanceof IntuticGateRefusal ? err.message : crashMessage(err);
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
class IdleTimeout extends Error {
|
|
233
|
+
constructor() { super('watch(): stream idle'); }
|
|
234
|
+
}
|
|
235
|
+
/** 400–499 except 408 (timeout) and 429 (rate limit): a retry cannot help. */
|
|
236
|
+
export function isFatal4xx(err) {
|
|
237
|
+
const status = err?.status;
|
|
238
|
+
return typeof status === 'number' && status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
239
|
+
}
|
|
240
|
+
function sleep(ms, signal) {
|
|
241
|
+
return new Promise((resolve) => {
|
|
242
|
+
const t = setTimeout(done, ms);
|
|
243
|
+
function done() { signal?.removeEventListener('abort', done); resolve(); }
|
|
244
|
+
signal?.addEventListener('abort', done, { once: true });
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/** Yields the stream's events; throws IdleTimeout when none arrives within `ms`. */
|
|
248
|
+
async function* withIdleTimeout(stream, ms, signal) {
|
|
249
|
+
const it = stream[Symbol.asyncIterator]();
|
|
250
|
+
try {
|
|
251
|
+
while (true) {
|
|
252
|
+
if (signal?.aborted)
|
|
253
|
+
return;
|
|
254
|
+
const next = it.next();
|
|
255
|
+
const result = ms === undefined
|
|
256
|
+
? await next
|
|
257
|
+
: await Promise.race([
|
|
258
|
+
next,
|
|
259
|
+
new Promise((_, reject) => { const t = setTimeout(() => reject(new IdleTimeout()), ms); void next.finally(() => clearTimeout(t)); }),
|
|
260
|
+
]);
|
|
261
|
+
if (result.done)
|
|
262
|
+
return;
|
|
263
|
+
yield result.value;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
// Not awaited: after an idle timeout the generator is still suspended
|
|
268
|
+
// inside its own pending `next()`, and `return()` queues behind it — an
|
|
269
|
+
// await here would hang exactly as long as the dead stream does.
|
|
270
|
+
void Promise.resolve(it.return?.()).catch(() => undefined);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
export class IntuticSessionConfirmer {
|
|
274
|
+
sessionId;
|
|
275
|
+
client;
|
|
276
|
+
gate;
|
|
277
|
+
/** Tool-call event ids already answered (by this instance), so a `poll()`
|
|
278
|
+
* that overlaps a live `watch()` — or a re-delivered webhook — never
|
|
279
|
+
* double-sends a confirmation for the same id. */
|
|
280
|
+
answered = new Set();
|
|
281
|
+
constructor(client, sessionId, opts = {}) {
|
|
282
|
+
this.client = client;
|
|
283
|
+
this.sessionId = sessionId;
|
|
284
|
+
this.gate = opts.gate;
|
|
285
|
+
}
|
|
286
|
+
noteConfirmationEvent(event) {
|
|
287
|
+
const toolUseId = eventField(event, 'tool_use_id');
|
|
288
|
+
if (typeof toolUseId === 'string' && toolUseId)
|
|
289
|
+
this.answered.add(toolUseId);
|
|
290
|
+
}
|
|
291
|
+
/** Answer one event if it needs an answer; returns the sent params, or
|
|
292
|
+
* `null` if nothing was sent (already answered, or
|
|
293
|
+
* {@link confirmationForEvent} returned `null` — see its doc). */
|
|
294
|
+
async handleEvent(event) {
|
|
295
|
+
const eventId = event.id;
|
|
296
|
+
if (!eventId || this.answered.has(eventId))
|
|
297
|
+
return null;
|
|
298
|
+
const confirmation = await confirmationForEvent(event, { gate: this.gate });
|
|
299
|
+
if (confirmation === null)
|
|
300
|
+
return null;
|
|
301
|
+
await this.client.beta.sessions.events.send(this.sessionId, { events: [confirmation] });
|
|
302
|
+
this.answered.add(eventId);
|
|
303
|
+
return confirmation;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* List recent events and answer every unanswered
|
|
307
|
+
* `agent.tool_use`/`agent.mcp_tool_use` pause found. Returns the sent
|
|
308
|
+
* confirmations, in event order.
|
|
309
|
+
*/
|
|
310
|
+
async poll(opts = {}) {
|
|
311
|
+
const sent = [];
|
|
312
|
+
const events = this.client.beta.sessions.events.list(this.sessionId, {
|
|
313
|
+
limit: opts.limit ?? 1000,
|
|
314
|
+
types: ['agent.tool_use', 'agent.mcp_tool_use', 'user.tool_confirmation'],
|
|
315
|
+
});
|
|
316
|
+
for await (const event of events) {
|
|
317
|
+
if (event.type === 'user.tool_confirmation') {
|
|
318
|
+
this.noteConfirmationEvent(event);
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
const confirmation = await this.handleEvent(event);
|
|
322
|
+
if (confirmation !== null)
|
|
323
|
+
sent.push(confirmation);
|
|
324
|
+
}
|
|
325
|
+
return sent;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Answer the pause a `session.requires_action` webhook signalled.
|
|
329
|
+
*
|
|
330
|
+
* `unwrapped` is what `client.beta.webhooks.unwrap(rawBody, { headers })`
|
|
331
|
+
* returns (or its `.data`, or a plain object of the same shape). Anything
|
|
332
|
+
* other than a `session.requires_action` payload is a no-op (`[]`): this
|
|
333
|
+
* module only ever answers tool-confirmation pauses, so every other
|
|
334
|
+
* webhook event type is intentionally ignored here.
|
|
335
|
+
*
|
|
336
|
+
* The webhook payload carries only the session id — it does NOT carry the
|
|
337
|
+
* tool call itself — so this always falls through to a {@link poll}.
|
|
338
|
+
*/
|
|
339
|
+
async handleWebhook(unwrapped) {
|
|
340
|
+
const data = unwrapped.data ?? unwrapped;
|
|
341
|
+
if (data.type !== 'session.requires_action')
|
|
342
|
+
return [];
|
|
343
|
+
if (data.id !== undefined && data.id !== this.sessionId) {
|
|
344
|
+
throw new Error(`webhook session id ${JSON.stringify(data.id)} does not match this confirmer's ` +
|
|
345
|
+
`sessionId ${JSON.stringify(this.sessionId)}; construct a confirmer per session ` +
|
|
346
|
+
'rather than reusing one across sessions');
|
|
347
|
+
}
|
|
348
|
+
return this.poll();
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Catch up on anything already pending, then follow the live event
|
|
352
|
+
* stream, yielding each confirmation as it is sent, reconnecting when the
|
|
353
|
+
* stream drops (TD-428).
|
|
354
|
+
*
|
|
355
|
+
* Modelled on the SDK's own `SessionToolRunner#streamLoop`: open the
|
|
356
|
+
* stream FIRST, then reconcile with `poll()` — an event emitted in the gap
|
|
357
|
+
* is buffered on the stream rather than lost, and `answered` dedups
|
|
358
|
+
* anything that shows up on both. On end or error the loop sleeps
|
|
359
|
+
* (backoff doubling from `backoffStartMs` to `backoffCapMs`, reset on any
|
|
360
|
+
* received event) and reopens. A fatal 4xx (anything 400–499 other than
|
|
361
|
+
* 408 and 429) rethrows: the session is gone or the key is wrong, and
|
|
362
|
+
* retrying would loop forever. `idleTimeoutMs` drops a stream that has
|
|
363
|
+
* gone silent and reopens it, which re-polls. Ends only on a terminal
|
|
364
|
+
* event, `signal` abort, or `maxReconnects` exhausted (unbounded by
|
|
365
|
+
* default, the SDK's own choice).
|
|
366
|
+
*/
|
|
367
|
+
async *watch(opts = {}) {
|
|
368
|
+
const backoffStart = opts.backoffStartMs ?? 500;
|
|
369
|
+
const backoffCap = opts.backoffCapMs ?? 10_000;
|
|
370
|
+
const maxReconnects = opts.maxReconnects ?? Number.POSITIVE_INFINITY;
|
|
371
|
+
let backoff = backoffStart;
|
|
372
|
+
let reconnects = 0;
|
|
373
|
+
while (!opts.signal?.aborted) {
|
|
374
|
+
let terminal = false;
|
|
375
|
+
try {
|
|
376
|
+
const stream = await this.client.beta.sessions.events.stream(this.sessionId);
|
|
377
|
+
for (const confirmation of await this.poll())
|
|
378
|
+
yield confirmation;
|
|
379
|
+
for await (const event of withIdleTimeout(stream, opts.idleTimeoutMs, opts.signal)) {
|
|
380
|
+
backoff = backoffStart;
|
|
381
|
+
if (TERMINAL_EVENT_TYPES.has(event.type)) {
|
|
382
|
+
terminal = true;
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
if (event.type === 'user.tool_confirmation') {
|
|
386
|
+
this.noteConfirmationEvent(event);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const confirmation = await this.handleEvent(event);
|
|
390
|
+
if (confirmation)
|
|
391
|
+
yield confirmation;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
if (opts.signal?.aborted)
|
|
396
|
+
return;
|
|
397
|
+
if (err instanceof IdleTimeout) {
|
|
398
|
+
// fall through: reconnect and re-poll
|
|
399
|
+
}
|
|
400
|
+
else if (isFatal4xx(err)) {
|
|
401
|
+
throw err;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (terminal || opts.signal?.aborted)
|
|
405
|
+
return;
|
|
406
|
+
if (reconnects >= maxReconnects) {
|
|
407
|
+
throw new Error(`watch(): stream ended ${reconnects + 1} time(s) and maxReconnects (${maxReconnects}) is exhausted`);
|
|
408
|
+
}
|
|
409
|
+
reconnects += 1;
|
|
410
|
+
await sleep(backoff, opts.signal);
|
|
411
|
+
backoff = Math.min(backoff * 2, backoffCap);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const CUSTOM_TOOL_GUARDED = Symbol.for('intutic.gate.managedAgents.customToolGuarded');
|
|
416
|
+
/**
|
|
417
|
+
* Wrap one Managed Agents custom tool (`BetaRunnableTool` shape) so
|
|
418
|
+
* `Gate.guard()` runs before `run()` executes.
|
|
419
|
+
*
|
|
420
|
+
* Returns a NEW object (a shallow copy with `run` replaced); does not mutate
|
|
421
|
+
* the one passed in. On refusal the wrapped `run()` THROWS
|
|
422
|
+
* `IntuticGateRefusal` before the real implementation ever runs — the same
|
|
423
|
+
* throw-based contract `wrapTool()` uses. Already-wrapped tools pass through
|
|
424
|
+
* unchanged.
|
|
425
|
+
*/
|
|
426
|
+
export function wrapManagedAgentsCustomTool(tool, opts = {}) {
|
|
427
|
+
const marked = tool;
|
|
428
|
+
if (marked[CUSTOM_TOOL_GUARDED] === true)
|
|
429
|
+
return tool;
|
|
430
|
+
const originalRun = tool.run;
|
|
431
|
+
const toolName = tool.name || 'tool';
|
|
432
|
+
const guardedRun = async (args, context) => {
|
|
433
|
+
const g = resolveGate(opts.gate, 'wrapManagedAgentsCustomTool()');
|
|
434
|
+
await g.guard(toolName, args ?? {});
|
|
435
|
+
return originalRun.call(tool, args, context);
|
|
436
|
+
};
|
|
437
|
+
const wrapped = { ...tool, run: guardedRun };
|
|
438
|
+
Object.defineProperty(wrapped, CUSTOM_TOOL_GUARDED, { value: true, enumerable: false });
|
|
439
|
+
return wrapped;
|
|
440
|
+
}
|
|
441
|
+
/** Wrap a collection of Managed Agents custom tools in one call — see
|
|
442
|
+
* {@link wrapManagedAgentsCustomTool}. */
|
|
443
|
+
export function wrapManagedAgentsCustomTools(tools, opts = {}) {
|
|
444
|
+
return tools.map((t) => wrapManagedAgentsCustomTool(t, opts));
|
|
445
|
+
}
|
|
446
|
+
//# sourceMappingURL=managedAgents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"managedAgents.js","sourceRoot":"","sources":["../src/managedAgents.ts"],"names":[],"mappings":"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"}
|
package/dist/mastra.d.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastra adapter: `intuticHooks()`.
|
|
3
|
+
*
|
|
4
|
+
* `@intutic/gate/mastra` — a framework-specific specialization of the same
|
|
5
|
+
* idea `wrapTools.ts` implements generically (see that file's module doc):
|
|
6
|
+
* wrap the framework's own veto point around this package's `Gate.guard()`,
|
|
7
|
+
* throwing/vetoing before the real tool body ever runs.
|
|
8
|
+
*
|
|
9
|
+
* Mastra (`@mastra/core`) exposes a documented pre-execution veto point: an
|
|
10
|
+
* `Agent`'s `hooks.beforeToolCall`. It runs for every tool in the agent's
|
|
11
|
+
* assembled tool dictionary — natively defined tools AND MCP-sourced tools
|
|
12
|
+
* registered through `@mastra/mcp`, since both are merged into the same
|
|
13
|
+
* `CoreTool` record before hook-wrapping happens (`wrapToolsWithHooks`
|
|
14
|
+
* operates generically over `Object.entries(tools)`, with no source-specific
|
|
15
|
+
* branch). Returning `{ proceed: false, output }` from `beforeToolCall` skips
|
|
16
|
+
* the tool's real `execute` entirely and hands `output` back to the model IN
|
|
17
|
+
* PLACE OF the tool's result.
|
|
18
|
+
*
|
|
19
|
+
* ## Confirmed against a real install
|
|
20
|
+
*
|
|
21
|
+
* `@mastra/core@1.59.0` was installed in a scratch directory and its shipped
|
|
22
|
+
* `.d.ts`/compiled source read directly (not inferred from docs alone):
|
|
23
|
+
*
|
|
24
|
+
* `dist/tools/types.d.ts`:
|
|
25
|
+
* ```ts
|
|
26
|
+
* export interface ToolHookContext<TInput, TContext, TMetadata> {
|
|
27
|
+
* toolName: string
|
|
28
|
+
* input: TInput
|
|
29
|
+
* context: TContext
|
|
30
|
+
* metadata?: TMetadata
|
|
31
|
+
* }
|
|
32
|
+
* export interface ToolBeforeHookResult<TOutput> {
|
|
33
|
+
* proceed: false
|
|
34
|
+
* output: TOutput
|
|
35
|
+
* }
|
|
36
|
+
* export interface ToolHooks<TInput, TOutput, TContext, TMetadata> {
|
|
37
|
+
* beforeToolCall?: (context: ToolHookContext<TInput, TContext, TMetadata>) =>
|
|
38
|
+
* void | ToolBeforeHookResult<TOutput> | Promise<void | ToolBeforeHookResult<TOutput>>
|
|
39
|
+
* afterToolCall?: (context: ToolAfterHookContext<...>) => void | Promise<void>
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
* `void`/`undefined` means "proceed" (allow); only an explicit
|
|
43
|
+
* `{ proceed: false, output }` vetoes — this adapter's `beforeToolCall`
|
|
44
|
+
* therefore returns `undefined` on allow, matching the framework's own
|
|
45
|
+
* "no opinion" contract rather than a synthetic allow value.
|
|
46
|
+
*
|
|
47
|
+
* `dist/agent-CKAVuxKN.js`'s `wrapToolWithHooks` (the actual wrapping the
|
|
48
|
+
* `.d.ts` above only declares the shape of) confirms the consumption side:
|
|
49
|
+
* ```js
|
|
50
|
+
* const beforeResult = await hooks.beforeToolCall?.(hookContext)
|
|
51
|
+
* if (beforeResult?.proceed === false) return beforeResult.output
|
|
52
|
+
* ```
|
|
53
|
+
* `output` is returned AS the tool's own `execute` return value at this
|
|
54
|
+
* layer, with no schema check performed by the hook wrapper itself — but a
|
|
55
|
+
* tool with a strict Zod `outputSchema` may still validate/reject that value
|
|
56
|
+
* further downstream, and `beforeToolCall` is never told what that schema
|
|
57
|
+
* is (the hook context carries no `outputSchema`/tool-definition reference).
|
|
58
|
+
* This adapter therefore cannot GUARANTEE a schema-conformant denial payload
|
|
59
|
+
* for an arbitrary tool; see {@link IntuticHooksOptions.denialOutput} to
|
|
60
|
+
* shape it per your own tools when the generic default will not validate.
|
|
61
|
+
*
|
|
62
|
+
* ## TAMPER NOTE — per-call hooks OVERRIDE agent-level hooks, they do not merge
|
|
63
|
+
*
|
|
64
|
+
* Also confirmed against the same install — `dist/agent.d.ts`'s
|
|
65
|
+
* `getConfiguredToolHooks()` doc comment:
|
|
66
|
+
*
|
|
67
|
+
* > Run-level hooks override these via {@link resolveToolHooks}, so callers
|
|
68
|
+
* > that need to preserve the configured hooks must read and compose them
|
|
69
|
+
* > explicitly.
|
|
70
|
+
*
|
|
71
|
+
* Concretely: `new Agent({ hooks: intuticHooks() })` installs this gate at
|
|
72
|
+
* the agent level, but ANY caller of `.generate()`/`.stream()` who passes
|
|
73
|
+
* their own `hooks` option — even `{}`, or a `hooks` object with no
|
|
74
|
+
* `beforeToolCall` — REPLACES the agent-level hooks wholesale for that call.
|
|
75
|
+
* This gate silently does not run: no error, no warning, just an unguarded
|
|
76
|
+
* tool call. This is Mastra's own documented behaviour, not a defect this
|
|
77
|
+
* package can work around from outside the framework — there is no hook
|
|
78
|
+
* inheritance or merge to opt into. See `gateRegistry.ts`'s `mastra` row for
|
|
79
|
+
* the same note surfaced to the registry, and
|
|
80
|
+
* `apps/docs/integrations/mastra.md` for the operator-facing version.
|
|
81
|
+
*
|
|
82
|
+
* If your application passes call-level `hooks` anywhere, compose this
|
|
83
|
+
* gate's `beforeToolCall` into that call's hooks yourself:
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* const intutic = intuticHooks()
|
|
87
|
+
* await agent.generate(prompt, {
|
|
88
|
+
* hooks: {
|
|
89
|
+
* beforeToolCall: async (ctx) => {
|
|
90
|
+
* const denied = await intutic.beforeToolCall(ctx)
|
|
91
|
+
* if (denied) return denied
|
|
92
|
+
* return myOwnBeforeToolCall(ctx)
|
|
93
|
+
* },
|
|
94
|
+
* },
|
|
95
|
+
* })
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
import { type Gate } from './gate.js';
|
|
99
|
+
import { IntuticGateRefusal } from './errors.js';
|
|
100
|
+
/** Structural copy of `@mastra/core`'s `ToolHookContext` — this package does
|
|
101
|
+
* not depend on `@mastra/core` at runtime, so the shape is declared here
|
|
102
|
+
* rather than imported. Confirmed field-for-field against
|
|
103
|
+
* `@mastra/core@1.59.0`'s `dist/tools/types.d.ts` (see module doc). */
|
|
104
|
+
export interface MastraToolHookContext {
|
|
105
|
+
/** The name exposed to the model for this tool call. */
|
|
106
|
+
toolName: string;
|
|
107
|
+
/** Input passed to the tool — this is what `Gate.guard()` evaluates. */
|
|
108
|
+
input: unknown;
|
|
109
|
+
/** Execution context passed to the tool. Not read by this adapter. */
|
|
110
|
+
context?: unknown;
|
|
111
|
+
/** Optional adapter-specific metadata (e.g. `agentId`/`agentName`). */
|
|
112
|
+
metadata?: Record<string, unknown>;
|
|
113
|
+
}
|
|
114
|
+
/** Structural copy of `@mastra/core`'s `ToolBeforeHookResult`. */
|
|
115
|
+
export interface MastraToolBeforeHookResult {
|
|
116
|
+
proceed: false;
|
|
117
|
+
output: unknown;
|
|
118
|
+
}
|
|
119
|
+
/** Structural copy of `@mastra/core`'s `ToolHooks['beforeToolCall']` — the
|
|
120
|
+
* slice of the real type this adapter implements. Assignable directly to
|
|
121
|
+
* `Agent`'s `hooks` constructor option. */
|
|
122
|
+
export type MastraBeforeToolCall = (context: MastraToolHookContext) => Promise<void | MastraToolBeforeHookResult>;
|
|
123
|
+
export interface IntuticHooksOptions {
|
|
124
|
+
/** Overrides the process-wide installed gate (`install(new Gate(...))`). */
|
|
125
|
+
gate?: Gate;
|
|
126
|
+
/**
|
|
127
|
+
* Shapes the `output` handed back to the model on a refusal. The default
|
|
128
|
+
* produces `{ error: true, message, code, incidentId? }` — reasonable for
|
|
129
|
+
* an unstructured/text-ish tool output, but NOT guaranteed to satisfy an
|
|
130
|
+
* arbitrary tool's own Zod `outputSchema` (this hook is never told what
|
|
131
|
+
* that schema is — see the module doc's "Confirmed against a real
|
|
132
|
+
* install" section). Supply this when your tools declare strict output
|
|
133
|
+
* schemas the default shape would fail.
|
|
134
|
+
*/
|
|
135
|
+
denialOutput?: (ctx: {
|
|
136
|
+
toolName: string;
|
|
137
|
+
refusal: IntuticGateRefusal;
|
|
138
|
+
}) => unknown;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Builds a `{ beforeToolCall }` object shaped for direct use in `@mastra/core`'s
|
|
142
|
+
* `Agent` constructor's `hooks` option:
|
|
143
|
+
*
|
|
144
|
+
* ```ts
|
|
145
|
+
* import { Agent } from '@mastra/core/agent'
|
|
146
|
+
* import { Gate, install } from '@intutic/gate'
|
|
147
|
+
* import { intuticHooks } from '@intutic/gate/mastra'
|
|
148
|
+
*
|
|
149
|
+
* install(new Gate({ workspaceId: process.env.INTUTIC_WORKSPACE_ID }))
|
|
150
|
+
*
|
|
151
|
+
* const agent = new Agent({
|
|
152
|
+
* id: 'my-agent',
|
|
153
|
+
* name: 'My Agent',
|
|
154
|
+
* model: 'openai/gpt-5-mini',
|
|
155
|
+
* tools: { deleteTool },
|
|
156
|
+
* hooks: intuticHooks(),
|
|
157
|
+
* })
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* On refusal, returns `{ proceed: false, output }` — the real tool body
|
|
161
|
+
* never runs. On allow, returns `undefined`, Mastra's own "no opinion"
|
|
162
|
+
* value, so the real `execute` runs untouched.
|
|
163
|
+
*
|
|
164
|
+
* Requires a `Gate` — either passed via `{ gate }` here, or installed
|
|
165
|
+
* process-wide with `install(new Gate(...))`. A call with neither throws
|
|
166
|
+
* (via the underlying `beforeToolCall`), refusing to run tools unguarded
|
|
167
|
+
* rather than silently skipping enforcement — same posture as `wrapTool`.
|
|
168
|
+
*
|
|
169
|
+
* See the module doc's TAMPER NOTE: this only governs calls that do not
|
|
170
|
+
* override `hooks` at the `.generate()`/`.stream()` call site.
|
|
171
|
+
*/
|
|
172
|
+
export declare function intuticHooks(opts?: IntuticHooksOptions): {
|
|
173
|
+
beforeToolCall: MastraBeforeToolCall;
|
|
174
|
+
};
|
|
175
|
+
//# sourceMappingURL=mastra.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mastra.d.ts","sourceRoot":"","sources":["../src/mastra.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgGG;AAEH,OAAO,EAAwB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEhD;;;wEAGwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAA;IAChB,wEAAwE;IACxE,KAAK,EAAE,OAAO,CAAA;IACd,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,kEAAkE;AAClE,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,KAAK,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;4CAE4C;AAC5C,MAAM,MAAM,oBAAoB,GAAG,CACjC,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC,IAAI,GAAG,0BAA0B,CAAC,CAAA;AAE/C,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,IAAI,CAAA;IACX;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,kBAAkB,CAAA;KAAE,KAAK,OAAO,CAAA;CACnF;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG;IAAE,cAAc,EAAE,oBAAoB,CAAA;CAAE,CAuBrG"}
|