@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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +261 -0
  3. package/dist/actions.d.ts +86 -0
  4. package/dist/actions.d.ts.map +1 -0
  5. package/dist/actions.js +239 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/client.d.ts +94 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +207 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/dsh.d.ts +162 -0
  12. package/dist/dsh.d.ts.map +1 -0
  13. package/dist/dsh.js +189 -0
  14. package/dist/dsh.js.map +1 -0
  15. package/dist/errors.d.ts +41 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +53 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/eve.d.ts +425 -0
  20. package/dist/eve.d.ts.map +1 -0
  21. package/dist/eve.js +378 -0
  22. package/dist/eve.js.map +1 -0
  23. package/dist/gate.d.ts +113 -0
  24. package/dist/gate.d.ts.map +1 -0
  25. package/dist/gate.js +274 -0
  26. package/dist/gate.js.map +1 -0
  27. package/dist/harness.d.ts +482 -0
  28. package/dist/harness.d.ts.map +1 -0
  29. package/dist/harness.js +504 -0
  30. package/dist/harness.js.map +1 -0
  31. package/dist/headers.d.ts +26 -0
  32. package/dist/headers.d.ts.map +1 -0
  33. package/dist/headers.js +30 -0
  34. package/dist/headers.js.map +1 -0
  35. package/dist/imagecheck.d.ts +93 -0
  36. package/dist/imagecheck.d.ts.map +1 -0
  37. package/dist/imagecheck.js +286 -0
  38. package/dist/imagecheck.js.map +1 -0
  39. package/dist/index.d.ts +26 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +19 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/managedAgents.d.ts +375 -0
  44. package/dist/managedAgents.d.ts.map +1 -0
  45. package/dist/managedAgents.js +446 -0
  46. package/dist/managedAgents.js.map +1 -0
  47. package/dist/mastra.d.ts +175 -0
  48. package/dist/mastra.d.ts.map +1 -0
  49. package/dist/mastra.js +161 -0
  50. package/dist/mastra.js.map +1 -0
  51. package/dist/openai.d.ts +392 -0
  52. package/dist/openai.d.ts.map +1 -0
  53. package/dist/openai.js +658 -0
  54. package/dist/openai.js.map +1 -0
  55. package/dist/shlex.d.ts +16 -0
  56. package/dist/shlex.d.ts.map +1 -0
  57. package/dist/shlex.js +78 -0
  58. package/dist/shlex.js.map +1 -0
  59. package/dist/snapshot.d.ts +111 -0
  60. package/dist/snapshot.d.ts.map +1 -0
  61. package/dist/snapshot.js +243 -0
  62. package/dist/snapshot.js.map +1 -0
  63. package/dist/soprules.d.ts +88 -0
  64. package/dist/soprules.d.ts.map +1 -0
  65. package/dist/soprules.js +176 -0
  66. package/dist/soprules.js.map +1 -0
  67. package/dist/trueforge.d.ts +191 -0
  68. package/dist/trueforge.d.ts.map +1 -0
  69. package/dist/trueforge.js +184 -0
  70. package/dist/trueforge.js.map +1 -0
  71. package/dist/vercel.d.ts +174 -0
  72. package/dist/vercel.d.ts.map +1 -0
  73. package/dist/vercel.js +157 -0
  74. package/dist/vercel.js.map +1 -0
  75. package/dist/workflow.d.ts +209 -0
  76. package/dist/workflow.d.ts.map +1 -0
  77. package/dist/workflow.js +254 -0
  78. package/dist/workflow.js.map +1 -0
  79. package/dist/wrapTools.d.ts +68 -0
  80. package/dist/wrapTools.d.ts.map +1 -0
  81. package/dist/wrapTools.js +95 -0
  82. package/dist/wrapTools.js.map +1 -0
  83. package/package.json +123 -0
package/dist/eve.js ADDED
@@ -0,0 +1,378 @@
1
+ /**
2
+ * eve adapter: `intuticApproval()`, `intuticConnectionApproval()`,
3
+ * `intuticAuditHooks()`.
4
+ *
5
+ * `@intutic/gate/eve` — a framework-specific specialization of the same idea
6
+ * `wrapTools.ts` implements generically (see that file's module doc): wrap the
7
+ * framework's own veto point around this package's `Gate.guard()`.
8
+ *
9
+ * eve (npm `eve`, github.com/vercel/eve) is Vercel's filesystem-first
10
+ * framework for durable backend AI agents: an agent is a directory (`agent/`
11
+ * with `instructions.md`, `agent/tools/*.ts`, `agent/connections/*.ts`,
12
+ * `agent/hooks/*.ts`, ...) that eve builds by walking the tree. It is a
13
+ * pre-1.0 PREVIEW product (0.39.1 at the time of writing, very active) —
14
+ * see docs/TECH_DEBT.md TD-410 for the churn shield this adapter carries,
15
+ * the same posture the dsh integration established.
16
+ *
17
+ * ## The veto mechanism — CONFIRMED against a real install, not assumed
18
+ *
19
+ * `eve@0.39.1` is installed as a devDependency of this package and its
20
+ * shipped `.d.ts` files were read directly (not inferred from docs alone).
21
+ * eve's per-tool veto point is the `approval` property on a tool definition
22
+ * (`dist/src/public/definitions/tool.d.ts`), and the same `Approval` type is
23
+ * accepted per-connection by `defineMcpClientConnection` /
24
+ * `defineOpenAPIConnection` (`dist/src/public/definitions/connections/
25
+ * {mcp,openapi}.d.ts`), gating ALL of that connection's tools. There is NO
26
+ * agent-level default approval field (`agent.d.ts` carries zero approval
27
+ * fields — verified) — attaching a shared policy per tool/connection is
28
+ * eve's own documented multi-tenant pattern
29
+ * (`docs/patterns/multi-tenant-approvals.md`), and exactly the shape this
30
+ * adapter exports.
31
+ *
32
+ * The confirmed policy contract (`dist/src/public/definitions/approval.d.ts`):
33
+ *
34
+ * ```ts
35
+ * export interface ApprovalContext<TInput = Record<string, unknown>> extends SessionContext {
36
+ * readonly approvedTools: ReadonlySet<string>
37
+ * readonly callId: string
38
+ * readonly toolInput?: ApprovalToolInput<TInput> // Readonly<TInput> | TInput; CAN be undefined
39
+ * readonly toolName: string
40
+ * }
41
+ * export type ApprovalStatus = undefined | boolean
42
+ * | 'not-applicable' | 'approved' | 'denied' | 'user-approval'
43
+ * | { readonly type: 'not-applicable'; readonly reason?: never }
44
+ * | { readonly type: 'approved'; readonly reason?: string }
45
+ * | { readonly type: 'denied'; readonly reason?: string }
46
+ * | { readonly type: 'user-approval'; readonly reason?: never }
47
+ * export type ApprovalPolicy<TInput = Record<string, unknown>> =
48
+ * (ctx: ApprovalContext<TInput>) => ApprovalStatus | Promise<ApprovalStatus>
49
+ * ```
50
+ *
51
+ * These are AI SDK 7 approval statuses (eve is built on `ai` v7 — the same
52
+ * family `vercel.ts` documents). `'denied'` / `{ type: 'denied', reason }`
53
+ * vetoes the call before execution; `reason` reaches the model in place of a
54
+ * tool result. `'not-applicable'` (and `undefined`) means "continue without a
55
+ * prompt"; `'user-approval'` parks the run durably for a human. The boolean
56
+ * forms are back-compat for eve's earlier predicate shape: `true` is treated
57
+ * as `'user-approval'` and `false` as `'not-applicable'` (confirmed in
58
+ * `docs/tools/human-in-the-loop.md`) — this adapter never RETURNS booleans,
59
+ * but its structural {@link EveApprovalStatus} copy includes them so the type
60
+ * stays field-for-field faithful to the real union.
61
+ *
62
+ * ## Allow-shape: why `'not-applicable'` and not `'approved'`
63
+ *
64
+ * Unlike the Vercel AI SDK's `toolApproval` (a separate, whole-run config
65
+ * that coexists with per-tool mechanisms), eve's `approval` IS the tool's one
66
+ * approval policy — whatever this function returns is the whole decision for
67
+ * that call. On allow this adapter returns `'not-applicable'` by default:
68
+ * eve's own "no prompt needed, continue" value, matching the semantics of an
69
+ * omitted `approval` (`never()`). It deliberately does NOT return
70
+ * `'approved'`: that value asserts an affirmative approval decision this gate
71
+ * did not make (Intutic's allow is "no objection", not "signed off"), and its
72
+ * interaction with eve's `approvedTools` bookkeeping (`once()` keys off that
73
+ * set) is not something this adapter verified. Callers who want eve's human
74
+ * flow ON TOP of the governance gate set `{ onAllow: 'user-approval' }` —
75
+ * "governance has no objection; still ask a person" — or compose manually
76
+ * (see {@link intuticApproval}).
77
+ *
78
+ * ## Audit hooks — observe-only, and honestly scoped
79
+ *
80
+ * eve's hook surface (`agent/hooks/*.ts`, `dist/src/public/definitions/
81
+ * hook.d.ts`) exposes `approval.candidate` / `approval.settled` stream
82
+ * events. Handlers are OBSERVE-ONLY by eve's own contract ("fire after eve
83
+ * has accepted and durably recorded each event ... cannot inject model
84
+ * context") — {@link intuticAuditHooks} is telemetry, not enforcement; the
85
+ * enforcement surface is {@link intuticApproval}. LOAD-BEARING caveat,
86
+ * verified against `dist/src/protocol/message.d.ts`: these two events carry
87
+ * `requestId`/`responderPrincipalId`/`turnId`/`stepIndex` but NOT the tool
88
+ * name or input.
89
+ *
90
+ * TD-411 half 2, revisited: `intuticApproval()` and `intuticAuditHooks()` are
91
+ * both plain functions exported from this one module and cannot be assumed to
92
+ * run in the same process at settlement time — eve's own docs state a parked
93
+ * approval survives a process restart verbatim ("the process can restart and
94
+ * the parked turn survives", `docs/tools/human-in-the-loop.md`), so a
95
+ * `requestId -> toolName` map populated at approval-decision time and read
96
+ * back later inside the hook would silently degrade for exactly the durable,
97
+ * long-parked approvals this mechanism exists for — the worst kind of fix,
98
+ * correct in local testing and quietly wrong in the deployments that matter
99
+ * most. That map was NOT built.
100
+ *
101
+ * What WAS found and built instead needs no cross-call memory at all: eve
102
+ * ships a THIRD hookable event, `input.requested` (confirmed in the same
103
+ * `HookEventMap`, `dist/src/public/definitions/hook.d.ts`), fired once when a
104
+ * batch of human-input requests is created — including every `kind:
105
+ * 'tool-approval'` request `intuticApproval()`'s `'user-approval'` return
106
+ * value produces. Each request in that ONE event already carries
107
+ * `requestId` AND `action.toolName` TOGETHER (`InputRequest`,
108
+ * `dist/src/runtime/input/types.d.ts`'s `inputRequestSchema` — confirmed by
109
+ * the shipped zod schema, not just prose; corroborated by
110
+ * `docs/tools/human-in-the-loop.md`'s explicit statement that "`toolName` and
111
+ * `requestId` identify the action and request"). {@link intuticAuditHooks}
112
+ * now subscribes to it too: it emits a real, tool-identified `tool_flagged`
113
+ * event at REQUEST time (closing the "no tool identity" gap unconditionally,
114
+ * with no process-boundary risk — the whole payload arrives in one event),
115
+ * and best-effort caches `requestId -> toolName` in memory so the LATER
116
+ * `approval.candidate`/`approval.settled` events attribute to the real tool
117
+ * name too WHEN this cache is still warm (same-process, or a short-lived
118
+ * pause). When it is not — a genuinely long-parked, cross-restart approval —
119
+ * settlement still falls back to the synthetic {@link EVE_APPROVAL_TOOL_NAME},
120
+ * exactly the pre-existing behaviour, never worse. See TD-411 for the full
121
+ * reasoning and what a persistent (non-memory) correlation store would take
122
+ * to close that residual gap.
123
+ *
124
+ * ## LLM egress — documented honestly, not oversold
125
+ *
126
+ * eve routes models through the Vercel AI Gateway by default (a gateway model
127
+ * id string in `agent.ts`'s `defineAgent({ model })`, `AI_GATEWAY_API_KEY` /
128
+ * `VERCEL_OIDC_TOKEN` credentials — confirmed in `docs/agent-config.md` and
129
+ * `docs/getting-started.mdx`). The AI Gateway wire protocol is NOT something
130
+ * the Intutic proxy parses, so gateway-routed egress is ungoverned by the
131
+ * proxy — full stop, and TD-412 tracks it. The governable path is eve's
132
+ * other documented option: passing a provider-authored `LanguageModel`
133
+ * (`createOpenAI(...)` etc.) to `defineAgent({ model })`, where the
134
+ * `withIntuticProxy()` helper — implemented once in `vercel.ts` and
135
+ * re-exported here — applies exactly as it does for the plain Vercel AI SDK.
136
+ * As with that framework, there is no env-var base-URL override: routing is
137
+ * in-code, per provider-construction call site.
138
+ *
139
+ * @module
140
+ */
141
+ import { active as activeGate } from './gate.js';
142
+ import { GateClient } from './client.js';
143
+ import { IntuticGateRefusal } from './errors.js';
144
+ // One routing story, one implementation: eve's direct-provider path is the
145
+ // Vercel AI SDK's (eve is built on `ai` v7), so the helper is re-exported
146
+ // rather than duplicated. See vercel.ts for the full module doc.
147
+ export { intuticProxyUrl, withIntuticProxy } from './vercel.js';
148
+ /** Synthetic tool name used by {@link intuticAuditHooks} — eve's
149
+ * `approval.candidate`/`approval.settled` events do not carry the real tool
150
+ * name (verified against `dist/src/protocol/message.d.ts`; see module doc). */
151
+ export const EVE_APPROVAL_TOOL_NAME = 'eve:approval';
152
+ /**
153
+ * Render an eve `toolInput` as the `tool_input` object {@link Gate.guard}
154
+ * evaluates.
155
+ *
156
+ * Mirrors dsh.ts's `renderDshToolInput` (same reasoning as its own mirror of
157
+ * `wrapTools.ts`'s non-exported `renderToolInput`): a plain object passes
158
+ * through as-is; `undefined` (which eve documents as possible — "toolInput
159
+ * can be undefined, so guard the access") becomes `{}`; any other non-object
160
+ * value falls back to `{ args: [...] }` so the gate still has something to
161
+ * evaluate.
162
+ */
163
+ function renderEveToolInput(input) {
164
+ if (input === undefined)
165
+ return {};
166
+ if (input !== null && typeof input === 'object' && !Array.isArray(input)) {
167
+ return input;
168
+ }
169
+ return { args: [input] };
170
+ }
171
+ /**
172
+ * Builds an approval policy matching eve's `ApprovalPolicy` signature, for
173
+ * direct use as a tool definition's `approval` field:
174
+ *
175
+ * ```ts
176
+ * // agent/tools/refund_charge.ts
177
+ * import { defineTool } from 'eve/tools'
178
+ * import { z } from 'zod'
179
+ * import { intuticApproval } from '@intutic/gate/eve'
180
+ *
181
+ * export default defineTool({
182
+ * description: 'Refund a charge.',
183
+ * inputSchema: z.object({ chargeId: z.string(), amount: z.number() }),
184
+ * approval: intuticApproval(),
185
+ * async execute(input) {
186
+ * return refund(input)
187
+ * },
188
+ * })
189
+ * ```
190
+ *
191
+ * (Install the gate once, process-wide, e.g. in `agent.ts`:
192
+ * `install(new Gate({ workspaceId: process.env.INTUTIC_WORKSPACE_ID }))`.)
193
+ *
194
+ * Resolves to `'not-applicable'` on allow (or `opts.onAllow` — see
195
+ * {@link IntuticApprovalOptions.onAllow}) and to `{ type: 'denied', reason }`
196
+ * on refusal — `reason` is the `[Intutic Governance] BLOCKED: ...` message,
197
+ * handed back to the model in place of a tool result; the tool's `execute`
198
+ * never runs.
199
+ *
200
+ * Requires a `Gate` — either passed via `{ gate }` here, or installed
201
+ * process-wide with `install(new Gate(...))`. A call with neither throws,
202
+ * refusing to run the tool unguarded rather than silently skipping
203
+ * enforcement — same posture as `wrapTool`/`intuticHooks`/
204
+ * `intuticToolApproval`. A non-refusal crash inside the gate is re-thrown
205
+ * rather than mapped to a denial — eve's own approval docs direct policies to
206
+ * "throw or deny, never silently allow", and a thrown policy fails the turn
207
+ * (fail closed), matching vercel.ts's posture for the same status family.
208
+ */
209
+ export function intuticApproval(opts = {}) {
210
+ return async (ctx) => {
211
+ const g = opts.gate ?? activeGate();
212
+ if (g === null) {
213
+ throw new Error('No gate configured: call install(new Gate(...)) from @intutic/gate, or pass ' +
214
+ '{ gate } to intuticApproval(). Refusing to run the tool unguarded.');
215
+ }
216
+ try {
217
+ await g.guard(ctx.toolName, renderEveToolInput(ctx.toolInput));
218
+ return opts.onAllow ?? 'not-applicable';
219
+ }
220
+ catch (exc) {
221
+ if (exc instanceof IntuticGateRefusal) {
222
+ return { type: 'denied', reason: exc.message };
223
+ }
224
+ throw exc;
225
+ }
226
+ };
227
+ }
228
+ /**
229
+ * The same policy, shaped for a CONNECTION's `approval` field — eve's MCP and
230
+ * OpenAPI connections accept one `approval` gating ALL of that connection's
231
+ * tools (`defineMcpClientConnection`/`defineOpenAPIConnection`, confirmed at
232
+ * `dist/src/public/definitions/connections/{mcp,openapi}.d.ts` — an omitted
233
+ * `approval` means every connection tool executes without approval):
234
+ *
235
+ * ```ts
236
+ * // agent/connections/support.ts
237
+ * import { defineMcpClientConnection } from 'eve/connections'
238
+ * import { intuticConnectionApproval } from '@intutic/gate/eve'
239
+ *
240
+ * export default defineMcpClientConnection({
241
+ * url: 'https://support.example.com/mcp',
242
+ * description: 'Support tickets.',
243
+ * approval: intuticConnectionApproval(),
244
+ * })
245
+ * ```
246
+ *
247
+ * Implemented AS {@link intuticApproval} (one enforcement path, not two) —
248
+ * the separate export exists because the surface differs in one way worth
249
+ * knowing: `ctx.toolName` for a connection tool is the QUALIFIED name
250
+ * (`support__add_internal_note`, connection slug + `__` + remote tool name —
251
+ * eve's own convention, per `docs/patterns/multi-tenant-approvals.md`), and
252
+ * that qualified name is what `Gate.guard()` evaluates and what SOP rules /
253
+ * snapshot `tool:` subjects must match. This adapter deliberately does not
254
+ * strip the prefix: the qualified name is the call's real identity inside
255
+ * this eve app, and un-prefixing would let two connections' same-named remote
256
+ * tools collapse into one rule target.
257
+ */
258
+ export function intuticConnectionApproval(opts = {}) {
259
+ return intuticApproval(opts);
260
+ }
261
+ /**
262
+ * Observe-only audit emitter for eve's human-approval lifecycle, shaped for
263
+ * `agent/hooks/`:
264
+ *
265
+ * ```ts
266
+ * // agent/hooks/intutic-audit.ts
267
+ * import { defineHook } from 'eve/hooks'
268
+ * import { intuticAuditHooks } from '@intutic/gate/eve'
269
+ *
270
+ * export default defineHook(intuticAuditHooks())
271
+ * ```
272
+ *
273
+ * Maps eve's approval stream events onto Intutic's gate event vocabulary
274
+ * (the same `tool_allowed`/`tool_blocked`/`tool_flagged` names `gate.ts`
275
+ * emits, so the dashboard renders both sources on one timeline):
276
+ *
277
+ * - `input.requested`, one `kind: 'tool-approval'` request → `tool_flagged`,
278
+ * under the REAL tool name (`request.action.toolName`) — a genuinely new
279
+ * signal this adapter did not emit before: a call is now pending human
280
+ * approval. See the module doc (TD-411) for why this event, not a
281
+ * process-memory map, is what closes "no tool identity" here.
282
+ * - `approval.settled` `outcome: 'approved'` → `tool_allowed` — a human
283
+ * explicitly approved the parked call.
284
+ * - `approval.settled` `outcome: 'cancelled'` → `tool_blocked` — the request
285
+ * was settled without approval; the call never ran. This is a HUMAN veto
286
+ * recorded for audit, not an Intutic-gate refusal (the reason string says
287
+ * so), and like every `tool_blocked` it creates an incident row.
288
+ * - `approval.candidate` non-`'pending'` outcomes (`rejected`/`failed`/
289
+ * `timed-out`/`stale`) → `tool_flagged` — a responder's attempt to answer
290
+ * went somewhere worth an operator's attention. `'pending'` (a candidate
291
+ * simply being created) is routine lifecycle and deliberately NOT emitted.
292
+ *
293
+ * `approval.candidate`/`approval.settled` attribute to the REAL tool name
294
+ * when this instance's in-memory `requestId -> toolName` cache (populated
295
+ * by `input.requested`) is still warm, and fall back to the synthetic
296
+ * {@link EVE_APPROVAL_TOOL_NAME} otherwise — see the module doc for why that
297
+ * fallback exists and is not itself a bug.
298
+ *
299
+ * These hooks are TELEMETRY, not enforcement: eve runs hook handlers after
300
+ * events are durably recorded, and they cannot veto anything (eve's own
301
+ * "observe-only" hook contract). The enforcement surface is
302
+ * {@link intuticApproval}. Handlers never throw — a telemetry failure must
303
+ * not break the run (`GateClient.emit`'s own never-throws contract).
304
+ */
305
+ export function intuticAuditHooks(opts = {}) {
306
+ const perSession = new Map();
307
+ // Best-effort, same-process-only. Populated by `input.requested` (which
308
+ // carries requestId and toolName together already — no correlation is
309
+ // needed to populate it) and consulted by `approval.candidate`/
310
+ // `approval.settled` (which do not carry toolName at all). Evicted on
311
+ // `approval.settled` — the terminal event for a requestId, after which it
312
+ // is never looked up again. A process that restarts between request and
313
+ // settlement (eve's own docs say this is expected and safe for eve itself
314
+ // — "the process can restart and the parked turn survives") simply finds
315
+ // this map empty and falls back to EVE_APPROVAL_TOOL_NAME, exactly this
316
+ // function's behaviour before this cache existed. See TD-411.
317
+ const requestIdToToolName = new Map();
318
+ function clientFor(sessionId) {
319
+ if (opts.client !== undefined)
320
+ return opts.client;
321
+ let client = perSession.get(sessionId);
322
+ if (client === undefined) {
323
+ try {
324
+ client = GateClient.fromEnv({ harness: 'eve', sessionId });
325
+ }
326
+ catch {
327
+ // fromEnv only throws on a missing session id, which we always
328
+ // supply — but a telemetry hook must never take the run down over a
329
+ // client it could not build.
330
+ client = null;
331
+ }
332
+ perSession.set(sessionId, client);
333
+ }
334
+ return client;
335
+ }
336
+ return {
337
+ events: {
338
+ 'input.requested': async (event, ctx) => {
339
+ const client = clientFor(ctx.session.id);
340
+ for (const request of event.data.requests) {
341
+ if (request.kind !== 'tool-approval' || request.action === undefined)
342
+ continue;
343
+ requestIdToToolName.set(request.requestId, request.action.toolName);
344
+ if (client === null)
345
+ continue;
346
+ await client.emit('tool_flagged', request.action.toolName, `eve requested human approval before running this tool (request ${request.requestId})`, request.action.input);
347
+ }
348
+ },
349
+ 'approval.candidate': async (event, ctx) => {
350
+ if (event.data.outcome === 'pending')
351
+ return;
352
+ const client = clientFor(ctx.session.id);
353
+ if (client === null)
354
+ return;
355
+ const toolName = requestIdToToolName.get(event.data.requestId) ?? EVE_APPROVAL_TOOL_NAME;
356
+ const suffix = event.data.reason ? `: ${event.data.reason}` : '';
357
+ await client.emit('tool_flagged', toolName, `eve approval candidate ${event.data.outcome} (request ${event.data.requestId}, ` +
358
+ `responder ${event.data.responderPrincipalId})${suffix}`, event.data);
359
+ },
360
+ 'approval.settled': async (event, ctx) => {
361
+ const client = clientFor(ctx.session.id);
362
+ const toolName = requestIdToToolName.get(event.data.requestId) ?? EVE_APPROVAL_TOOL_NAME;
363
+ requestIdToToolName.delete(event.data.requestId);
364
+ if (client === null)
365
+ return;
366
+ if (event.data.outcome === 'approved') {
367
+ await client.emit('tool_allowed', toolName, `eve approval settled: approved by ${event.data.responderPrincipalId} ` +
368
+ `(request ${event.data.requestId})`, event.data);
369
+ return;
370
+ }
371
+ await client.emit('tool_blocked', toolName, `eve approval settled: cancelled — the parked tool call never ran ` +
372
+ `(request ${event.data.requestId}, responder ${event.data.responderPrincipalId}). ` +
373
+ `Human veto recorded by the observe-only audit hook, not an Intutic gate refusal.`, event.data);
374
+ },
375
+ },
376
+ };
377
+ }
378
+ //# sourceMappingURL=eve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eve.js","sourceRoot":"","sources":["../src/eve.ts"],"names":[],"mappings":"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"}
package/dist/gate.d.ts ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The enforcement point.
3
+ *
4
+ * Port of `packages/intutic-clawde/intutic_clawde/gate/gate.py`.
5
+ *
6
+ * Intutic ships PreToolUse hooks for the harnesses it has a native adapter
7
+ * for; frameworks outside that list (Mastra, the Vercel AI SDK, LangChain.js,
8
+ * a hand-rolled loop) have no adapter, and the sync-daemon writes no
9
+ * PreToolUse hook for them. This package is that enforcement point: the
10
+ * missing adapter, written against Intutic's own published gate contract.
11
+ *
12
+ * Four tiers, in order — identical precedence to the Python SDK:
13
+ *
14
+ * A1 policy snapshot port of intuticGate() fails CLOSED
15
+ * A3 SOP rules authored in the product fails OPEN (A2 covers it)
16
+ * A2 image integrity local check fails CLOSED
17
+ * B POST /hook-gate control-plane check fail posture set by GateClient
18
+ *
19
+ * A1 and A2 are load-bearing and local. Tier B contributes the DLP regexes
20
+ * and workspace policy from the control plane; whether an unreachable control
21
+ * plane blocks is the client's `failClosed` setting (default true).
22
+ *
23
+ * Tier A3 applies rules written in the SOP register rather than in code. It
24
+ * runs BEFORE A2 so that a block, when both would fire, is attributed to the
25
+ * authored policy rather than to the hardcoded one. It fails open because A2
26
+ * covers the identical case and fails closed: A3 moves where the policy is
27
+ * *written*, and is not what makes the run safe. See soprules.ts.
28
+ *
29
+ * ## Naming note for callers porting from other Intutic SDKs
30
+ *
31
+ * This is NOT the "static floor" tier the compiled shell/JS gate emitters
32
+ * carry (`staticFloorPatterns()` in
33
+ * `services/sync-daemon/src/harness/protectedPaths.ts`, concatenated ahead of
34
+ * the snapshot's own rules in `gateBody.ts`'s `intuticGate()`). Like the
35
+ * Python SDK it ports, this package's Tier A1 reads ONLY
36
+ * `~/.intutic/hooks/policy-snapshot.rules` — the compiled-in bypass/secret/
37
+ * skill-surface patterns never reach that file (only SOP-authored rules, the
38
+ * destructive-command tier, and the tier-promoted skill-surface rules do, via
39
+ * `buildSnapshotRules` in `services/sync-daemon/src/lib/policySnapshot.ts`).
40
+ * A workspace relying on this SDK for governance therefore gets a strict
41
+ * SUBSET of what a shipped shell/JS harness gate enforces. That gap already
42
+ * exists in the Python SDK this package ports; it is not introduced here. See
43
+ * `src/__tests__/fidelity.test.ts` for the parts of the shipped contract this
44
+ * package's Tier A1 DOES reproduce exactly (the destructive-command and
45
+ * skill-surface pattern tables, as they would arrive via a real snapshot).
46
+ */
47
+ import { GateClient } from './client.js';
48
+ import * as imagecheck from './imagecheck.js';
49
+ import * as snapshot from './snapshot.js';
50
+ import * as soprules from './soprules.js';
51
+ /** Tools that cannot change anything. They still get the local snapshot check
52
+ * (Tier A1), but skip the remote gate call (Tier B). */
53
+ export declare const READ_ONLY_TOOLS: ReadonlySet<string>;
54
+ export interface GateConfig {
55
+ repoRoot?: string;
56
+ workspaceId?: string;
57
+ allowlistPath?: string;
58
+ enforce?: boolean;
59
+ useHookGate?: boolean;
60
+ useSopRules?: boolean;
61
+ }
62
+ interface ResolvedConfig {
63
+ repoRoot: string;
64
+ workspaceId: string;
65
+ allowlistPath: string;
66
+ enforce: boolean;
67
+ useHookGate: boolean;
68
+ useSopRules: boolean;
69
+ }
70
+ export type ToolInput = Record<string, unknown>;
71
+ export declare class Gate {
72
+ readonly cfg: ResolvedConfig;
73
+ readonly client: GateClient | null;
74
+ private _snapshot;
75
+ private _snapshotReported;
76
+ private _policy;
77
+ /** `null` = not fetched yet. A failed fetch caches `[]` so one unreachable
78
+ * control plane does not add a timeout to every subsequent tool call. */
79
+ private _sopRules;
80
+ constructor(cfg?: GateConfig, client?: GateClient | null);
81
+ private allowlistAbs;
82
+ getSnapshot(): snapshot.Snapshot;
83
+ /**
84
+ * Load the image allowlist.
85
+ *
86
+ * A missing or malformed allowlist is NOT treated as "allow everything".
87
+ * An empty policy with `require_digest` still refuses every unpinned
88
+ * image, which is the safe direction; a policy we cannot read at all
89
+ * throws, and the caller turns that into a block.
90
+ */
91
+ getPolicy(): imagecheck.ImagePolicy;
92
+ /**
93
+ * Active SOP rules, fetched once per process.
94
+ *
95
+ * Deliberately not refreshed on a timer: a rule set that changes mid-run
96
+ * makes a verdict depend on when the fetch landed. Construct a new `Gate`
97
+ * (or clear the cache) to refresh.
98
+ */
99
+ getSopRules(): Promise<soprules.SopRule[]>;
100
+ private emit;
101
+ /**
102
+ * Report snapshot condition exactly once per process — otherwise these
103
+ * states are computed and never read, and an operator cannot distinguish
104
+ * "snapshot missing on 400 machines" from "snapshot healthy".
105
+ */
106
+ private reportSnapshotHealthOnce;
107
+ /** Throws {@link IntuticGateRefusal} if this call must not run. Resolves to allow. */
108
+ guard(toolName: string, toolInput: ToolInput): Promise<void>;
109
+ }
110
+ export declare function install(gate: Gate | null): void;
111
+ export declare function active(): Gate | null;
112
+ export {};
113
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAKH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;yDACyD;AACzD,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAM9C,CAAA;AAEF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,UAAU,cAAc;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,OAAO,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;CACrB;AAaD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE/C,qBAAa,IAAI;IACf,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IAClC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,OAAO,CAAsC;IACrD;8EAC0E;IAC1E,OAAO,CAAC,SAAS,CAAkC;gBAEvC,GAAG,GAAE,UAAe,EAAE,MAAM,GAAE,UAAU,GAAG,IAAW;IAKlE,OAAO,CAAC,YAAY;IAOpB,WAAW,IAAI,QAAQ,CAAC,QAAQ;IAOhC;;;;;;;OAOG;IACH,SAAS,IAAI,UAAU,CAAC,WAAW;IAwBnC;;;;;;OAMG;IACG,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YAwBlC,IAAI;IAalB;;;;OAIG;YACW,wBAAwB;IAWtC,sFAAsF;IAChF,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAqGnE;AA0BD,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAE/C;AAED,wBAAgB,MAAM,IAAI,IAAI,GAAG,IAAI,CAEpC"}