@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,174 @@
1
+ /**
2
+ * Vercel AI SDK adapter: `intuticToolApproval()` and `withIntuticProxy()`.
3
+ *
4
+ * `@intutic/gate/vercel` — 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
+ *
8
+ * The `ai` package (v6+) exposes a documented pre-execution veto point: a
9
+ * `toolApproval` callback accepted by `generateText`/`streamText` and
10
+ * `ToolLoopAgent`. It runs once per tool call, before execution, and its
11
+ * return value decides whether the call proceeds.
12
+ *
13
+ * ## Confirmed against a real install
14
+ *
15
+ * `ai@7.0.68` + `@ai-sdk/openai@4.0.43` were installed in a scratch directory
16
+ * and `node_modules/ai/dist/index.d.ts` read directly (not inferred from
17
+ * docs alone). The relevant types:
18
+ *
19
+ * ```ts
20
+ * type ToolApprovalStatus =
21
+ * | undefined | 'not-applicable' | 'approved' | 'denied' | 'user-approval'
22
+ * | { type: 'not-applicable'; reason?: never }
23
+ * | { type: 'approved'; reason?: string }
24
+ * | { type: 'denied'; reason?: string }
25
+ * | { type: 'user-approval'; reason?: never }
26
+ *
27
+ * type GenericToolApprovalFunction<TOOLS, TOOLS_CONTEXT, RUNTIME_CONTEXT> = (options: {
28
+ * toolCall: TypedToolCall<TOOLS> // { toolName: string; input: ... ; ... }
29
+ * tools: TOOLS | undefined
30
+ * toolsContext: TOOLS_CONTEXT
31
+ * runtimeContext: RUNTIME_CONTEXT
32
+ * messages: ModelMessage[]
33
+ * }) => MaybePromiseLike<ToolApprovalStatus>
34
+ *
35
+ * type ToolApprovalConfiguration<TOOLS, RUNTIME_CONTEXT> =
36
+ * GenericToolApprovalFunction<...> | { [toolName]?: ToolApprovalStatus | SingleToolApprovalFunction<...> }
37
+ * ```
38
+ * (`generateText`/`streamText`/`ToolLoopAgent` all accept
39
+ * `toolApproval?: ToolApprovalConfiguration<TOOLS, RUNTIME_CONTEXT>`.)
40
+ *
41
+ * `'not-applicable'` (also spelled `{ type: 'not-applicable' }`, and
42
+ * `undefined` is treated the same) means "this gate has no opinion, proceed
43
+ * normally" — exactly matching the plan's description. `'denied'` (or
44
+ * `{ type: 'denied', reason }`) vetoes the call before execution; the SDK
45
+ * hands `reason` back to the model in place of a tool result. This adapter
46
+ * implements `GenericToolApprovalFunction`, the whole-config form (not the
47
+ * per-tool-keyed form), so a single `intuticToolApproval()` call covers every
48
+ * tool in `tools`.
49
+ *
50
+ * ## Known, documented limitation — no env-var LLM routing
51
+ *
52
+ * Unlike almost every other harness in this codebase (which honour
53
+ * `OPENAI_BASE_URL`/`ANTHROPIC_BASE_URL` or an equivalent), the Vercel AI SDK
54
+ * has NO environment-variable-based base-URL override mechanism. Routing LLM
55
+ * traffic through the Intutic proxy requires IN-CODE provider construction —
56
+ * `createOpenAI({ baseURL })`, `createGateway({ baseURL })`, etc. — not a
57
+ * sourced `.env.intutic` file the way `langchainAdapter`/`langgraphAdapter`
58
+ * and the rest of the SDK-gated family work. `withIntuticProxy()` below is a
59
+ * small factory wrapper for that in-code construction; it does not — and
60
+ * cannot — make this "zero-code" the way an env-var-honouring harness's
61
+ * integration correctly can claim to be. See
62
+ * `apps/docs/integrations/vercel-ai-sdk.md` for the operator-facing version
63
+ * of this same limitation, stated plainly rather than oversold.
64
+ */
65
+ import { type Gate } from './gate.js';
66
+ /** Structural copy of `ai`'s `ToolApprovalStatus` — this package does not
67
+ * depend on `ai` at runtime, so the shape is declared here rather than
68
+ * imported. Confirmed field-for-field against `ai@7.0.68`'s
69
+ * `dist/index.d.ts` (see module doc). */
70
+ export type VercelToolApprovalStatus = undefined | 'not-applicable' | 'approved' | 'denied' | 'user-approval' | {
71
+ type: 'not-applicable';
72
+ reason?: undefined;
73
+ } | {
74
+ type: 'approved';
75
+ reason?: string;
76
+ } | {
77
+ type: 'denied';
78
+ reason?: string;
79
+ } | {
80
+ type: 'user-approval';
81
+ reason?: undefined;
82
+ };
83
+ /** Structural copy of `ai`'s `TypedToolCall` — the fields this adapter
84
+ * actually reads (`toolName`, `input`); the real type carries more. */
85
+ export interface VercelToolCall {
86
+ toolName: string;
87
+ input: unknown;
88
+ }
89
+ /** Structural copy of the options object `ai`'s `GenericToolApprovalFunction`
90
+ * receives — the whole-config form of `toolApproval` accepted by
91
+ * `generateText`/`streamText`/`ToolLoopAgent`. Only `toolCall` is read here;
92
+ * the rest pass through the structural type so this function is assignable
93
+ * to the real (wider) signature. */
94
+ export interface VercelToolApprovalOptions {
95
+ toolCall: VercelToolCall;
96
+ [key: string]: unknown;
97
+ }
98
+ /** Structural copy of `ai`'s `GenericToolApprovalFunction` — the exact shape
99
+ * `toolApproval` expects. */
100
+ export type VercelToolApproval = (options: VercelToolApprovalOptions) => Promise<VercelToolApprovalStatus>;
101
+ export interface IntuticToolApprovalOptions {
102
+ /** Overrides the process-wide installed gate (`install(new Gate(...))`). */
103
+ gate?: Gate;
104
+ }
105
+ /**
106
+ * Builds a `toolApproval` callback matching `ai`'s `GenericToolApprovalFunction`
107
+ * signature, for direct use on `generateText`/`streamText`/`ToolLoopAgent`:
108
+ *
109
+ * ```ts
110
+ * import { generateText } from 'ai'
111
+ * import { Gate, install } from '@intutic/gate'
112
+ * import { intuticToolApproval } from '@intutic/gate/vercel'
113
+ *
114
+ * install(new Gate({ workspaceId: process.env.INTUTIC_WORKSPACE_ID }))
115
+ *
116
+ * await generateText({
117
+ * model,
118
+ * tools,
119
+ * toolApproval: intuticToolApproval(),
120
+ * })
121
+ * ```
122
+ *
123
+ * Resolves to `'not-applicable'` on allow (this gate has no opinion; any
124
+ * other approval mechanism the caller has configured still applies) and to
125
+ * `{ type: 'denied', reason }` on refusal — `reason` is the
126
+ * `[Intutic Governance] BLOCKED: ...` message, handed back to the model in
127
+ * place of a tool result.
128
+ *
129
+ * Requires a `Gate` — either passed via `{ gate }` here, or installed
130
+ * process-wide with `install(new Gate(...))`. A call with neither throws,
131
+ * refusing to run the tool unguarded rather than silently skipping
132
+ * enforcement — same posture as `wrapTool`/`intuticHooks`.
133
+ */
134
+ export declare function intuticToolApproval(opts?: IntuticToolApprovalOptions): VercelToolApproval;
135
+ /**
136
+ * Resolves the Intutic proxy URL the same way the rest of this codebase does
137
+ * — `INTUTIC_PROXY_URL`, falling back to the local default every CLI command
138
+ * and env-writer here shares (`tools/cli/src/commands/exec.ts`,
139
+ * `tools/cli/src/lib/onboarding.ts`, `services/sync-daemon/src/agentReporter.ts`).
140
+ * Not re-hardcoded per call site — one resolution rule, reused.
141
+ */
142
+ export declare function intuticProxyUrl(): string;
143
+ /**
144
+ * Wraps a provider-constructing function so it points at the Intutic proxy.
145
+ *
146
+ * The Vercel AI SDK has no env-var base-URL override (see module doc), so
147
+ * routing LLM traffic through the proxy is IN-CODE: pass the provider
148
+ * factory you would otherwise call directly (`createOpenAI`, `createGateway`,
149
+ * any `@ai-sdk/*` `create*` export or compatible custom factory that accepts
150
+ * a `{ baseURL }`-shaped options object) and get back one pre-configured to
151
+ * call the proxy instead of the provider's real endpoint:
152
+ *
153
+ * ```ts
154
+ * import { createOpenAI } from '@ai-sdk/openai'
155
+ * import { withIntuticProxy } from '@intutic/gate/vercel'
156
+ *
157
+ * const openai = withIntuticProxy(createOpenAI)({ apiKey: process.env.OPENAI_API_KEY })
158
+ * const model = openai('gpt-5-mini')
159
+ * ```
160
+ *
161
+ * `baseUrl` overrides the resolved proxy URL (see {@link intuticProxyUrl});
162
+ * omit it to use `INTUTIC_PROXY_URL`/the local default, same as every other
163
+ * harness in this codebase.
164
+ *
165
+ * This does NOT make Vercel AI SDK integration "zero-code": every provider
166
+ * construction call site in the caller's application must go through this
167
+ * wrapper (or otherwise set `baseURL` itself) for LLM egress to be governed
168
+ * — there is no ambient env var this package (or any other) can set to
169
+ * achieve the same effect for this framework.
170
+ */
171
+ export declare function withIntuticProxy<TOptions extends {
172
+ baseURL?: string;
173
+ }, TProvider>(providerFactory: (options: TOptions) => TProvider, baseUrl?: string): (options: TOptions) => TProvider;
174
+ //# sourceMappingURL=vercel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../src/vercel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AAEH,OAAO,EAAwB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAG3D;;;0CAG0C;AAC1C,MAAM,MAAM,wBAAwB,GAChC,SAAS,GACT,gBAAgB,GAChB,UAAU,GACV,QAAQ,GACR,eAAe,GACf;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,CAAA;AAEjD;wEACwE;AACxE,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf;AAED;;;;qCAIqC;AACrC,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,cAAc,CAAA;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED;8BAC8B;AAC9B,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,wBAAwB,CAAC,CAAA;AAEtC,MAAM,WAAW,0BAA0B;IACzC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,IAAI,CAAA;CACZ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,0BAA+B,GAAG,kBAAkB,CAmB7F;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,SAAS;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EACrC,SAAS,EACT,eAAe,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,CAGvG"}
package/dist/vercel.js ADDED
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Vercel AI SDK adapter: `intuticToolApproval()` and `withIntuticProxy()`.
3
+ *
4
+ * `@intutic/gate/vercel` — 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
+ *
8
+ * The `ai` package (v6+) exposes a documented pre-execution veto point: a
9
+ * `toolApproval` callback accepted by `generateText`/`streamText` and
10
+ * `ToolLoopAgent`. It runs once per tool call, before execution, and its
11
+ * return value decides whether the call proceeds.
12
+ *
13
+ * ## Confirmed against a real install
14
+ *
15
+ * `ai@7.0.68` + `@ai-sdk/openai@4.0.43` were installed in a scratch directory
16
+ * and `node_modules/ai/dist/index.d.ts` read directly (not inferred from
17
+ * docs alone). The relevant types:
18
+ *
19
+ * ```ts
20
+ * type ToolApprovalStatus =
21
+ * | undefined | 'not-applicable' | 'approved' | 'denied' | 'user-approval'
22
+ * | { type: 'not-applicable'; reason?: never }
23
+ * | { type: 'approved'; reason?: string }
24
+ * | { type: 'denied'; reason?: string }
25
+ * | { type: 'user-approval'; reason?: never }
26
+ *
27
+ * type GenericToolApprovalFunction<TOOLS, TOOLS_CONTEXT, RUNTIME_CONTEXT> = (options: {
28
+ * toolCall: TypedToolCall<TOOLS> // { toolName: string; input: ... ; ... }
29
+ * tools: TOOLS | undefined
30
+ * toolsContext: TOOLS_CONTEXT
31
+ * runtimeContext: RUNTIME_CONTEXT
32
+ * messages: ModelMessage[]
33
+ * }) => MaybePromiseLike<ToolApprovalStatus>
34
+ *
35
+ * type ToolApprovalConfiguration<TOOLS, RUNTIME_CONTEXT> =
36
+ * GenericToolApprovalFunction<...> | { [toolName]?: ToolApprovalStatus | SingleToolApprovalFunction<...> }
37
+ * ```
38
+ * (`generateText`/`streamText`/`ToolLoopAgent` all accept
39
+ * `toolApproval?: ToolApprovalConfiguration<TOOLS, RUNTIME_CONTEXT>`.)
40
+ *
41
+ * `'not-applicable'` (also spelled `{ type: 'not-applicable' }`, and
42
+ * `undefined` is treated the same) means "this gate has no opinion, proceed
43
+ * normally" — exactly matching the plan's description. `'denied'` (or
44
+ * `{ type: 'denied', reason }`) vetoes the call before execution; the SDK
45
+ * hands `reason` back to the model in place of a tool result. This adapter
46
+ * implements `GenericToolApprovalFunction`, the whole-config form (not the
47
+ * per-tool-keyed form), so a single `intuticToolApproval()` call covers every
48
+ * tool in `tools`.
49
+ *
50
+ * ## Known, documented limitation — no env-var LLM routing
51
+ *
52
+ * Unlike almost every other harness in this codebase (which honour
53
+ * `OPENAI_BASE_URL`/`ANTHROPIC_BASE_URL` or an equivalent), the Vercel AI SDK
54
+ * has NO environment-variable-based base-URL override mechanism. Routing LLM
55
+ * traffic through the Intutic proxy requires IN-CODE provider construction —
56
+ * `createOpenAI({ baseURL })`, `createGateway({ baseURL })`, etc. — not a
57
+ * sourced `.env.intutic` file the way `langchainAdapter`/`langgraphAdapter`
58
+ * and the rest of the SDK-gated family work. `withIntuticProxy()` below is a
59
+ * small factory wrapper for that in-code construction; it does not — and
60
+ * cannot — make this "zero-code" the way an env-var-honouring harness's
61
+ * integration correctly can claim to be. See
62
+ * `apps/docs/integrations/vercel-ai-sdk.md` for the operator-facing version
63
+ * of this same limitation, stated plainly rather than oversold.
64
+ */
65
+ import { active as activeGate } from './gate.js';
66
+ import { IntuticGateRefusal } from './errors.js';
67
+ /**
68
+ * Builds a `toolApproval` callback matching `ai`'s `GenericToolApprovalFunction`
69
+ * signature, for direct use on `generateText`/`streamText`/`ToolLoopAgent`:
70
+ *
71
+ * ```ts
72
+ * import { generateText } from 'ai'
73
+ * import { Gate, install } from '@intutic/gate'
74
+ * import { intuticToolApproval } from '@intutic/gate/vercel'
75
+ *
76
+ * install(new Gate({ workspaceId: process.env.INTUTIC_WORKSPACE_ID }))
77
+ *
78
+ * await generateText({
79
+ * model,
80
+ * tools,
81
+ * toolApproval: intuticToolApproval(),
82
+ * })
83
+ * ```
84
+ *
85
+ * Resolves to `'not-applicable'` on allow (this gate has no opinion; any
86
+ * other approval mechanism the caller has configured still applies) and to
87
+ * `{ type: 'denied', reason }` on refusal — `reason` is the
88
+ * `[Intutic Governance] BLOCKED: ...` message, handed back to the model in
89
+ * place of a tool result.
90
+ *
91
+ * Requires a `Gate` — either passed via `{ gate }` here, or installed
92
+ * process-wide with `install(new Gate(...))`. A call with neither throws,
93
+ * refusing to run the tool unguarded rather than silently skipping
94
+ * enforcement — same posture as `wrapTool`/`intuticHooks`.
95
+ */
96
+ export function intuticToolApproval(opts = {}) {
97
+ return async ({ toolCall }) => {
98
+ const g = opts.gate ?? activeGate();
99
+ if (g === null) {
100
+ throw new Error('No gate configured: call install(new Gate(...)) from @intutic/gate, or pass ' +
101
+ '{ gate } to intuticToolApproval(). Refusing to run the tool unguarded.');
102
+ }
103
+ try {
104
+ await g.guard(toolCall.toolName, (toolCall.input ?? {}));
105
+ return 'not-applicable';
106
+ }
107
+ catch (exc) {
108
+ if (exc instanceof IntuticGateRefusal) {
109
+ return { type: 'denied', reason: exc.message };
110
+ }
111
+ throw exc;
112
+ }
113
+ };
114
+ }
115
+ /**
116
+ * Resolves the Intutic proxy URL the same way the rest of this codebase does
117
+ * — `INTUTIC_PROXY_URL`, falling back to the local default every CLI command
118
+ * and env-writer here shares (`tools/cli/src/commands/exec.ts`,
119
+ * `tools/cli/src/lib/onboarding.ts`, `services/sync-daemon/src/agentReporter.ts`).
120
+ * Not re-hardcoded per call site — one resolution rule, reused.
121
+ */
122
+ export function intuticProxyUrl() {
123
+ return process.env.INTUTIC_PROXY_URL || 'http://localhost:4000';
124
+ }
125
+ /**
126
+ * Wraps a provider-constructing function so it points at the Intutic proxy.
127
+ *
128
+ * The Vercel AI SDK has no env-var base-URL override (see module doc), so
129
+ * routing LLM traffic through the proxy is IN-CODE: pass the provider
130
+ * factory you would otherwise call directly (`createOpenAI`, `createGateway`,
131
+ * any `@ai-sdk/*` `create*` export or compatible custom factory that accepts
132
+ * a `{ baseURL }`-shaped options object) and get back one pre-configured to
133
+ * call the proxy instead of the provider's real endpoint:
134
+ *
135
+ * ```ts
136
+ * import { createOpenAI } from '@ai-sdk/openai'
137
+ * import { withIntuticProxy } from '@intutic/gate/vercel'
138
+ *
139
+ * const openai = withIntuticProxy(createOpenAI)({ apiKey: process.env.OPENAI_API_KEY })
140
+ * const model = openai('gpt-5-mini')
141
+ * ```
142
+ *
143
+ * `baseUrl` overrides the resolved proxy URL (see {@link intuticProxyUrl});
144
+ * omit it to use `INTUTIC_PROXY_URL`/the local default, same as every other
145
+ * harness in this codebase.
146
+ *
147
+ * This does NOT make Vercel AI SDK integration "zero-code": every provider
148
+ * construction call site in the caller's application must go through this
149
+ * wrapper (or otherwise set `baseURL` itself) for LLM egress to be governed
150
+ * — there is no ambient env var this package (or any other) can set to
151
+ * achieve the same effect for this framework.
152
+ */
153
+ export function withIntuticProxy(providerFactory, baseUrl) {
154
+ const url = baseUrl ?? intuticProxyUrl();
155
+ return (options) => providerFactory({ ...options, baseURL: url });
156
+ }
157
+ //# sourceMappingURL=vercel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../src/vercel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AAEH,OAAO,EAAE,MAAM,IAAI,UAAU,EAAa,MAAM,WAAW,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AA6ChD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAmC,EAAE;IACvE,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,UAAU,EAAE,CAAA;QACnC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,8EAA8E;gBAC5E,wEAAwE,CAC3E,CAAA;QACH,CAAC;QACD,IAAI,CAAC;YACH,MAAM,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAC,CAAA;YACnF,OAAO,gBAAgB,CAAA;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,kBAAkB,EAAE,CAAC;gBACtC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,CAAA;YAChD,CAAC;YACD,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,uBAAuB,CAAA;AACjE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,gBAAgB,CAG9B,eAAiD,EAAE,OAAgB;IACnE,MAAM,GAAG,GAAG,OAAO,IAAI,eAAe,EAAE,CAAA;IACxC,OAAO,CAAC,OAAiB,EAAa,EAAE,CAAC,eAAe,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAA;AACxF,CAAC"}
@@ -0,0 +1,209 @@
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 { type Gate } from './gate.js';
62
+ import { IntuticGateRefusal } from './errors.js';
63
+ import { type AnyFn, type ExecutableTool } 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 declare class IntuticWorkflowRefusal extends IntuticGateRefusal {
78
+ readonly fatal: true;
79
+ constructor(reason: string, code: string, incidentId?: string);
80
+ /** Rewrap a core refusal so the durable runtime treats it as fatal. */
81
+ static from(refusal: IntuticGateRefusal): IntuticWorkflowRefusal;
82
+ /**
83
+ * Cross-realm-safe detection (the same trick `FatalError.is` itself uses):
84
+ * `instanceof` fails across the workflow `vm` boundary, so this checks the
85
+ * duck-typed name plus this package's own message prefix.
86
+ */
87
+ static is(value: unknown): boolean;
88
+ }
89
+ /** Structural copy of the options object `@ai-sdk/workflow`'s agent loop
90
+ * passes to a tool's `needsApproval` function (confirmed from the compiled
91
+ * loop: `needsApproval(tc.input, { toolCallId, messages, context })`). Only
92
+ * the fields this adapter could ever read are named; the index signature
93
+ * keeps the function assignable to the real (wider) option type. */
94
+ export interface WorkflowNeedsApprovalOptions {
95
+ toolCallId: string;
96
+ [key: string]: unknown;
97
+ }
98
+ /** The `needsApproval` function shape this adapter produces — assignable to
99
+ * `ai`'s tool-level `needsApproval` (the surface `@ai-sdk/workflow`'s agent
100
+ * loop actually consults). */
101
+ export type WorkflowNeedsApproval = (input: unknown, options: WorkflowNeedsApprovalOptions) => Promise<boolean>;
102
+ export interface IntuticNeedsApprovalOptions {
103
+ /** Overrides the process-wide installed gate (`install(new Gate(...))`). */
104
+ gate?: Gate;
105
+ /**
106
+ * What an ALLOWED call maps to. `needsApproval` answers "does a human need
107
+ * to approve this?", which is a different question from "may this run?" —
108
+ * so an allow has two honest encodings and this option picks one:
109
+ *
110
+ * - `'auto'` (default): resolve `false` — the gate evaluated the call and
111
+ * allowed it; no human pause. Denials never reach this mapping at all
112
+ * (they THROW — see below), so `false` here never launders a block.
113
+ * - `'human'`: resolve `true` — the gate allowed it AND a human still
114
+ * approves, using the framework's own durable pause (approvable hours
115
+ * later). Belt-and-braces for high-stakes tools.
116
+ *
117
+ * A BLOCKED call takes neither value: it throws
118
+ * {@link IntuticWorkflowRefusal}. Returning `true` for a denial would hand
119
+ * the decision to a human approver as though the gate had no verdict, and
120
+ * returning `false` would RUN the tool — the one thing a block must never
121
+ * do.
122
+ */
123
+ onAllow?: 'auto' | 'human';
124
+ }
125
+ /**
126
+ * Build an async `needsApproval` function for ONE workflow tool:
127
+ *
128
+ * ```ts
129
+ * import { WorkflowAgent } from '@ai-sdk/workflow'
130
+ * import { Gate, install } from '@intutic/gate'
131
+ * import { intuticNeedsApproval } from '@intutic/gate/workflow'
132
+ *
133
+ * install(new Gate({ workspaceId: process.env.INTUTIC_WORKSPACE_ID }))
134
+ *
135
+ * const agent = new WorkflowAgent({
136
+ * model,
137
+ * tools: {
138
+ * deployService: {
139
+ * description: '...',
140
+ * inputSchema,
141
+ * execute: deployStep,
142
+ * needsApproval: intuticNeedsApproval('deployService'),
143
+ * },
144
+ * },
145
+ * })
146
+ * ```
147
+ *
148
+ * The tool name is a parameter because the framework's `needsApproval`
149
+ * signature does not carry it — `(input, { toolCallId, messages, context })`
150
+ * has no `toolName` field (confirmed against the shipped types). Use
151
+ * {@link withIntuticApproval} to attach this to a whole tools record without
152
+ * hand-repeating each name.
153
+ *
154
+ * Verdict mapping: BLOCK → throws {@link IntuticWorkflowRefusal} (fatal —
155
+ * aborts instead of retry-looping; see module doc); ALLOW → `false` or `true`
156
+ * per `onAllow`. A non-refusal crash in the gate is re-thrown untouched
157
+ * (retryable — correct for transients).
158
+ */
159
+ export declare function intuticNeedsApproval(toolName: string, opts?: IntuticNeedsApprovalOptions): WorkflowNeedsApproval;
160
+ /** The slice of a workflow tool definition this adapter reads/replaces.
161
+ * Structurally compatible with `ai`'s `Tool` (whose `needsApproval` is
162
+ * `boolean | ToolNeedsApprovalFunction<...>`). */
163
+ export interface WorkflowToolLike {
164
+ needsApproval?: boolean | ((input: never, options: never) => unknown);
165
+ [key: string]: unknown;
166
+ }
167
+ /**
168
+ * Attach the Intutic gate to every tool in a record, composing with any
169
+ * `needsApproval` the tool already declares:
170
+ *
171
+ * ```ts
172
+ * const agent = new WorkflowAgent({ model, tools: withIntuticApproval(tools) })
173
+ * ```
174
+ *
175
+ * For each tool (the record KEY is the tool name reported to the gate — the
176
+ * same identity the framework itself dispatches on):
177
+ *
178
+ * 1. `Gate.guard()` runs first. BLOCK throws {@link IntuticWorkflowRefusal}.
179
+ * 2. On allow, the tool's OWN prior `needsApproval` still applies: a
180
+ * boolean is returned as-is; a function is awaited with the original
181
+ * arguments; a tool with none falls back to `onAllow` ('auto' → false).
182
+ *
183
+ * So a tool that already said "always ask a human" (`needsApproval: true`)
184
+ * keeps asking a human — this gate only ADDS the ability to refuse outright,
185
+ * it never removes an approval pause the caller had configured.
186
+ *
187
+ * Returns a NEW record of NEW tool objects (shallow copies); the input record
188
+ * and its tools are not mutated.
189
+ */
190
+ export declare function withIntuticApproval<T extends Record<string, WorkflowToolLike>>(tools: T, opts?: IntuticNeedsApprovalOptions): T;
191
+ /**
192
+ * `wrapTools` for durable workflow tool definitions — the same generic
193
+ * execute-wrapping `wrapTools.ts` provides (gate first, then the real body),
194
+ * with ONE workflow-specific change: a refusal thrown out of the wrapped
195
+ * `execute` is rewrapped as {@link IntuticWorkflowRefusal}, because a
196
+ * workflow tool's `execute` is a durable STEP and a step that throws a
197
+ * non-fatal error is retried (see module doc). Everything else — including
198
+ * "no gate configured" handling and the record-key-is-tool-name convention —
199
+ * is `wrapTools`'s own behaviour, reused rather than reimplemented.
200
+ *
201
+ * Prefer {@link withIntuticApproval} as the primary integration: it uses the
202
+ * framework's own pre-execution veto surface, refuses BEFORE the durable
203
+ * step ever starts, and keeps the human-approval lane available. This
204
+ * execute-level wrapper is defense in depth (or the option for callers whose
205
+ * tools are shared with non-workflow code paths); running both double-guards
206
+ * the call, which is harmless but emits duplicate gate telemetry.
207
+ */
208
+ export declare function wrapWorkflowTools<T extends AnyFn | ExecutableTool>(tools: Record<string, T>, gate?: Gate): Record<string, T>;
209
+ //# sourceMappingURL=workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,OAAO,EAAwB,KAAK,IAAI,EAAkB,MAAM,WAAW,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAa,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE3E;;;;;;;;;;;;GAYG;AACH,qBAAa,sBAAuB,SAAQ,kBAAkB;IAC5D,SAAgB,KAAK,EAAG,IAAI,CAAS;gBACzB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;IAK7D,uEAAuE;IACvE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,sBAAsB;IAIhE;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;CASnC;AAED;;;;qEAIqE;AACrE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAA;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED;;+BAE+B;AAC/B,MAAM,MAAM,qBAAqB,GAAG,CAClC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,4BAA4B,KAClC,OAAO,CAAC,OAAO,CAAC,CAAA;AAErB,MAAM,WAAW,2BAA2B;IAC1C,4EAA4E;IAC5E,IAAI,CAAC,EAAE,IAAI,CAAA;IACX;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B;AAyBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,2BAAgC,GACrC,qBAAqB,CAcvB;AAED;;mDAEmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,KAAK,OAAO,CAAC,CAAA;IACrE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAC5E,KAAK,EAAE,CAAC,EACR,IAAI,GAAE,2BAAgC,GACrC,CAAC,CAiBH;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,KAAK,GAAG,cAAc,EAChE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EACxB,IAAI,CAAC,EAAE,IAAI,GACV,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAWnB"}