@agentforge-io/core 2.0.19 → 2.0.20
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.
|
@@ -105,6 +105,19 @@ export declare class AgentService {
|
|
|
105
105
|
* authenticated user's connector tools (Gmail, Drive, …) on the fly
|
|
106
106
|
* via `overrides.extraTools`. Optional — connectors are opt-in. */
|
|
107
107
|
connectorRegistry?: ConnectorRegistryService | undefined);
|
|
108
|
+
/**
|
|
109
|
+
* Look up the human-friendly connector name + tool description for a
|
|
110
|
+
* given tool slug. Powers the friendly copy in `awaiting_approval` /
|
|
111
|
+
* `tool_blocked` cards: the visitor sees "Granola" instead of
|
|
112
|
+
* `granola_list_meetings`. Returns `undefined` for built-in / MCP
|
|
113
|
+
* tools, or when the registry isn't wired — the chat client falls
|
|
114
|
+
* back to the raw `toolName` in that case.
|
|
115
|
+
*
|
|
116
|
+
* Iterates every registered connector definition. The registry is
|
|
117
|
+
* small (handful of entries) so this is O(connectors × tools);
|
|
118
|
+
* acceptable for the rare per-approval call path.
|
|
119
|
+
*/
|
|
120
|
+
private describeTool;
|
|
108
121
|
/**
|
|
109
122
|
* Fetch the connector tools the user has authorized, swallowing failures.
|
|
110
123
|
* The agent loop must keep working even if a connector's refresh token is
|
|
@@ -29,6 +29,38 @@ class AgentService {
|
|
|
29
29
|
this.hooks = hooks;
|
|
30
30
|
this.connectorRegistry = connectorRegistry;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Look up the human-friendly connector name + tool description for a
|
|
34
|
+
* given tool slug. Powers the friendly copy in `awaiting_approval` /
|
|
35
|
+
* `tool_blocked` cards: the visitor sees "Granola" instead of
|
|
36
|
+
* `granola_list_meetings`. Returns `undefined` for built-in / MCP
|
|
37
|
+
* tools, or when the registry isn't wired — the chat client falls
|
|
38
|
+
* back to the raw `toolName` in that case.
|
|
39
|
+
*
|
|
40
|
+
* Iterates every registered connector definition. The registry is
|
|
41
|
+
* small (handful of entries) so this is O(connectors × tools);
|
|
42
|
+
* acceptable for the rare per-approval call path.
|
|
43
|
+
*/
|
|
44
|
+
describeTool(toolName) {
|
|
45
|
+
if (!this.connectorRegistry)
|
|
46
|
+
return undefined;
|
|
47
|
+
try {
|
|
48
|
+
for (const def of this.connectorRegistry.list()) {
|
|
49
|
+
for (const factory of def.tools) {
|
|
50
|
+
if (factory.definition.name === toolName) {
|
|
51
|
+
return {
|
|
52
|
+
connectorName: def.name,
|
|
53
|
+
toolDescription: factory.definition.description,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Registry is misconfigured — fall back to bare tool name.
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
32
64
|
/**
|
|
33
65
|
* Fetch the connector tools the user has authorized, swallowing failures.
|
|
34
66
|
* The agent loop must keep working even if a connector's refresh token is
|
|
@@ -265,44 +297,58 @@ class AgentService {
|
|
|
265
297
|
// stream may close, but the conversation history doesn't lose
|
|
266
298
|
// context.
|
|
267
299
|
if ((0, tool_approval_gate_1.isToolApprovalRequired)(err)) {
|
|
300
|
+
const ctx = this.describeTool(err.toolName);
|
|
268
301
|
yield {
|
|
269
302
|
type: 'awaiting_approval',
|
|
270
303
|
approvalId: err.approvalId,
|
|
271
304
|
toolName: err.toolName,
|
|
272
305
|
expiresAt: err.expiresAt,
|
|
306
|
+
connectorName: ctx?.connectorName,
|
|
307
|
+
toolDescription: ctx?.toolDescription,
|
|
273
308
|
};
|
|
274
309
|
await this.conversations.addMessage({
|
|
275
310
|
conversationId: params.conversationId,
|
|
276
311
|
userId: params.userId,
|
|
277
312
|
role: 'assistant',
|
|
278
|
-
// Plain-text
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
content:
|
|
313
|
+
// Plain-text fallback. Friendly enough for legacy clients
|
|
314
|
+
// that don't render `metadata.kind`. The structured card
|
|
315
|
+
// lives in `metadata` for capable widgets.
|
|
316
|
+
content: ctx?.connectorName
|
|
317
|
+
? `Necesito tu permiso para usar ${ctx.connectorName}.`
|
|
318
|
+
: `Necesito tu permiso para continuar.`,
|
|
282
319
|
metadata: {
|
|
283
320
|
kind: 'awaiting_approval',
|
|
284
321
|
approvalId: err.approvalId,
|
|
285
322
|
toolName: err.toolName,
|
|
286
323
|
expiresAt: err.expiresAt,
|
|
324
|
+
connectorName: ctx?.connectorName,
|
|
325
|
+
toolDescription: ctx?.toolDescription,
|
|
287
326
|
},
|
|
288
327
|
});
|
|
289
328
|
return;
|
|
290
329
|
}
|
|
291
330
|
if ((0, tool_approval_gate_1.isToolBlockedError)(err)) {
|
|
331
|
+
const ctx = this.describeTool(err.toolName);
|
|
292
332
|
yield {
|
|
293
333
|
type: 'tool_blocked',
|
|
294
334
|
toolName: err.toolName,
|
|
295
335
|
reason: err.reason,
|
|
336
|
+
connectorName: ctx?.connectorName,
|
|
337
|
+
toolDescription: ctx?.toolDescription,
|
|
296
338
|
};
|
|
297
339
|
await this.conversations.addMessage({
|
|
298
340
|
conversationId: params.conversationId,
|
|
299
341
|
userId: params.userId,
|
|
300
342
|
role: 'assistant',
|
|
301
|
-
content:
|
|
343
|
+
content: ctx?.connectorName
|
|
344
|
+
? `No puedo usar ${ctx.connectorName} en esta cuenta.`
|
|
345
|
+
: `No puedo usar esa herramienta en esta cuenta.`,
|
|
302
346
|
metadata: {
|
|
303
347
|
kind: 'tool_blocked',
|
|
304
348
|
toolName: err.toolName,
|
|
305
349
|
reason: err.reason,
|
|
350
|
+
connectorName: ctx?.connectorName,
|
|
351
|
+
toolDescription: ctx?.toolDescription,
|
|
306
352
|
},
|
|
307
353
|
});
|
|
308
354
|
return;
|
|
@@ -187,6 +187,16 @@ export type StreamChunk = {
|
|
|
187
187
|
approvalId: string;
|
|
188
188
|
toolName: string;
|
|
189
189
|
expiresAt: string;
|
|
190
|
+
/** Human-friendly connector name (`'Granola'`, `'Notion'`). The
|
|
191
|
+
* runner enriches it from the registry so the chat widget can
|
|
192
|
+
* render a sentence like "I need permission to use Granola"
|
|
193
|
+
* instead of the raw tool slug. Optional — clients fall back
|
|
194
|
+
* to `toolName` when the runtime doesn't supply this. */
|
|
195
|
+
connectorName?: string;
|
|
196
|
+
/** Human-friendly first sentence of the tool's description.
|
|
197
|
+
* Same story: enables the widget to say what the tool DOES in
|
|
198
|
+
* natural language. Optional. */
|
|
199
|
+
toolDescription?: string;
|
|
190
200
|
} | {
|
|
191
201
|
/** Emitted when a tool dispatch hit `kind: 'blocked'`. Mirrors the
|
|
192
202
|
* shape above so the client renders a terminal-error card with
|
|
@@ -194,6 +204,8 @@ export type StreamChunk = {
|
|
|
194
204
|
type: 'tool_blocked';
|
|
195
205
|
toolName: string;
|
|
196
206
|
reason?: string;
|
|
207
|
+
connectorName?: string;
|
|
208
|
+
toolDescription?: string;
|
|
197
209
|
} | {
|
|
198
210
|
type: 'done';
|
|
199
211
|
messageId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentforge-io/core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.20",
|
|
4
4
|
"description": "Framework-free AI runtime SDK. Owns: agent loop (Anthropic), conversations, tools, streaming, agent-job queue, SdkHooks. Identity, billing, infra (email/uploads/secrets) live in the host's modules — not here.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|