@intutic/gate 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +261 -0
- package/dist/actions.d.ts +86 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +239 -0
- package/dist/actions.js.map +1 -0
- package/dist/client.d.ts +94 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +207 -0
- package/dist/client.js.map +1 -0
- package/dist/dsh.d.ts +162 -0
- package/dist/dsh.d.ts.map +1 -0
- package/dist/dsh.js +189 -0
- package/dist/dsh.js.map +1 -0
- package/dist/errors.d.ts +41 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +53 -0
- package/dist/errors.js.map +1 -0
- package/dist/eve.d.ts +425 -0
- package/dist/eve.d.ts.map +1 -0
- package/dist/eve.js +378 -0
- package/dist/eve.js.map +1 -0
- package/dist/gate.d.ts +113 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +274 -0
- package/dist/gate.js.map +1 -0
- package/dist/harness.d.ts +482 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +504 -0
- package/dist/harness.js.map +1 -0
- package/dist/headers.d.ts +26 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +30 -0
- package/dist/headers.js.map +1 -0
- package/dist/imagecheck.d.ts +93 -0
- package/dist/imagecheck.d.ts.map +1 -0
- package/dist/imagecheck.js +286 -0
- package/dist/imagecheck.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/managedAgents.d.ts +375 -0
- package/dist/managedAgents.d.ts.map +1 -0
- package/dist/managedAgents.js +446 -0
- package/dist/managedAgents.js.map +1 -0
- package/dist/mastra.d.ts +175 -0
- package/dist/mastra.d.ts.map +1 -0
- package/dist/mastra.js +161 -0
- package/dist/mastra.js.map +1 -0
- package/dist/openai.d.ts +392 -0
- package/dist/openai.d.ts.map +1 -0
- package/dist/openai.js +658 -0
- package/dist/openai.js.map +1 -0
- package/dist/shlex.d.ts +16 -0
- package/dist/shlex.d.ts.map +1 -0
- package/dist/shlex.js +78 -0
- package/dist/shlex.js.map +1 -0
- package/dist/snapshot.d.ts +111 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +243 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/soprules.d.ts +88 -0
- package/dist/soprules.d.ts.map +1 -0
- package/dist/soprules.js +176 -0
- package/dist/soprules.js.map +1 -0
- package/dist/trueforge.d.ts +191 -0
- package/dist/trueforge.d.ts.map +1 -0
- package/dist/trueforge.js +184 -0
- package/dist/trueforge.js.map +1 -0
- package/dist/vercel.d.ts +174 -0
- package/dist/vercel.d.ts.map +1 -0
- package/dist/vercel.js +157 -0
- package/dist/vercel.js.map +1 -0
- package/dist/workflow.d.ts +209 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +254 -0
- package/dist/workflow.js.map +1 -0
- package/dist/wrapTools.d.ts +68 -0
- package/dist/wrapTools.d.ts.map +1 -0
- package/dist/wrapTools.js +95 -0
- package/dist/wrapTools.js.map +1 -0
- package/package.json +123 -0
package/dist/soprules.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SOP rules authored in the product, enforced before execution.
|
|
3
|
+
*
|
|
4
|
+
* Port of `packages/intutic-clawde/intutic_clawde/gate/soprules.py`.
|
|
5
|
+
*
|
|
6
|
+
* `SopRule` has carried an optional `argPattern` since mcp-proxy shipped, and
|
|
7
|
+
* `packages/mcp-proxy/src/policy.ts` matches it against the serialised tool
|
|
8
|
+
* input. `intutic/intutic-enterprise#14` added a ` WHERE ` clause to the SOP
|
|
9
|
+
* title grammar, so a rule can finally say "kubectl apply without a digest"
|
|
10
|
+
* instead of only "any shell call":
|
|
11
|
+
*
|
|
12
|
+
* BLOCK:^shell$ WHERE kubectl\s+apply(?!.*@sha256:):deploy must be pinned
|
|
13
|
+
*
|
|
14
|
+
* Now the *policy* lives in the SOP register — versioned, lifecycle-gated,
|
|
15
|
+
* visible on the dashboard, editable without touching agent code — and this
|
|
16
|
+
* module is only the part that applies it.
|
|
17
|
+
*
|
|
18
|
+
* Matching mirrors `matchSopRule` in
|
|
19
|
+
* `services/control-plane/src/lib/sopRuleTitle.ts` (itself a deliberate
|
|
20
|
+
* line-for-line mirror of `PolicyClient.matchRule` in
|
|
21
|
+
* `packages/mcp-proxy/src/policy.ts`), deliberately:
|
|
22
|
+
*
|
|
23
|
+
* * rules are tried in the order the control plane returned them
|
|
24
|
+
* * first match wins
|
|
25
|
+
* * `toolPattern` is tested against the tool name, `argPattern` against the
|
|
26
|
+
* serialised tool input
|
|
27
|
+
* * the serialisation is the `JSON.stringify(tool_input ?? {})` shape every
|
|
28
|
+
* mirror matches against — compact separators, insertion order, non-ASCII
|
|
29
|
+
* left intact
|
|
30
|
+
* * a rule whose regex does not compile is SKIPPED, not fatal
|
|
31
|
+
*
|
|
32
|
+
* Fetch failure is non-fatal for the same reason it is in `policy.ts`: the
|
|
33
|
+
* gate's image-integrity tier independently covers the deploy case and fails
|
|
34
|
+
* CLOSED. This tier changes where the policy is *authored*, not whether the
|
|
35
|
+
* run is safe.
|
|
36
|
+
*/
|
|
37
|
+
export const FETCH_TIMEOUT_MS = 3_000;
|
|
38
|
+
// Actions the control plane can return. Anything else is ignored rather than
|
|
39
|
+
// guessed at — an unrecognised action must not silently become "allow".
|
|
40
|
+
export const ACTION_BLOCK = 'block';
|
|
41
|
+
export const ACTION_WARN = 'warn';
|
|
42
|
+
export const ACTION_APPROVAL = 'require_approval';
|
|
43
|
+
const KNOWN_ACTIONS = new Set([ACTION_BLOCK, ACTION_WARN, ACTION_APPROVAL]);
|
|
44
|
+
/** Mirror of `matchSopRule`, including its silent-skip on bad regex. */
|
|
45
|
+
export function ruleMatches(rule, toolName, toolInputJson) {
|
|
46
|
+
try {
|
|
47
|
+
if (!new RegExp(rule.toolPattern).test(toolName))
|
|
48
|
+
return false;
|
|
49
|
+
if (rule.argPattern !== null) {
|
|
50
|
+
return new RegExp(rule.argPattern).test(toolInputJson);
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Malformed regex in rule — skip silently, exactly as the control plane
|
|
56
|
+
// and mcp-proxy mirrors do.
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The `JSON.stringify(tool_input ?? {})` shape every mirror matches against.
|
|
62
|
+
*
|
|
63
|
+
* `JSON.stringify` already produces compact separators and preserves
|
|
64
|
+
* insertion order and non-ASCII by default — this function exists mainly so
|
|
65
|
+
* every call site names the contract explicitly rather than calling
|
|
66
|
+
* `JSON.stringify` inline and drifting on a future edit.
|
|
67
|
+
*/
|
|
68
|
+
export function serialiseToolInput(toolInput) {
|
|
69
|
+
return JSON.stringify(toolInput ?? {});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Accept the rule list in whichever envelope the control plane used.
|
|
73
|
+
*
|
|
74
|
+
* `/api/v1/sop/rules` returns `{rules:[...]}`, while sibling list endpoints
|
|
75
|
+
* variously return `{items:[...]}` and `{data:[...]}`. Reading the wrong key
|
|
76
|
+
* yields zero rules and a tier that enforces nothing while looking healthy —
|
|
77
|
+
* the worst failure a gate can have — so all three are accepted rather than
|
|
78
|
+
* assumed.
|
|
79
|
+
*/
|
|
80
|
+
export function parseRules(payload) {
|
|
81
|
+
let rows;
|
|
82
|
+
if (Array.isArray(payload)) {
|
|
83
|
+
rows = payload;
|
|
84
|
+
}
|
|
85
|
+
else if (payload !== null && typeof payload === 'object') {
|
|
86
|
+
const obj = payload;
|
|
87
|
+
const candidate = obj.rules ?? obj.items ?? obj.data ?? [];
|
|
88
|
+
rows = Array.isArray(candidate) ? candidate : [];
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const row of rows) {
|
|
95
|
+
if (row === null || typeof row !== 'object')
|
|
96
|
+
continue;
|
|
97
|
+
const r = row;
|
|
98
|
+
const toolPattern = r.toolPattern ?? r.tool_pattern;
|
|
99
|
+
const action = String(r.action ?? '').toLowerCase();
|
|
100
|
+
if (typeof toolPattern !== 'string' || !toolPattern)
|
|
101
|
+
continue;
|
|
102
|
+
if (!KNOWN_ACTIONS.has(action))
|
|
103
|
+
continue;
|
|
104
|
+
const arg = r.argPattern ?? r.arg_pattern;
|
|
105
|
+
out.push({
|
|
106
|
+
id: String(r.id ?? r.sopId ?? 'sop'),
|
|
107
|
+
toolPattern,
|
|
108
|
+
action,
|
|
109
|
+
reason: String(r.reason ?? `SOP rule ${toolPattern}`),
|
|
110
|
+
argPattern: typeof arg === 'string' && arg ? arg : null,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Fetch active VALIDATED rules, or `null` if they could not be fetched.
|
|
117
|
+
*
|
|
118
|
+
* `null` and `[]` mean different things and the caller must not conflate
|
|
119
|
+
* them: `null` is "the register did not answer", `[]` is "the register
|
|
120
|
+
* answered and this workspace has no rules". Only the second is a statement
|
|
121
|
+
* about policy.
|
|
122
|
+
*/
|
|
123
|
+
export async function fetchRules(baseUrl, apiKey, workspaceId, timeoutMs = FETCH_TIMEOUT_MS) {
|
|
124
|
+
const qs = new URLSearchParams({ workspaceId, active: 'true' });
|
|
125
|
+
const controller = new AbortController();
|
|
126
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
127
|
+
try {
|
|
128
|
+
const res = await fetch(`${baseUrl.replace(/\/$/, '')}/api/v1/sop/rules?${qs.toString()}`, {
|
|
129
|
+
method: 'GET',
|
|
130
|
+
headers: {
|
|
131
|
+
Authorization: `Bearer ${apiKey}`,
|
|
132
|
+
'X-Workspace-Id': workspaceId,
|
|
133
|
+
},
|
|
134
|
+
signal: controller.signal,
|
|
135
|
+
});
|
|
136
|
+
if (!res.ok)
|
|
137
|
+
return null;
|
|
138
|
+
const raw = await res.text();
|
|
139
|
+
return parseRules(raw ? JSON.parse(raw) : {});
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* True when the control plane emitted at least one usable `argPattern`.
|
|
150
|
+
*
|
|
151
|
+
* A control plane predating `intutic-enterprise#14` cannot produce one, so
|
|
152
|
+
* this doubles as a deployment probe — surface it rather than letting the
|
|
153
|
+
* tier be silently inert.
|
|
154
|
+
*/
|
|
155
|
+
export function supportsArgPatterns(rules) {
|
|
156
|
+
return rules.some((r) => r.argPattern !== null && compiles(r.argPattern));
|
|
157
|
+
}
|
|
158
|
+
function compiles(pattern) {
|
|
159
|
+
try {
|
|
160
|
+
new RegExp(pattern);
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/** First rule matching this call, in control-plane order. */
|
|
168
|
+
export function firstMatch(rules, toolName, toolInput) {
|
|
169
|
+
const payload = serialiseToolInput(toolInput);
|
|
170
|
+
for (const rule of rules) {
|
|
171
|
+
if (ruleMatches(rule, toolName, payload))
|
|
172
|
+
return rule;
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=soprules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"soprules.js","sourceRoot":"","sources":["../src/soprules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TrueForge (embedded) adapter: `intuticApprovalResponder()`.
|
|
3
|
+
*
|
|
4
|
+
* `@intutic/gate/trueforge` — Phase 1 of TrueForge support: another team's
|
|
5
|
+
* Node process that imports `@truefoundry/trueforge-core` directly
|
|
6
|
+
* (github.com/truefoundry/trueforge, MIT) and drives its own
|
|
7
|
+
* `SessionHandle`/`TurnHandle` API in-process. Same family idea as
|
|
8
|
+
* `mastra.ts`/`vercel.ts` (wrap the framework's own veto point around this
|
|
9
|
+
* package's `Gate.guard()`), but TrueForge's veto point is shaped
|
|
10
|
+
* differently from either of those, so this module follows `harness.ts`'s
|
|
11
|
+
* `intuticApprovalResponder()` precedent instead — a batch function
|
|
12
|
+
* answering pending approvals — not a single-call `beforeToolCall`/
|
|
13
|
+
* `toolApproval` option.
|
|
14
|
+
*
|
|
15
|
+
* Phase 2 (`HarnessType.TRUEFORGE_SERVER` — TrueForge run as its own
|
|
16
|
+
* standalone/hosted server, e.g. `npx @truefoundry/trueforge`, Docker
|
|
17
|
+
* Compose, or the Helm chart) is explicitly OUT OF SCOPE here. Nobody embeds
|
|
18
|
+
* an Intutic gate into a third-party OSS server process; that deployment
|
|
19
|
+
* mode needs governance running in an external Intutic-operated bridge
|
|
20
|
+
* service — separate, larger work, not attempted in this module.
|
|
21
|
+
*
|
|
22
|
+
* ## Confirmed against the real published package, not assumed
|
|
23
|
+
*
|
|
24
|
+
* `@truefoundry/trueforge-core@0.1.4` (`latest` on npm at the time this was
|
|
25
|
+
* written) was downloaded from the npm registry and its shipped `.d.ts`
|
|
26
|
+
* files read directly — not inferred from docs alone:
|
|
27
|
+
*
|
|
28
|
+
* - There is NO synchronous, in-process "approval resolver" callback
|
|
29
|
+
* anywhere in the package. Every shipped `.d.ts` file was grepped for
|
|
30
|
+
* `approvalResolver`/`onApproval`/`approvalHandler`/`resolveApproval`:
|
|
31
|
+
* zero matches. `ITurnResourceResolver`
|
|
32
|
+
* (`dist/agent-session/ITurnResourceResolver.d.ts`) resolves sandboxes,
|
|
33
|
+
* agent definitions, and tracing per run — it carries no tool-call veto
|
|
34
|
+
* hook of any kind.
|
|
35
|
+
* - The ONLY approval mechanism, embedded or not, is the same turn/event
|
|
36
|
+
* contract TrueForge's standalone server exposes over HTTP: a turn
|
|
37
|
+
* pauses with a `required_actions` entry of type `tool.approval_required`
|
|
38
|
+
* (`EventType.TOOL_APPROVAL_REQUIRED`, `dist/agent-session/schemas/
|
|
39
|
+
* events.d.ts`), naming each pending call only as `{id, source_event_id}`
|
|
40
|
+
* — no tool name or arguments travel on the event itself. Answering it
|
|
41
|
+
* means constructing a NEW turn (`SessionHandle.createTurn()`) whose
|
|
42
|
+
* `input` carries a `user.tool_approval` item:
|
|
43
|
+
* `{type:'user.tool_approval', thread_id, tool_call_id, approval}`,
|
|
44
|
+
* where `approval` is `{status:'allow'} | {status:'deny', reason?:
|
|
45
|
+
* string}` (`ApprovalDecisionSchema`, `dist/core/events/schema.d.ts`) —
|
|
46
|
+
* confirmed field-for-field, and pinned by this module's own test file
|
|
47
|
+
* via a structural assignability check against the real exported
|
|
48
|
+
* `TurnInputItem` (`@truefoundry/trueforge-core/agent-session`) and
|
|
49
|
+
* `ApprovalDecision` (`@truefoundry/trueforge-core/core`) types.
|
|
50
|
+
*
|
|
51
|
+
* So unlike `mastra.ts`/`vercel.ts`, this adapter cannot BE "the function you
|
|
52
|
+
* pass as a framework option" — embedded TrueForge has no such option. What
|
|
53
|
+
* it can be, and is, is the `harness.ts`-shaped piece: a function that
|
|
54
|
+
* evaluates a batch of pending approval requests through `Gate.guard()` and
|
|
55
|
+
* returns the `user.tool_approval` `TurnInputItem[]` ready to hand to your
|
|
56
|
+
* next `session.createTurn({ input, previous_turn_id })` call.
|
|
57
|
+
*
|
|
58
|
+
* ## What the embedding host still has to do itself
|
|
59
|
+
*
|
|
60
|
+
* Resolving a `{id, source_event_id}` tool-call reference back to the real
|
|
61
|
+
* tool NAME and ARGUMENTS is the host's job, not this adapter's: the host
|
|
62
|
+
* already owns the `TurnHandle`/event stream (`turn.stream()`/
|
|
63
|
+
* `listEvents()`) this package has no reason to depend on, and
|
|
64
|
+
* `source_event_id` points at a `model.message` event whose tool-call wire
|
|
65
|
+
* shape this package deliberately does not import — matching
|
|
66
|
+
* `mastra.ts`/`vercel.ts`'s existing convention of zero runtime dependency
|
|
67
|
+
* on the framework being gated. Build the `TrueforgeApprovalRequest[]` this
|
|
68
|
+
* responder consumes from whatever your own event-resolution loop already
|
|
69
|
+
* extracts, then feed the returned items straight into your next
|
|
70
|
+
* `createTurn()` call.
|
|
71
|
+
*
|
|
72
|
+
* ## GUESSED, clearly marked as such: the exact orchestration wiring
|
|
73
|
+
*
|
|
74
|
+
* The plan that authored this phase asked to pin the exact registration API
|
|
75
|
+
* against `TurnHandle`/`ITurnResourceResolver`; having read both real files,
|
|
76
|
+
* NEITHER carries a registration point for this at all — the orchestration
|
|
77
|
+
* loop (drive `turn.stream()`, detect a `tool.approval_required` required
|
|
78
|
+
* action, resolve each `source_event_id`, call this responder, call
|
|
79
|
+
* `createTurn()` again with the answers) is necessarily HAND-WRITTEN
|
|
80
|
+
* application code in the embedding host, not something a config option
|
|
81
|
+
* turns on. That loop's exact shape — how you persist the paused turn id,
|
|
82
|
+
* how you resolve `source_event_id`, whether you drain `stream()` to
|
|
83
|
+
* completion before or after answering — is a genuine judgment call for the
|
|
84
|
+
* embedding host's own architecture and is NOT prescribed here. See
|
|
85
|
+
* `apps/docs/integrations/trueforge.md` for a worked (illustrative, not
|
|
86
|
+
* load-bearing) example of one way to wire this loop.
|
|
87
|
+
*
|
|
88
|
+
* @module
|
|
89
|
+
*/
|
|
90
|
+
import { type Gate } from './gate.js';
|
|
91
|
+
/**
|
|
92
|
+
* One pending `tool.approval_required` request, already resolved to a real
|
|
93
|
+
* tool name and arguments by the embedding host — see the module doc's
|
|
94
|
+
* "What the embedding host still has to do itself".
|
|
95
|
+
*/
|
|
96
|
+
export interface TrueforgeApprovalRequest {
|
|
97
|
+
/** TrueForge's own thread id for this call — carried through unchanged
|
|
98
|
+
* onto the `user.tool_approval` item this responder produces. */
|
|
99
|
+
readonly threadId: string;
|
|
100
|
+
/** The pending tool call's id (`ToolCallRef.id`) — becomes `tool_call_id`
|
|
101
|
+
* on the produced item. */
|
|
102
|
+
readonly toolCallId: string;
|
|
103
|
+
/** The tool name `Gate.guard()` evaluates. */
|
|
104
|
+
readonly toolName: string;
|
|
105
|
+
/** The tool's arguments, resolved from the `model.message` event the
|
|
106
|
+
* pending call's `source_event_id` points at. */
|
|
107
|
+
readonly input: unknown;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Structural copy of `@truefoundry/trueforge-core`'s `ApprovalDecision`
|
|
111
|
+
* (`ApprovalDecisionSchema`, `core/events/schema.ts`) — this package does
|
|
112
|
+
* not depend on `@truefoundry/trueforge-core` at runtime, so the shape is
|
|
113
|
+
* declared here rather than imported. Confirmed field-for-field against
|
|
114
|
+
* `@truefoundry/trueforge-core@0.1.4`'s shipped `dist/core/events/schema.d.ts`
|
|
115
|
+
* (see module doc).
|
|
116
|
+
*/
|
|
117
|
+
export type TrueforgeApprovalDecision = {
|
|
118
|
+
status: 'allow';
|
|
119
|
+
} | {
|
|
120
|
+
status: 'deny';
|
|
121
|
+
reason?: string;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Structural copy of the `user.tool_approval` member of
|
|
125
|
+
* `@truefoundry/trueforge-core`'s `TurnInputItem` union
|
|
126
|
+
* (`UserToolApprovalMessageSchema`, `agent-session/schemas/events.ts`) — the
|
|
127
|
+
* shape `SessionHandle.createTurn({ input })` expects for answering a
|
|
128
|
+
* pending approval. Confirmed field-for-field against the same install.
|
|
129
|
+
*/
|
|
130
|
+
export interface TrueforgeUserToolApprovalItem {
|
|
131
|
+
type: 'user.tool_approval';
|
|
132
|
+
thread_id: string;
|
|
133
|
+
tool_call_id: string;
|
|
134
|
+
approval: TrueforgeApprovalDecision;
|
|
135
|
+
}
|
|
136
|
+
export interface IntuticApprovalResponderOptions {
|
|
137
|
+
/** Overrides the process-wide installed gate (`install(new Gate(...))`). */
|
|
138
|
+
gate?: Gate;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Render a pending request's `input` as the `tool_input` object
|
|
142
|
+
* {@link Gate.guard} evaluates. Mirrors `harness.ts`'s/`workflow.ts`'s own
|
|
143
|
+
* coercion: a non-object input (a bare string/number/array — TrueForge's own
|
|
144
|
+
* `model.message` tool-call arguments are typically already an object, but
|
|
145
|
+
* this adapter does not import that wire format to assume so) falls back to
|
|
146
|
+
* `{ args: [...] }` so the gate always has something to evaluate.
|
|
147
|
+
*/
|
|
148
|
+
export declare function renderTrueforgeToolInput(input: unknown): Record<string, unknown>;
|
|
149
|
+
/**
|
|
150
|
+
* Build an approval responder: a function that evaluates each pending
|
|
151
|
+
* TrueForge tool-approval request via `Gate.guard()` and produces the
|
|
152
|
+
* `user.tool_approval` input items for your next `createTurn()` call:
|
|
153
|
+
*
|
|
154
|
+
* ```ts
|
|
155
|
+
* import { Gate, install } from '@intutic/gate'
|
|
156
|
+
* import { intuticApprovalResponder } from '@intutic/gate/trueforge'
|
|
157
|
+
*
|
|
158
|
+
* install(new Gate({ workspaceId: process.env.INTUTIC_WORKSPACE_ID }))
|
|
159
|
+
*
|
|
160
|
+
* const respond = intuticApprovalResponder()
|
|
161
|
+
*
|
|
162
|
+
* // ...drive turn.stream() until it pauses on a tool.approval_required
|
|
163
|
+
* // required_action; resolve each {id, source_event_id} back to a real
|
|
164
|
+
* // tool name + arguments (see the module doc), then:
|
|
165
|
+
* const input = await respond(pendingRequests)
|
|
166
|
+
* const nextTurn = await session.createTurn({
|
|
167
|
+
* turn_id: newTurnId(),
|
|
168
|
+
* input,
|
|
169
|
+
* previous_turn_id: pausedTurn.id,
|
|
170
|
+
* signal,
|
|
171
|
+
* resolver,
|
|
172
|
+
* })
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* Decision mapping, per request:
|
|
176
|
+
*
|
|
177
|
+
* - gate allows -> `{ status: 'allow' }`
|
|
178
|
+
* - `IntuticGateRefusal` -> `{ status: 'deny', reason }` — the
|
|
179
|
+
* `[Intutic Governance] BLOCKED: ...` message
|
|
180
|
+
* - any OTHER throw -> `{ status: 'deny', reason: '... gate
|
|
181
|
+
* crashed ...' }` — fail CLOSED, same posture as `harness.ts`'s
|
|
182
|
+
* responder: this function's output is a decision list fed into a
|
|
183
|
+
* resumed turn, and a deliverable, auditable deny is better than an
|
|
184
|
+
* escaped throw that leaves the caller with nothing to resume with.
|
|
185
|
+
*
|
|
186
|
+
* Requires a `Gate` — either passed via `{ gate }`, or installed process-wide
|
|
187
|
+
* with `install(new Gate(...))`. A call with neither throws before
|
|
188
|
+
* evaluating anything, refusing to answer approvals unguarded.
|
|
189
|
+
*/
|
|
190
|
+
export declare function intuticApprovalResponder(opts?: IntuticApprovalResponderOptions): (requests: readonly TrueforgeApprovalRequest[]) => Promise<TrueforgeUserToolApprovalItem[]>;
|
|
191
|
+
//# sourceMappingURL=trueforge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trueforge.d.ts","sourceRoot":"","sources":["../src/trueforge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAEH,OAAO,EAAwB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAG3D;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC;sEACkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB;gCAC4B;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB;sDACkD;IAClD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,yBAAyB,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjG;;;;;;GAMG;AACH,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,oBAAoB,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,yBAAyB,CAAA;CACpC;AAED,MAAM,WAAW,+BAA+B;IAC9C,4EAA4E;IAC5E,IAAI,CAAC,EAAE,IAAI,CAAA;CACZ;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKhF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,GAAE,+BAAoC,GACzC,CAAC,QAAQ,EAAE,SAAS,wBAAwB,EAAE,KAAK,OAAO,CAAC,6BAA6B,EAAE,CAAC,CAsC7F"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TrueForge (embedded) adapter: `intuticApprovalResponder()`.
|
|
3
|
+
*
|
|
4
|
+
* `@intutic/gate/trueforge` — Phase 1 of TrueForge support: another team's
|
|
5
|
+
* Node process that imports `@truefoundry/trueforge-core` directly
|
|
6
|
+
* (github.com/truefoundry/trueforge, MIT) and drives its own
|
|
7
|
+
* `SessionHandle`/`TurnHandle` API in-process. Same family idea as
|
|
8
|
+
* `mastra.ts`/`vercel.ts` (wrap the framework's own veto point around this
|
|
9
|
+
* package's `Gate.guard()`), but TrueForge's veto point is shaped
|
|
10
|
+
* differently from either of those, so this module follows `harness.ts`'s
|
|
11
|
+
* `intuticApprovalResponder()` precedent instead — a batch function
|
|
12
|
+
* answering pending approvals — not a single-call `beforeToolCall`/
|
|
13
|
+
* `toolApproval` option.
|
|
14
|
+
*
|
|
15
|
+
* Phase 2 (`HarnessType.TRUEFORGE_SERVER` — TrueForge run as its own
|
|
16
|
+
* standalone/hosted server, e.g. `npx @truefoundry/trueforge`, Docker
|
|
17
|
+
* Compose, or the Helm chart) is explicitly OUT OF SCOPE here. Nobody embeds
|
|
18
|
+
* an Intutic gate into a third-party OSS server process; that deployment
|
|
19
|
+
* mode needs governance running in an external Intutic-operated bridge
|
|
20
|
+
* service — separate, larger work, not attempted in this module.
|
|
21
|
+
*
|
|
22
|
+
* ## Confirmed against the real published package, not assumed
|
|
23
|
+
*
|
|
24
|
+
* `@truefoundry/trueforge-core@0.1.4` (`latest` on npm at the time this was
|
|
25
|
+
* written) was downloaded from the npm registry and its shipped `.d.ts`
|
|
26
|
+
* files read directly — not inferred from docs alone:
|
|
27
|
+
*
|
|
28
|
+
* - There is NO synchronous, in-process "approval resolver" callback
|
|
29
|
+
* anywhere in the package. Every shipped `.d.ts` file was grepped for
|
|
30
|
+
* `approvalResolver`/`onApproval`/`approvalHandler`/`resolveApproval`:
|
|
31
|
+
* zero matches. `ITurnResourceResolver`
|
|
32
|
+
* (`dist/agent-session/ITurnResourceResolver.d.ts`) resolves sandboxes,
|
|
33
|
+
* agent definitions, and tracing per run — it carries no tool-call veto
|
|
34
|
+
* hook of any kind.
|
|
35
|
+
* - The ONLY approval mechanism, embedded or not, is the same turn/event
|
|
36
|
+
* contract TrueForge's standalone server exposes over HTTP: a turn
|
|
37
|
+
* pauses with a `required_actions` entry of type `tool.approval_required`
|
|
38
|
+
* (`EventType.TOOL_APPROVAL_REQUIRED`, `dist/agent-session/schemas/
|
|
39
|
+
* events.d.ts`), naming each pending call only as `{id, source_event_id}`
|
|
40
|
+
* — no tool name or arguments travel on the event itself. Answering it
|
|
41
|
+
* means constructing a NEW turn (`SessionHandle.createTurn()`) whose
|
|
42
|
+
* `input` carries a `user.tool_approval` item:
|
|
43
|
+
* `{type:'user.tool_approval', thread_id, tool_call_id, approval}`,
|
|
44
|
+
* where `approval` is `{status:'allow'} | {status:'deny', reason?:
|
|
45
|
+
* string}` (`ApprovalDecisionSchema`, `dist/core/events/schema.d.ts`) —
|
|
46
|
+
* confirmed field-for-field, and pinned by this module's own test file
|
|
47
|
+
* via a structural assignability check against the real exported
|
|
48
|
+
* `TurnInputItem` (`@truefoundry/trueforge-core/agent-session`) and
|
|
49
|
+
* `ApprovalDecision` (`@truefoundry/trueforge-core/core`) types.
|
|
50
|
+
*
|
|
51
|
+
* So unlike `mastra.ts`/`vercel.ts`, this adapter cannot BE "the function you
|
|
52
|
+
* pass as a framework option" — embedded TrueForge has no such option. What
|
|
53
|
+
* it can be, and is, is the `harness.ts`-shaped piece: a function that
|
|
54
|
+
* evaluates a batch of pending approval requests through `Gate.guard()` and
|
|
55
|
+
* returns the `user.tool_approval` `TurnInputItem[]` ready to hand to your
|
|
56
|
+
* next `session.createTurn({ input, previous_turn_id })` call.
|
|
57
|
+
*
|
|
58
|
+
* ## What the embedding host still has to do itself
|
|
59
|
+
*
|
|
60
|
+
* Resolving a `{id, source_event_id}` tool-call reference back to the real
|
|
61
|
+
* tool NAME and ARGUMENTS is the host's job, not this adapter's: the host
|
|
62
|
+
* already owns the `TurnHandle`/event stream (`turn.stream()`/
|
|
63
|
+
* `listEvents()`) this package has no reason to depend on, and
|
|
64
|
+
* `source_event_id` points at a `model.message` event whose tool-call wire
|
|
65
|
+
* shape this package deliberately does not import — matching
|
|
66
|
+
* `mastra.ts`/`vercel.ts`'s existing convention of zero runtime dependency
|
|
67
|
+
* on the framework being gated. Build the `TrueforgeApprovalRequest[]` this
|
|
68
|
+
* responder consumes from whatever your own event-resolution loop already
|
|
69
|
+
* extracts, then feed the returned items straight into your next
|
|
70
|
+
* `createTurn()` call.
|
|
71
|
+
*
|
|
72
|
+
* ## GUESSED, clearly marked as such: the exact orchestration wiring
|
|
73
|
+
*
|
|
74
|
+
* The plan that authored this phase asked to pin the exact registration API
|
|
75
|
+
* against `TurnHandle`/`ITurnResourceResolver`; having read both real files,
|
|
76
|
+
* NEITHER carries a registration point for this at all — the orchestration
|
|
77
|
+
* loop (drive `turn.stream()`, detect a `tool.approval_required` required
|
|
78
|
+
* action, resolve each `source_event_id`, call this responder, call
|
|
79
|
+
* `createTurn()` again with the answers) is necessarily HAND-WRITTEN
|
|
80
|
+
* application code in the embedding host, not something a config option
|
|
81
|
+
* turns on. That loop's exact shape — how you persist the paused turn id,
|
|
82
|
+
* how you resolve `source_event_id`, whether you drain `stream()` to
|
|
83
|
+
* completion before or after answering — is a genuine judgment call for the
|
|
84
|
+
* embedding host's own architecture and is NOT prescribed here. See
|
|
85
|
+
* `apps/docs/integrations/trueforge.md` for a worked (illustrative, not
|
|
86
|
+
* load-bearing) example of one way to wire this loop.
|
|
87
|
+
*
|
|
88
|
+
* @module
|
|
89
|
+
*/
|
|
90
|
+
import { active as activeGate } from './gate.js';
|
|
91
|
+
import { IntuticGateRefusal } from './errors.js';
|
|
92
|
+
/**
|
|
93
|
+
* Render a pending request's `input` as the `tool_input` object
|
|
94
|
+
* {@link Gate.guard} evaluates. Mirrors `harness.ts`'s/`workflow.ts`'s own
|
|
95
|
+
* coercion: a non-object input (a bare string/number/array — TrueForge's own
|
|
96
|
+
* `model.message` tool-call arguments are typically already an object, but
|
|
97
|
+
* this adapter does not import that wire format to assume so) falls back to
|
|
98
|
+
* `{ args: [...] }` so the gate always has something to evaluate.
|
|
99
|
+
*/
|
|
100
|
+
export function renderTrueforgeToolInput(input) {
|
|
101
|
+
if (input !== null && typeof input === 'object' && !Array.isArray(input)) {
|
|
102
|
+
return input;
|
|
103
|
+
}
|
|
104
|
+
return { args: [input] };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Build an approval responder: a function that evaluates each pending
|
|
108
|
+
* TrueForge tool-approval request via `Gate.guard()` and produces the
|
|
109
|
+
* `user.tool_approval` input items for your next `createTurn()` call:
|
|
110
|
+
*
|
|
111
|
+
* ```ts
|
|
112
|
+
* import { Gate, install } from '@intutic/gate'
|
|
113
|
+
* import { intuticApprovalResponder } from '@intutic/gate/trueforge'
|
|
114
|
+
*
|
|
115
|
+
* install(new Gate({ workspaceId: process.env.INTUTIC_WORKSPACE_ID }))
|
|
116
|
+
*
|
|
117
|
+
* const respond = intuticApprovalResponder()
|
|
118
|
+
*
|
|
119
|
+
* // ...drive turn.stream() until it pauses on a tool.approval_required
|
|
120
|
+
* // required_action; resolve each {id, source_event_id} back to a real
|
|
121
|
+
* // tool name + arguments (see the module doc), then:
|
|
122
|
+
* const input = await respond(pendingRequests)
|
|
123
|
+
* const nextTurn = await session.createTurn({
|
|
124
|
+
* turn_id: newTurnId(),
|
|
125
|
+
* input,
|
|
126
|
+
* previous_turn_id: pausedTurn.id,
|
|
127
|
+
* signal,
|
|
128
|
+
* resolver,
|
|
129
|
+
* })
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* Decision mapping, per request:
|
|
133
|
+
*
|
|
134
|
+
* - gate allows -> `{ status: 'allow' }`
|
|
135
|
+
* - `IntuticGateRefusal` -> `{ status: 'deny', reason }` — the
|
|
136
|
+
* `[Intutic Governance] BLOCKED: ...` message
|
|
137
|
+
* - any OTHER throw -> `{ status: 'deny', reason: '... gate
|
|
138
|
+
* crashed ...' }` — fail CLOSED, same posture as `harness.ts`'s
|
|
139
|
+
* responder: this function's output is a decision list fed into a
|
|
140
|
+
* resumed turn, and a deliverable, auditable deny is better than an
|
|
141
|
+
* escaped throw that leaves the caller with nothing to resume with.
|
|
142
|
+
*
|
|
143
|
+
* Requires a `Gate` — either passed via `{ gate }`, or installed process-wide
|
|
144
|
+
* with `install(new Gate(...))`. A call with neither throws before
|
|
145
|
+
* evaluating anything, refusing to answer approvals unguarded.
|
|
146
|
+
*/
|
|
147
|
+
export function intuticApprovalResponder(opts = {}) {
|
|
148
|
+
return async (requests) => {
|
|
149
|
+
const g = opts.gate ?? activeGate();
|
|
150
|
+
if (g === null) {
|
|
151
|
+
throw new Error('No gate configured: call install(new Gate(...)) from @intutic/gate, or pass ' +
|
|
152
|
+
'{ gate } to intuticApprovalResponder(). Refusing to answer tool approvals unguarded.');
|
|
153
|
+
}
|
|
154
|
+
const items = [];
|
|
155
|
+
for (const req of requests) {
|
|
156
|
+
let approval;
|
|
157
|
+
try {
|
|
158
|
+
await g.guard(req.toolName, renderTrueforgeToolInput(req.input));
|
|
159
|
+
approval = { status: 'allow' };
|
|
160
|
+
}
|
|
161
|
+
catch (exc) {
|
|
162
|
+
if (exc instanceof IntuticGateRefusal) {
|
|
163
|
+
approval = { status: 'deny', reason: exc.message };
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const detail = exc instanceof Error ? exc.message : String(exc);
|
|
167
|
+
approval = {
|
|
168
|
+
status: 'deny',
|
|
169
|
+
reason: `[Intutic Governance] BLOCKED: gate crashed (${detail}) — failing closed rather ` +
|
|
170
|
+
'than approving an unevaluated call.',
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
items.push({
|
|
175
|
+
type: 'user.tool_approval',
|
|
176
|
+
thread_id: req.threadId,
|
|
177
|
+
tool_call_id: req.toolCallId,
|
|
178
|
+
approval,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return items;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=trueforge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trueforge.js","sourceRoot":"","sources":["../src/trueforge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAEH,OAAO,EAAE,MAAM,IAAI,UAAU,EAAa,MAAM,WAAW,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAkDhD;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzE,OAAO,KAAgC,CAAA;IACzC,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,CAAA;AAC1B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAwC,EAAE;IAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE;QACxB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,UAAU,EAAE,CAAA;QACnC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,8EAA8E;gBAC5E,sFAAsF,CACzF,CAAA;QACH,CAAC;QAED,MAAM,KAAK,GAAoC,EAAE,CAAA;QACjD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,IAAI,QAAmC,CAAA;YACvC,IAAI,CAAC;gBACH,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,wBAAwB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;gBAChE,QAAQ,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;YAChC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,kBAAkB,EAAE,CAAC;oBACtC,QAAQ,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,CAAA;gBACpD,CAAC;qBAAM,CAAC;oBACN,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;oBAC/D,QAAQ,GAAG;wBACT,MAAM,EAAE,MAAM;wBACd,MAAM,EACJ,+CAA+C,MAAM,4BAA4B;4BACjF,qCAAqC;qBACxC,CAAA;gBACH,CAAC;YACH,CAAC;YACD,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,oBAAoB;gBAC1B,SAAS,EAAE,GAAG,CAAC,QAAQ;gBACvB,YAAY,EAAE,GAAG,CAAC,UAAU;gBAC5B,QAAQ;aACT,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;AACH,CAAC"}
|