@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
@@ -0,0 +1,254 @@
1
+ /**
2
+ * `@ai-sdk/workflow` adapter: `intuticNeedsApproval()`, `withIntuticApproval()`,
3
+ * `wrapWorkflowTools()`, and the FatalError-compatible `IntuticWorkflowRefusal`.
4
+ *
5
+ * `@intutic/gate/workflow` — the same idea `vercel.ts` implements for the base
6
+ * `ai` package (wrap the framework's own veto point around this package's
7
+ * `Gate.guard()`), reshaped for Vercel's DURABLE workflow-agent runtime, where
8
+ * two facts change the adapter's whole design:
9
+ *
10
+ * ## 1. The veto surface is per-tool `needsApproval`, not an agent option
11
+ *
12
+ * Confirmed against a real install (`@ai-sdk/workflow@1.0.69`):
13
+ * `WorkflowAgent`/`WorkflowAgentOptions` carry ZERO approval fields — the
14
+ * string `toolApproval` does not appear anywhere in the package's shipped
15
+ * `.d.ts`. The veto surface is `needsApproval` (boolean | function) on the
16
+ * TOOL definition, exactly as the framework's own workflow-agent docs
17
+ * describe under "Tool Approval". The compiled agent loop confirms the
18
+ * semantics (dist/index.js): `needsApproval == null` → no approval; a
19
+ * boolean is used directly; a function is awaited as
20
+ * `needsApproval(input, { toolCallId, messages, context })`. A call needing
21
+ * approval pauses DURABLY — the run suspends and a human can approve hours
22
+ * later — so this framework, uniquely in this package, has a real
23
+ * human-in-the-loop lane for the gate to route into.
24
+ *
25
+ * (`ai@7.0.68` marks tool-level `needsApproval` `@deprecated` in favour of
26
+ * generateText-level `toolApproval` — but `@ai-sdk/workflow`'s OWN agent loop
27
+ * reads the tool-level field and exposes no other veto surface, so it is the
28
+ * correct — indeed the only — integration point here. Noted in the TD entry
29
+ * this phase filed, so a future @ai-sdk/workflow release moving to the
30
+ * generateText-shaped surface gets caught rather than silently ungated.)
31
+ *
32
+ * ## 2. Denials must not be retried — refusals here are FatalError-compatible
33
+ *
34
+ * A durable workflow retries failed steps. Confirmed against real installs
35
+ * (`workflow@4.8.3` re-exporting `@workflow/errors@4.2.1` via
36
+ * `@workflow/core`): the runtime's retry/abort decision consults
37
+ * `FatalError.is(err)` (`@workflow/core`'s `runtime/step-handler.js` — a
38
+ * fatal error "bubbl[es] up to parent workflow"; anything else is retried
39
+ * toward `maxAttempts`), and `FatalError.is()` DUCK-TYPES: it checks
40
+ * `err.name === 'FatalError'` on any Error-shaped object, precisely because
41
+ * workflows execute in a separate `vm` realm where `instanceof` fails across
42
+ * the boundary. A plain `IntuticGateRefusal` thrown from `needsApproval` or a
43
+ * tool body would therefore be RETRIED — a governance denial replayed on a
44
+ * timer until max attempts, burning the run to reach the same refusal. So
45
+ * this adapter's refusals are {@link IntuticWorkflowRefusal}: still an
46
+ * `IntuticGateRefusal` (`instanceof` works in-realm, `.reason`/`.code`/
47
+ * `.incidentId` intact, message still `[Intutic Governance] BLOCKED: ...`)
48
+ * but with `name = 'FatalError'` so the real runtime's duck-check aborts
49
+ * instead of retry-looping. `__tests__/workflow.test.ts` pins this against
50
+ * the REAL `FatalError.is` from the `workflow` package, not a re-derivation.
51
+ *
52
+ * Deliberately asymmetric: a NON-refusal crash inside `Gate.guard()` (a
53
+ * transient network failure in a remote tier, an unexpected bug) is re-thrown
54
+ * UNTOUCHED — retryable, which is the durable runtime's correct response to a
55
+ * transient failure. Only a real governance verdict (and the fail-closed
56
+ * "no gate configured" case, which is deterministic and would fail every
57
+ * retry identically) is made fatal.
58
+ *
59
+ * @module
60
+ */
61
+ import { active as activeGate } from './gate.js';
62
+ import { IntuticGateRefusal } from './errors.js';
63
+ import { wrapTools } from './wrapTools.js';
64
+ /**
65
+ * A refused call inside a durable workflow.
66
+ *
67
+ * `name` is `'FatalError'` — NOT `'IntuticGateRefusal'` — on purpose: the
68
+ * workflow runtime's `FatalError.is()` duck-types on that exact string (see
69
+ * module doc), and it is the only cross-VM-realm-safe way to tell the runtime
70
+ * "abort, do not retry". Everything else about the refusal contract is
71
+ * preserved: `instanceof IntuticGateRefusal` still holds (same realm),
72
+ * `.message` still carries the `[Intutic Governance] BLOCKED:` prefix, and
73
+ * `.reason`/`.code`/`.incidentId` carry the structured verdict. `fatal: true`
74
+ * mirrors the property the real `FatalError` class sets, for code that
75
+ * checks the flag rather than the name.
76
+ */
77
+ export class IntuticWorkflowRefusal extends IntuticGateRefusal {
78
+ fatal = true;
79
+ constructor(reason, code, incidentId) {
80
+ super(reason, code, incidentId);
81
+ this.name = 'FatalError';
82
+ }
83
+ /** Rewrap a core refusal so the durable runtime treats it as fatal. */
84
+ static from(refusal) {
85
+ return new IntuticWorkflowRefusal(refusal.reason, refusal.code, refusal.incidentId);
86
+ }
87
+ /**
88
+ * Cross-realm-safe detection (the same trick `FatalError.is` itself uses):
89
+ * `instanceof` fails across the workflow `vm` boundary, so this checks the
90
+ * duck-typed name plus this package's own message prefix.
91
+ */
92
+ static is(value) {
93
+ return (typeof value === 'object' &&
94
+ value !== null &&
95
+ value.name === 'FatalError' &&
96
+ typeof value.message === 'string' &&
97
+ (value.message.startsWith('[Intutic Governance] BLOCKED:')));
98
+ }
99
+ }
100
+ function requireGate(gate, helper) {
101
+ const g = gate ?? activeGate();
102
+ if (g === null) {
103
+ // FatalError-compatible on purpose: "no gate configured" is
104
+ // deterministic — a durable retry would fail identically every attempt,
105
+ // so burning the step's retry budget on it helps nobody. Fail closed,
106
+ // once, loudly.
107
+ throw new IntuticWorkflowRefusal(`No gate configured: call install(new Gate(...)) from @intutic/gate, or pass { gate } to ${helper}. ` +
108
+ 'Refusing to run the tool unguarded.', 'NO_GATE');
109
+ }
110
+ return g;
111
+ }
112
+ function renderWorkflowToolInput(input) {
113
+ if (input !== null && typeof input === 'object' && !Array.isArray(input)) {
114
+ return input;
115
+ }
116
+ return { args: [input] };
117
+ }
118
+ /**
119
+ * Build an async `needsApproval` function for ONE workflow tool:
120
+ *
121
+ * ```ts
122
+ * import { WorkflowAgent } from '@ai-sdk/workflow'
123
+ * import { Gate, install } from '@intutic/gate'
124
+ * import { intuticNeedsApproval } from '@intutic/gate/workflow'
125
+ *
126
+ * install(new Gate({ workspaceId: process.env.INTUTIC_WORKSPACE_ID }))
127
+ *
128
+ * const agent = new WorkflowAgent({
129
+ * model,
130
+ * tools: {
131
+ * deployService: {
132
+ * description: '...',
133
+ * inputSchema,
134
+ * execute: deployStep,
135
+ * needsApproval: intuticNeedsApproval('deployService'),
136
+ * },
137
+ * },
138
+ * })
139
+ * ```
140
+ *
141
+ * The tool name is a parameter because the framework's `needsApproval`
142
+ * signature does not carry it — `(input, { toolCallId, messages, context })`
143
+ * has no `toolName` field (confirmed against the shipped types). Use
144
+ * {@link withIntuticApproval} to attach this to a whole tools record without
145
+ * hand-repeating each name.
146
+ *
147
+ * Verdict mapping: BLOCK → throws {@link IntuticWorkflowRefusal} (fatal —
148
+ * aborts instead of retry-looping; see module doc); ALLOW → `false` or `true`
149
+ * per `onAllow`. A non-refusal crash in the gate is re-thrown untouched
150
+ * (retryable — correct for transients).
151
+ */
152
+ export function intuticNeedsApproval(toolName, opts = {}) {
153
+ const onAllow = opts.onAllow ?? 'auto';
154
+ return async (input, _options) => {
155
+ const g = requireGate(opts.gate, 'intuticNeedsApproval()');
156
+ try {
157
+ await g.guard(toolName, renderWorkflowToolInput(input));
158
+ }
159
+ catch (exc) {
160
+ if (exc instanceof IntuticGateRefusal && !(exc instanceof IntuticWorkflowRefusal)) {
161
+ throw IntuticWorkflowRefusal.from(exc);
162
+ }
163
+ throw exc;
164
+ }
165
+ return onAllow === 'human';
166
+ };
167
+ }
168
+ /**
169
+ * Attach the Intutic gate to every tool in a record, composing with any
170
+ * `needsApproval` the tool already declares:
171
+ *
172
+ * ```ts
173
+ * const agent = new WorkflowAgent({ model, tools: withIntuticApproval(tools) })
174
+ * ```
175
+ *
176
+ * For each tool (the record KEY is the tool name reported to the gate — the
177
+ * same identity the framework itself dispatches on):
178
+ *
179
+ * 1. `Gate.guard()` runs first. BLOCK throws {@link IntuticWorkflowRefusal}.
180
+ * 2. On allow, the tool's OWN prior `needsApproval` still applies: a
181
+ * boolean is returned as-is; a function is awaited with the original
182
+ * arguments; a tool with none falls back to `onAllow` ('auto' → false).
183
+ *
184
+ * So a tool that already said "always ask a human" (`needsApproval: true`)
185
+ * keeps asking a human — this gate only ADDS the ability to refuse outright,
186
+ * it never removes an approval pause the caller had configured.
187
+ *
188
+ * Returns a NEW record of NEW tool objects (shallow copies); the input record
189
+ * and its tools are not mutated.
190
+ */
191
+ export function withIntuticApproval(tools, opts = {}) {
192
+ const out = {};
193
+ for (const [name, tool] of Object.entries(tools)) {
194
+ const prior = tool.needsApproval;
195
+ const gateApproval = intuticNeedsApproval(name, opts);
196
+ const composed = async (input, options) => {
197
+ const gateSaysHuman = await gateApproval(input, options);
198
+ if (typeof prior === 'boolean')
199
+ return prior || gateSaysHuman;
200
+ if (typeof prior === 'function') {
201
+ const priorResult = await prior(input, options);
202
+ return Boolean(priorResult) || gateSaysHuman;
203
+ }
204
+ return gateSaysHuman;
205
+ };
206
+ out[name] = { ...tool, needsApproval: composed };
207
+ }
208
+ return out;
209
+ }
210
+ /**
211
+ * `wrapTools` for durable workflow tool definitions — the same generic
212
+ * execute-wrapping `wrapTools.ts` provides (gate first, then the real body),
213
+ * with ONE workflow-specific change: a refusal thrown out of the wrapped
214
+ * `execute` is rewrapped as {@link IntuticWorkflowRefusal}, because a
215
+ * workflow tool's `execute` is a durable STEP and a step that throws a
216
+ * non-fatal error is retried (see module doc). Everything else — including
217
+ * "no gate configured" handling and the record-key-is-tool-name convention —
218
+ * is `wrapTools`'s own behaviour, reused rather than reimplemented.
219
+ *
220
+ * Prefer {@link withIntuticApproval} as the primary integration: it uses the
221
+ * framework's own pre-execution veto surface, refuses BEFORE the durable
222
+ * step ever starts, and keeps the human-approval lane available. This
223
+ * execute-level wrapper is defense in depth (or the option for callers whose
224
+ * tools are shared with non-workflow code paths); running both double-guards
225
+ * the call, which is harmless but emits duplicate gate telemetry.
226
+ */
227
+ export function wrapWorkflowTools(tools, gate) {
228
+ const wrapped = wrapTools(tools, gate);
229
+ const out = {};
230
+ for (const [name, tool] of Object.entries(wrapped)) {
231
+ if (typeof tool === 'function') {
232
+ out[name] = fatalize(tool);
233
+ }
234
+ else {
235
+ out[name] = { ...tool, execute: fatalize(tool.execute) };
236
+ }
237
+ }
238
+ return out;
239
+ }
240
+ function fatalize(fn) {
241
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
242
+ return async (...args) => {
243
+ try {
244
+ return await fn(...args);
245
+ }
246
+ catch (exc) {
247
+ if (exc instanceof IntuticGateRefusal && !(exc instanceof IntuticWorkflowRefusal)) {
248
+ throw IntuticWorkflowRefusal.from(exc);
249
+ }
250
+ throw exc;
251
+ }
252
+ };
253
+ }
254
+ //# sourceMappingURL=workflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow.js","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Generic tool-wrapping helper.
3
+ *
4
+ * The JS/TS analogue of `guard`/`guard_tools` in
5
+ * `packages/intutic-clawde/intutic_clawde/gate/framework.py`, reshaped for
6
+ * how JS agent frameworks actually define a "tool": either
7
+ *
8
+ * * a plain async function `(input) => result`, or
9
+ * * an object carrying an `execute(input) => result` method alongside
10
+ * framework metadata (`{ description, parameters, execute }` — the
11
+ * Vercel AI SDK / Mastra shape; LangChain.js `DynamicStructuredTool`
12
+ * and `tool()`-created tools carry the same `execute`/`func` contract
13
+ * closely enough that this shape covers them too).
14
+ *
15
+ * This is the helper any JS framework WITHOUT a dedicated `@intutic/gate/*`
16
+ * adapter (notably LangChain.js, until a sibling phase ships one) uses to get
17
+ * gated: wrap each tool with {@link wrapTool} (or a whole collection with
18
+ * {@link wrapTools}) before handing it to the framework's agent/executor.
19
+ *
20
+ * On deny, the wrapped tool's `execute`/call THROWS `IntuticGateRefusal`
21
+ * (see `errors.ts`) before the real implementation ever runs — this
22
+ * package's throw-based refusal contract, not a return-value one. On allow,
23
+ * the real implementation runs and its return value passes through
24
+ * untouched.
25
+ *
26
+ * Requires a `Gate` — either passed explicitly via `{ gate }`, or installed
27
+ * process-wide with `install(new Gate(...))` from `gate.ts`. Calling a
28
+ * wrapped tool with neither throws, refusing to run the tool unguarded
29
+ * rather than silently skipping enforcement.
30
+ */
31
+ import { type Gate } from './gate.js';
32
+ export type AnyFn = (...args: any[]) => any;
33
+ export interface ExecutableTool {
34
+ execute: AnyFn;
35
+ name?: string;
36
+ [key: string]: unknown;
37
+ }
38
+ export interface WrapToolOptions {
39
+ /** Tool name reported to the gate. Defaults to the function's own `.name`
40
+ * (or the tool object's `.name`), then falls back to `"tool"`. */
41
+ name?: string;
42
+ /** Overrides the process-wide installed gate for this tool only. */
43
+ gate?: Gate;
44
+ }
45
+ /**
46
+ * Wrap a single tool — a plain async function, or a `{ execute, ... }`
47
+ * object — so `Gate.guard()` runs before the real implementation.
48
+ *
49
+ * The object form returns a NEW object (a shallow copy with `execute`
50
+ * replaced); it does not mutate the one passed in, since the frameworks this
51
+ * targets tend to hand out tool definitions that are re-used or frozen.
52
+ * Already-wrapped functions/objects pass through unchanged, so wrapping the
53
+ * same tool twice does not double-gate it.
54
+ */
55
+ export declare function wrapTool<T extends AnyFn>(fn: T, opts?: WrapToolOptions): T;
56
+ export declare function wrapTool<T extends ExecutableTool>(tool: T, opts?: WrapToolOptions): T;
57
+ /**
58
+ * Wrap a collection of tools in one call.
59
+ *
60
+ * Accepts either an array (tool name comes from each tool's own `.name`/
61
+ * function name) or a record keyed by tool name (the Vercel AI SDK / Mastra
62
+ * `tools: { myTool: {...} }` shape) — in the record form the KEY is always
63
+ * the name reported to the gate, since that is the identity the framework
64
+ * itself uses to invoke the tool.
65
+ */
66
+ export declare function wrapTools<T extends AnyFn | ExecutableTool>(tools: readonly T[], gate?: Gate): T[];
67
+ export declare function wrapTools<T extends AnyFn | ExecutableTool>(tools: Record<string, T>, gate?: Gate): Record<string, T>;
68
+ //# sourceMappingURL=wrapTools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapTools.d.ts","sourceRoot":"","sources":["../src/wrapTools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAwB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAG3D,MAAM,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;AAE3C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,KAAK,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B;uEACmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,IAAI,CAAC,EAAE,IAAI,CAAA;CACZ;AAgDD;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,KAAK,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,CAAC,CAAA;AAC3E,wBAAgB,QAAQ,CAAC,CAAC,SAAS,cAAc,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,CAAC,CAAA;AAgBtF;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,KAAK,GAAG,cAAc,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,CAAA;AAClG,wBAAgB,SAAS,CAAC,CAAC,SAAS,KAAK,GAAG,cAAc,EACxD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EACxB,IAAI,CAAC,EAAE,IAAI,GACV,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Generic tool-wrapping helper.
3
+ *
4
+ * The JS/TS analogue of `guard`/`guard_tools` in
5
+ * `packages/intutic-clawde/intutic_clawde/gate/framework.py`, reshaped for
6
+ * how JS agent frameworks actually define a "tool": either
7
+ *
8
+ * * a plain async function `(input) => result`, or
9
+ * * an object carrying an `execute(input) => result` method alongside
10
+ * framework metadata (`{ description, parameters, execute }` — the
11
+ * Vercel AI SDK / Mastra shape; LangChain.js `DynamicStructuredTool`
12
+ * and `tool()`-created tools carry the same `execute`/`func` contract
13
+ * closely enough that this shape covers them too).
14
+ *
15
+ * This is the helper any JS framework WITHOUT a dedicated `@intutic/gate/*`
16
+ * adapter (notably LangChain.js, until a sibling phase ships one) uses to get
17
+ * gated: wrap each tool with {@link wrapTool} (or a whole collection with
18
+ * {@link wrapTools}) before handing it to the framework's agent/executor.
19
+ *
20
+ * On deny, the wrapped tool's `execute`/call THROWS `IntuticGateRefusal`
21
+ * (see `errors.ts`) before the real implementation ever runs — this
22
+ * package's throw-based refusal contract, not a return-value one. On allow,
23
+ * the real implementation runs and its return value passes through
24
+ * untouched.
25
+ *
26
+ * Requires a `Gate` — either passed explicitly via `{ gate }`, or installed
27
+ * process-wide with `install(new Gate(...))` from `gate.ts`. Calling a
28
+ * wrapped tool with neither throws, refusing to run the tool unguarded
29
+ * rather than silently skipping enforcement.
30
+ */
31
+ import { active as activeGate } from './gate.js';
32
+ const GUARDED = Symbol.for('intutic.gate.guarded');
33
+ function isGuarded(fn) {
34
+ return fn[GUARDED] === true;
35
+ }
36
+ /**
37
+ * Render a call's arguments as the `tool_input` object the gate evaluates.
38
+ *
39
+ * Every JS agent-framework tool signature this package targets calls
40
+ * `execute` with the arguments object as the first positional parameter
41
+ * (`execute(input)`, `execute(input, context)`, `_call(input)`, `func(input)`
42
+ * ...), so the first argument — when it is a plain object — IS the tool
43
+ * input. A call shaped some other way (positional scalars, no arguments)
44
+ * falls back to `{ args: [...] }` so the gate still sees something to
45
+ * evaluate rather than throwing on a signature it does not recognise.
46
+ */
47
+ function renderToolInput(args) {
48
+ const [first] = args;
49
+ if (first !== null && typeof first === 'object' && !Array.isArray(first)) {
50
+ return first;
51
+ }
52
+ return { args };
53
+ }
54
+ function wrapFn(fn, toolName, gate) {
55
+ if (isGuarded(fn))
56
+ return fn;
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ const wrapped = async (...args) => {
59
+ const g = gate ?? activeGate();
60
+ if (g === null) {
61
+ throw new Error('No gate configured: call install(new Gate(...)) from @intutic/gate, or pass ' +
62
+ '{ gate } to wrapTool()/wrapTools(). Refusing to run the tool unguarded.');
63
+ }
64
+ await g.guard(toolName, renderToolInput(args));
65
+ return fn(...args);
66
+ };
67
+ Object.defineProperty(wrapped, GUARDED, { value: true, enumerable: false });
68
+ Object.defineProperty(wrapped, 'name', { value: toolName, configurable: true });
69
+ return wrapped;
70
+ }
71
+ export function wrapTool(toolOrFn, opts = {}) {
72
+ if (typeof toolOrFn === 'function') {
73
+ const name = opts.name ?? (toolOrFn.name || 'tool');
74
+ return wrapFn(toolOrFn, name, opts.gate);
75
+ }
76
+ if (toolOrFn !== null && typeof toolOrFn === 'object' && typeof toolOrFn.execute === 'function') {
77
+ const name = opts.name ?? toolOrFn.name ?? 'tool';
78
+ return { ...toolOrFn, execute: wrapFn(toolOrFn.execute, name, opts.gate) };
79
+ }
80
+ throw new TypeError('wrapTool: expected an async function or a tool object with an execute() method, got ' +
81
+ `${typeof toolOrFn}`);
82
+ }
83
+ export function wrapTools(tools, gate) {
84
+ if (Array.isArray(tools)) {
85
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
+ return tools.map((t) => wrapTool(t, { gate }));
87
+ }
88
+ const out = {};
89
+ for (const [key, t] of Object.entries(tools)) {
90
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
+ out[key] = wrapTool(t, { name: key, gate });
92
+ }
93
+ return out;
94
+ }
95
+ //# sourceMappingURL=wrapTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapTools.js","sourceRoot":"","sources":["../src/wrapTools.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,123 @@
1
+ {
2
+ "name": "@intutic/gate",
3
+ "version": "1.10.1",
4
+ "description": "Intutic pre-execution tool gate for JS/TS agent frameworks without a shipped Intutic harness (Mastra, the Vercel AI SDK, LangChain.js, dsh, and hand-rolled agent loops)",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/intutic/intutic",
10
+ "directory": "packages/gate-js"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "import": "./dist/index.js",
20
+ "types": "./dist/index.d.ts"
21
+ },
22
+ "./mastra": {
23
+ "import": "./dist/mastra.js",
24
+ "types": "./dist/mastra.d.ts"
25
+ },
26
+ "./vercel": {
27
+ "import": "./dist/vercel.js",
28
+ "types": "./dist/vercel.d.ts"
29
+ },
30
+ "./dsh": {
31
+ "import": "./dist/dsh.js",
32
+ "types": "./dist/dsh.d.ts"
33
+ },
34
+ "./openai": {
35
+ "import": "./dist/openai.js",
36
+ "types": "./dist/openai.d.ts"
37
+ },
38
+ "./eve": {
39
+ "import": "./dist/eve.js",
40
+ "types": "./dist/eve.d.ts"
41
+ },
42
+ "./harness": {
43
+ "import": "./dist/harness.js",
44
+ "types": "./dist/harness.d.ts"
45
+ },
46
+ "./workflow": {
47
+ "import": "./dist/workflow.js",
48
+ "types": "./dist/workflow.d.ts"
49
+ },
50
+ "./managed-agents": {
51
+ "import": "./dist/managedAgents.js",
52
+ "types": "./dist/managedAgents.d.ts"
53
+ },
54
+ "./trueforge": {
55
+ "import": "./dist/trueforge.js",
56
+ "types": "./dist/trueforge.d.ts"
57
+ }
58
+ },
59
+ "typesVersions": {
60
+ "*": {
61
+ "mastra": [
62
+ "src/mastra.ts"
63
+ ],
64
+ "vercel": [
65
+ "src/vercel.ts"
66
+ ],
67
+ "dsh": [
68
+ "src/dsh.ts"
69
+ ],
70
+ "openai": [
71
+ "src/openai.ts"
72
+ ],
73
+ "eve": [
74
+ "src/eve.ts"
75
+ ],
76
+ "harness": [
77
+ "src/harness.ts"
78
+ ],
79
+ "workflow": [
80
+ "src/workflow.ts"
81
+ ],
82
+ "managed-agents": [
83
+ "src/managedAgents.ts"
84
+ ],
85
+ "trueforge": [
86
+ "src/trueforge.ts"
87
+ ]
88
+ }
89
+ },
90
+ "files": [
91
+ "dist/",
92
+ "README.md"
93
+ ],
94
+ "dependencies": {
95
+ "yaml": "^2.9.0"
96
+ },
97
+ "devDependencies": {
98
+ "@ai-sdk/harness": "^1.0.75",
99
+ "@anthropic-ai/sdk": "^0.117.1",
100
+ "@ai-sdk/openai": "^4.0.43",
101
+ "@ai-sdk/workflow": "^1.0.69",
102
+ "@mastra/core": "^1.59.0",
103
+ "@deepseek-ai/cordis": "^4.0.1",
104
+ "@openai/agents": "0.16.1",
105
+ "@truefoundry/trueforge-core": "^0.1.4",
106
+ "@types/node": "^22",
107
+ "ai": "^7.0.68",
108
+ "eve": "^0.39.1",
109
+ "typescript": "^5.5",
110
+ "vitest": "^4",
111
+ "workflow": "^4.8.3",
112
+ "zod": "^4.2.0"
113
+ },
114
+ "engines": {
115
+ "node": ">=18"
116
+ },
117
+ "scripts": {
118
+ "lint": "eslint src",
119
+ "build": "tsc",
120
+ "typecheck": "tsc --noEmit",
121
+ "test": "vitest run --passWithNoTests"
122
+ }
123
+ }