toolgate 1.0.0 → 1.0.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/dist/index.d.mts CHANGED
@@ -128,6 +128,36 @@ declare class ToolGate {
128
128
  */
129
129
  declare function cliApproval(request: ApprovalRequest): Promise<ApprovalResult>;
130
130
 
131
+ interface DashboardApprovalConfig {
132
+ supabaseUrl: string;
133
+ supabaseKey: string;
134
+ /** Polling interval in ms (default: 2000) */
135
+ pollInterval?: number;
136
+ /** Timeout in ms — reject all if no response (default: 10 minutes) */
137
+ timeout?: number;
138
+ /** Called each poll cycle — for logging */
139
+ onPoll?: (elapsed: number) => void;
140
+ }
141
+ /**
142
+ * Returns an `onApprovalNeeded` callback that:
143
+ * 1. Saves the session to Supabase (so it appears in the dashboard)
144
+ * 2. Polls until a human approves/rejects via the dashboard UI
145
+ * 3. Returns the decisions so ToolGate can execute approved actions
146
+ *
147
+ * Usage:
148
+ * const gate = new ToolGate(executor, {
149
+ * ...dashboardApproval({
150
+ * supabaseUrl: "https://xxx.supabase.co",
151
+ * supabaseKey: "eyJ...",
152
+ * }),
153
+ * });
154
+ */
155
+ declare function dashboardApproval(config: DashboardApprovalConfig): {
156
+ supabaseUrl: string;
157
+ supabaseKey: string;
158
+ onApprovalNeeded: (request: ApprovalRequest) => Promise<ApprovalResult>;
159
+ };
160
+
131
161
  declare function classifyTool(toolName: string, params: Record<string, unknown>, mcpToolDef?: MCPToolDef): ToolClassification;
132
162
 
133
163
  /**
@@ -162,4 +192,4 @@ declare function autoConfigFromMCP(servers: Array<{
162
192
  url: string;
163
193
  }>, overrides?: Partial<ToolGateConfig>): Promise<ToolGateConfig>;
164
194
 
165
- export { type ActionDecision, type ApprovalRequest, type ApprovalResult, type ExecutedRead, type MCPServerDef, type MCPToolDef, type PendingAction, type RealExecutor, type Session, type SessionStatus, type ToolCall, type ToolClassification, ToolGate, type ToolGateConfig, type ToolIntent, autoConfigFromMCP, classifyTool, cliApproval, discoverMCPTools, mcpToolGate };
195
+ export { type ActionDecision, type ApprovalRequest, type ApprovalResult, type DashboardApprovalConfig, type ExecutedRead, type MCPServerDef, type MCPToolDef, type PendingAction, type RealExecutor, type Session, type SessionStatus, type ToolCall, type ToolClassification, ToolGate, type ToolGateConfig, type ToolIntent, autoConfigFromMCP, classifyTool, cliApproval, dashboardApproval, discoverMCPTools, mcpToolGate };
package/dist/index.d.ts CHANGED
@@ -128,6 +128,36 @@ declare class ToolGate {
128
128
  */
129
129
  declare function cliApproval(request: ApprovalRequest): Promise<ApprovalResult>;
130
130
 
131
+ interface DashboardApprovalConfig {
132
+ supabaseUrl: string;
133
+ supabaseKey: string;
134
+ /** Polling interval in ms (default: 2000) */
135
+ pollInterval?: number;
136
+ /** Timeout in ms — reject all if no response (default: 10 minutes) */
137
+ timeout?: number;
138
+ /** Called each poll cycle — for logging */
139
+ onPoll?: (elapsed: number) => void;
140
+ }
141
+ /**
142
+ * Returns an `onApprovalNeeded` callback that:
143
+ * 1. Saves the session to Supabase (so it appears in the dashboard)
144
+ * 2. Polls until a human approves/rejects via the dashboard UI
145
+ * 3. Returns the decisions so ToolGate can execute approved actions
146
+ *
147
+ * Usage:
148
+ * const gate = new ToolGate(executor, {
149
+ * ...dashboardApproval({
150
+ * supabaseUrl: "https://xxx.supabase.co",
151
+ * supabaseKey: "eyJ...",
152
+ * }),
153
+ * });
154
+ */
155
+ declare function dashboardApproval(config: DashboardApprovalConfig): {
156
+ supabaseUrl: string;
157
+ supabaseKey: string;
158
+ onApprovalNeeded: (request: ApprovalRequest) => Promise<ApprovalResult>;
159
+ };
160
+
131
161
  declare function classifyTool(toolName: string, params: Record<string, unknown>, mcpToolDef?: MCPToolDef): ToolClassification;
132
162
 
133
163
  /**
@@ -162,4 +192,4 @@ declare function autoConfigFromMCP(servers: Array<{
162
192
  url: string;
163
193
  }>, overrides?: Partial<ToolGateConfig>): Promise<ToolGateConfig>;
164
194
 
165
- export { type ActionDecision, type ApprovalRequest, type ApprovalResult, type ExecutedRead, type MCPServerDef, type MCPToolDef, type PendingAction, type RealExecutor, type Session, type SessionStatus, type ToolCall, type ToolClassification, ToolGate, type ToolGateConfig, type ToolIntent, autoConfigFromMCP, classifyTool, cliApproval, discoverMCPTools, mcpToolGate };
195
+ export { type ActionDecision, type ApprovalRequest, type ApprovalResult, type DashboardApprovalConfig, type ExecutedRead, type MCPServerDef, type MCPToolDef, type PendingAction, type RealExecutor, type Session, type SessionStatus, type ToolCall, type ToolClassification, ToolGate, type ToolGateConfig, type ToolIntent, autoConfigFromMCP, classifyTool, cliApproval, dashboardApproval, discoverMCPTools, mcpToolGate };
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  autoConfigFromMCP: () => autoConfigFromMCP,
35
35
  classifyTool: () => classifyTool,
36
36
  cliApproval: () => cliApproval,
37
+ dashboardApproval: () => dashboardApproval,
37
38
  discoverMCPTools: () => discoverMCPTools,
38
39
  mcpToolGate: () => mcpToolGate
39
40
  });
@@ -256,6 +257,18 @@ var SupabasePersistence = class {
256
257
  metadata: session.metadata ? JSON.stringify(session.metadata) : null
257
258
  });
258
259
  }
260
+ async getSession(id) {
261
+ const endpoint = `${this.url}/rest/v1/toolgate_sessions?id=eq.${id}&select=*`;
262
+ const res = await fetch(endpoint, {
263
+ headers: {
264
+ apikey: this.key,
265
+ Authorization: `Bearer ${this.key}`
266
+ }
267
+ });
268
+ if (!res.ok) return null;
269
+ const rows = await res.json();
270
+ return rows[0] ?? null;
271
+ }
259
272
  async updateSession(session) {
260
273
  await this.rpc(
261
274
  "toolgate_sessions",
@@ -572,6 +585,53 @@ ${badge(a.classification.intent)} ${a.name}`);
572
585
  return { sessionId: request.sessionId, decisions, approvedAt: Date.now() };
573
586
  }
574
587
 
588
+ // src/core/dashboard-approval.ts
589
+ function dashboardApproval(config) {
590
+ const persistence = new SupabasePersistence(config.supabaseUrl, config.supabaseKey);
591
+ const pollMs = config.pollInterval ?? 2e3;
592
+ const timeoutMs = config.timeout ?? 10 * 60 * 1e3;
593
+ const onApprovalNeeded = async (request) => {
594
+ console.log(`
595
+ \u{1F310} Session saved to dashboard \u2014 waiting for approval...`);
596
+ console.log(` Open your dashboard and review session ${request.sessionId.slice(0, 8)}\u2026
597
+ `);
598
+ const start = Date.now();
599
+ while (true) {
600
+ const elapsed = Date.now() - start;
601
+ if (elapsed > timeoutMs) {
602
+ console.log(`\u23F0 Timeout \u2014 rejecting all actions.`);
603
+ const decisions = new Map(
604
+ request.pendingActions.map((a) => [a.id, { action: "reject" }])
605
+ );
606
+ return { sessionId: request.sessionId, decisions, approvedAt: Date.now() };
607
+ }
608
+ const row = await persistence.getSession(request.sessionId);
609
+ if (row && row.status !== "pending_approval") {
610
+ console.log(`\u2705 Dashboard response received: ${row.status}`);
611
+ const actions = typeof row.pending_actions === "string" ? JSON.parse(row.pending_actions) : row.pending_actions;
612
+ const decisions = /* @__PURE__ */ new Map();
613
+ for (const a of actions) {
614
+ if (a.approved && a.editedParams) {
615
+ decisions.set(a.id, { action: "edit", actionId: a.id, newParams: a.editedParams });
616
+ } else if (a.approved) {
617
+ decisions.set(a.id, { action: "approve" });
618
+ } else {
619
+ decisions.set(a.id, { action: "reject" });
620
+ }
621
+ }
622
+ return { sessionId: request.sessionId, decisions, approvedAt: Date.now() };
623
+ }
624
+ if (config.onPoll) config.onPoll(elapsed);
625
+ await new Promise((r) => setTimeout(r, pollMs));
626
+ }
627
+ };
628
+ return {
629
+ supabaseUrl: config.supabaseUrl,
630
+ supabaseKey: config.supabaseKey,
631
+ onApprovalNeeded
632
+ };
633
+ }
634
+
575
635
  // src/mcp/index.ts
576
636
  function mcpToolGate(executor, config = {}) {
577
637
  return new ToolGate(executor, config);
@@ -611,6 +671,7 @@ async function autoConfigFromMCP(servers, overrides = {}) {
611
671
  autoConfigFromMCP,
612
672
  classifyTool,
613
673
  cliApproval,
674
+ dashboardApproval,
614
675
  discoverMCPTools,
615
676
  mcpToolGate
616
677
  });
package/dist/index.mjs CHANGED
@@ -215,6 +215,18 @@ var SupabasePersistence = class {
215
215
  metadata: session.metadata ? JSON.stringify(session.metadata) : null
216
216
  });
217
217
  }
218
+ async getSession(id) {
219
+ const endpoint = `${this.url}/rest/v1/toolgate_sessions?id=eq.${id}&select=*`;
220
+ const res = await fetch(endpoint, {
221
+ headers: {
222
+ apikey: this.key,
223
+ Authorization: `Bearer ${this.key}`
224
+ }
225
+ });
226
+ if (!res.ok) return null;
227
+ const rows = await res.json();
228
+ return rows[0] ?? null;
229
+ }
218
230
  async updateSession(session) {
219
231
  await this.rpc(
220
232
  "toolgate_sessions",
@@ -531,6 +543,53 @@ ${badge(a.classification.intent)} ${a.name}`);
531
543
  return { sessionId: request.sessionId, decisions, approvedAt: Date.now() };
532
544
  }
533
545
 
546
+ // src/core/dashboard-approval.ts
547
+ function dashboardApproval(config) {
548
+ const persistence = new SupabasePersistence(config.supabaseUrl, config.supabaseKey);
549
+ const pollMs = config.pollInterval ?? 2e3;
550
+ const timeoutMs = config.timeout ?? 10 * 60 * 1e3;
551
+ const onApprovalNeeded = async (request) => {
552
+ console.log(`
553
+ \u{1F310} Session saved to dashboard \u2014 waiting for approval...`);
554
+ console.log(` Open your dashboard and review session ${request.sessionId.slice(0, 8)}\u2026
555
+ `);
556
+ const start = Date.now();
557
+ while (true) {
558
+ const elapsed = Date.now() - start;
559
+ if (elapsed > timeoutMs) {
560
+ console.log(`\u23F0 Timeout \u2014 rejecting all actions.`);
561
+ const decisions = new Map(
562
+ request.pendingActions.map((a) => [a.id, { action: "reject" }])
563
+ );
564
+ return { sessionId: request.sessionId, decisions, approvedAt: Date.now() };
565
+ }
566
+ const row = await persistence.getSession(request.sessionId);
567
+ if (row && row.status !== "pending_approval") {
568
+ console.log(`\u2705 Dashboard response received: ${row.status}`);
569
+ const actions = typeof row.pending_actions === "string" ? JSON.parse(row.pending_actions) : row.pending_actions;
570
+ const decisions = /* @__PURE__ */ new Map();
571
+ for (const a of actions) {
572
+ if (a.approved && a.editedParams) {
573
+ decisions.set(a.id, { action: "edit", actionId: a.id, newParams: a.editedParams });
574
+ } else if (a.approved) {
575
+ decisions.set(a.id, { action: "approve" });
576
+ } else {
577
+ decisions.set(a.id, { action: "reject" });
578
+ }
579
+ }
580
+ return { sessionId: request.sessionId, decisions, approvedAt: Date.now() };
581
+ }
582
+ if (config.onPoll) config.onPoll(elapsed);
583
+ await new Promise((r) => setTimeout(r, pollMs));
584
+ }
585
+ };
586
+ return {
587
+ supabaseUrl: config.supabaseUrl,
588
+ supabaseKey: config.supabaseKey,
589
+ onApprovalNeeded
590
+ };
591
+ }
592
+
534
593
  // src/mcp/index.ts
535
594
  function mcpToolGate(executor, config = {}) {
536
595
  return new ToolGate(executor, config);
@@ -569,6 +628,7 @@ export {
569
628
  autoConfigFromMCP,
570
629
  classifyTool,
571
630
  cliApproval,
631
+ dashboardApproval,
572
632
  discoverMCPTools,
573
633
  mcpToolGate
574
634
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolgate",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Optimistic execution middleware for autonomous agents — let reads pass, intercept actions, approve at the end.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",