botinabox 2.5.1 → 2.5.3
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/bin/botinabox.mjs +1 -1
- package/dist/channels/slack/index.d.ts +1 -1
- package/dist/{chat-pipeline-BWrtVqEP.d.ts → chat-pipeline-DuNX5WoL.d.ts} +3 -0
- package/dist/chunk-7AGWGYZC.js +419 -0
- package/dist/cli.js +1 -1
- package/dist/connectors/google/index.d.ts +10 -1
- package/dist/connectors/google/index.js +1 -1
- package/dist/gmail-connector-VP5FF56J.js +7 -0
- package/dist/index.d.ts +16 -3
- package/dist/index.js +67 -28
- package/package.json +1 -1
package/bin/botinabox.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import('../dist/cli.js').then(m => m.main(process.argv.slice(2)));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { C as ChannelAdapter, c as ChannelMeta, a as ChannelCapabilities, I as InboundMessage, b as ChannelConfig, H as HealthStatus, O as OutboundPayload, S as SendResult } from '../../channel-06G0vbIn.js';
|
|
2
|
-
import { H as HookBus, c as ChatPipeline } from '../../chat-pipeline-
|
|
2
|
+
import { H as HookBus, c as ChatPipeline } from '../../chat-pipeline-DuNX5WoL.js';
|
|
3
3
|
import 'better-sqlite3';
|
|
4
4
|
import '../../provider-DLGUfnNx.js';
|
|
5
5
|
|
|
@@ -584,6 +584,8 @@ interface ChatPipelineConfig {
|
|
|
584
584
|
model?: string;
|
|
585
585
|
/** Enable LLM fallback routing (default: false) */
|
|
586
586
|
llmRouting?: boolean;
|
|
587
|
+
/** Skip the ack layer — no fast response before task dispatch (default: false) */
|
|
588
|
+
skipAck?: boolean;
|
|
587
589
|
/** TaskQueue instance — required for task dispatch */
|
|
588
590
|
tasks: {
|
|
589
591
|
create(task: Record<string, unknown>): Promise<string>;
|
|
@@ -608,6 +610,7 @@ declare class ChatPipeline {
|
|
|
608
610
|
private readonly dedupWindowMs;
|
|
609
611
|
private readonly tasks;
|
|
610
612
|
private readonly wakeups;
|
|
613
|
+
private readonly skipAck;
|
|
611
614
|
private readonly threadChannelMap;
|
|
612
615
|
/** Last dispatch promise — exposed for testing. */
|
|
613
616
|
lastDispatch: Promise<void>;
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
// src/connectors/google/oauth.ts
|
|
2
|
+
var _google;
|
|
3
|
+
async function getGoogle() {
|
|
4
|
+
if (!_google) {
|
|
5
|
+
try {
|
|
6
|
+
const mod = await import("googleapis");
|
|
7
|
+
_google = mod.google;
|
|
8
|
+
} catch {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"googleapis is required for Google connectors. Install it: npm install googleapis"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return _google;
|
|
15
|
+
}
|
|
16
|
+
async function createOAuth2Client(config) {
|
|
17
|
+
const google = await getGoogle();
|
|
18
|
+
return new google.auth.OAuth2(
|
|
19
|
+
config.clientId,
|
|
20
|
+
config.clientSecret,
|
|
21
|
+
config.redirectUri
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
function getAuthUrl(client, scopes) {
|
|
25
|
+
return client.generateAuthUrl({
|
|
26
|
+
access_type: "offline",
|
|
27
|
+
prompt: "consent",
|
|
28
|
+
scope: scopes
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function exchangeCode(client, code) {
|
|
32
|
+
const { tokens } = await client.getToken(code);
|
|
33
|
+
return tokens;
|
|
34
|
+
}
|
|
35
|
+
async function createServiceAccountClient(config, scopes) {
|
|
36
|
+
const google = await getGoogle();
|
|
37
|
+
const auth = new google.auth.GoogleAuth({
|
|
38
|
+
...config.keyFile ? { keyFile: config.keyFile } : {},
|
|
39
|
+
...config.credentials ? { credentials: config.credentials } : {},
|
|
40
|
+
scopes,
|
|
41
|
+
clientOptions: { subject: config.subject }
|
|
42
|
+
});
|
|
43
|
+
return auth.getClient();
|
|
44
|
+
}
|
|
45
|
+
async function loadTokens(getter, accountKey) {
|
|
46
|
+
const raw = await getter(`google_tokens:${accountKey}`);
|
|
47
|
+
if (!raw) return null;
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(raw);
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function saveTokens(setter, accountKey, tokens) {
|
|
55
|
+
await setter(`google_tokens:${accountKey}`, JSON.stringify(tokens));
|
|
56
|
+
}
|
|
57
|
+
async function refreshIfNeeded(client, tokens, saver) {
|
|
58
|
+
const buffer = 6e4;
|
|
59
|
+
const isExpired = tokens.expiry_date != null && Date.now() >= tokens.expiry_date - buffer;
|
|
60
|
+
if (!isExpired) return tokens;
|
|
61
|
+
client.setCredentials(tokens);
|
|
62
|
+
const { credentials } = await client.refreshAccessToken();
|
|
63
|
+
const refreshed = {
|
|
64
|
+
access_token: credentials.access_token,
|
|
65
|
+
refresh_token: credentials.refresh_token ?? tokens.refresh_token,
|
|
66
|
+
expiry_date: credentials.expiry_date ?? void 0,
|
|
67
|
+
token_type: credentials.token_type ?? "Bearer"
|
|
68
|
+
};
|
|
69
|
+
if (saver) {
|
|
70
|
+
await saver(refreshed);
|
|
71
|
+
}
|
|
72
|
+
return refreshed;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/connectors/google/gmail-connector.ts
|
|
76
|
+
var GoogleGmailConnector = class {
|
|
77
|
+
id = "google-gmail";
|
|
78
|
+
meta = {
|
|
79
|
+
displayName: "Google Gmail",
|
|
80
|
+
provider: "google",
|
|
81
|
+
dataType: "email"
|
|
82
|
+
};
|
|
83
|
+
tokenLoader;
|
|
84
|
+
tokenSaver;
|
|
85
|
+
client = null;
|
|
86
|
+
config = null;
|
|
87
|
+
tokens = null;
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
89
|
+
gmail = null;
|
|
90
|
+
constructor(opts = {}) {
|
|
91
|
+
this.tokenLoader = opts.tokenLoader;
|
|
92
|
+
this.tokenSaver = opts.tokenSaver;
|
|
93
|
+
}
|
|
94
|
+
// ── Lifecycle ──────────────────────────────────────────────────
|
|
95
|
+
async connect(config) {
|
|
96
|
+
this.config = config;
|
|
97
|
+
const scopes = config.scopes ?? [
|
|
98
|
+
"https://www.googleapis.com/auth/gmail.readonly"
|
|
99
|
+
];
|
|
100
|
+
if (config.serviceAccount) {
|
|
101
|
+
this.client = await createServiceAccountClient(config.serviceAccount, scopes);
|
|
102
|
+
} else if (config.oauth) {
|
|
103
|
+
this.client = await createOAuth2Client(config.oauth);
|
|
104
|
+
if (!this.tokenLoader) {
|
|
105
|
+
throw new Error("tokenLoader required for OAuth2 flow");
|
|
106
|
+
}
|
|
107
|
+
this.tokens = await loadTokens(this.tokenLoader, config.account);
|
|
108
|
+
if (!this.tokens) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`No stored tokens for account ${config.account}. Complete the OAuth flow first.`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
this.tokens = await refreshIfNeeded(
|
|
114
|
+
this.client,
|
|
115
|
+
this.tokens,
|
|
116
|
+
this.tokenSaver ? async (t) => saveTokens(this.tokenSaver, config.account, t) : void 0
|
|
117
|
+
);
|
|
118
|
+
this.client.setCredentials(this.tokens);
|
|
119
|
+
} else {
|
|
120
|
+
throw new Error("Either serviceAccount or oauth config is required");
|
|
121
|
+
}
|
|
122
|
+
const { google } = await import("googleapis");
|
|
123
|
+
this.gmail = google.gmail({ version: "v1", auth: this.client });
|
|
124
|
+
}
|
|
125
|
+
async disconnect() {
|
|
126
|
+
this.client = null;
|
|
127
|
+
this.gmail = null;
|
|
128
|
+
this.tokens = null;
|
|
129
|
+
this.config = null;
|
|
130
|
+
}
|
|
131
|
+
async healthCheck() {
|
|
132
|
+
try {
|
|
133
|
+
this.ensureConnected();
|
|
134
|
+
const res = await this.gmail.users.getProfile({ userId: "me" });
|
|
135
|
+
return { ok: true, account: res.data.emailAddress };
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return { ok: false, error: errorMessage(err) };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// ── Auth ───────────────────────────────────────────────────────
|
|
141
|
+
async authenticate(codeProvider) {
|
|
142
|
+
if (!this.config) {
|
|
143
|
+
return { success: false, error: "Call connect() first to set config, or pass config and call authenticate() before connect()." };
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
if (!this.config.oauth) {
|
|
147
|
+
return { success: false, error: "OAuth config required for browser-based authenticate(). Use serviceAccount for headless auth." };
|
|
148
|
+
}
|
|
149
|
+
if (!this.tokenSaver) {
|
|
150
|
+
return { success: false, error: "tokenSaver required for authenticate() flow." };
|
|
151
|
+
}
|
|
152
|
+
const client = await createOAuth2Client(this.config.oauth);
|
|
153
|
+
const scopes = this.config.scopes ?? [
|
|
154
|
+
"https://www.googleapis.com/auth/gmail.readonly",
|
|
155
|
+
"https://www.googleapis.com/auth/gmail.send"
|
|
156
|
+
];
|
|
157
|
+
const authUrl = getAuthUrl(client, scopes);
|
|
158
|
+
const code = await codeProvider(authUrl);
|
|
159
|
+
const tokens = await exchangeCode(client, code);
|
|
160
|
+
await saveTokens(this.tokenSaver, this.config.account, tokens);
|
|
161
|
+
this.tokens = tokens;
|
|
162
|
+
this.client = client;
|
|
163
|
+
this.client.setCredentials(tokens);
|
|
164
|
+
const { google } = await import("googleapis");
|
|
165
|
+
this.gmail = google.gmail({ version: "v1", auth: this.client });
|
|
166
|
+
return { success: true, account: this.config.account };
|
|
167
|
+
} catch (err) {
|
|
168
|
+
return { success: false, error: errorMessage(err) };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// ── Sync ───────────────────────────────────────────────────────
|
|
172
|
+
async sync(options) {
|
|
173
|
+
this.ensureConnected();
|
|
174
|
+
if (options?.cursor) {
|
|
175
|
+
return this.syncIncremental(options.cursor, options.limit);
|
|
176
|
+
}
|
|
177
|
+
return this.syncFull(options);
|
|
178
|
+
}
|
|
179
|
+
/** Incremental sync using Gmail history API. */
|
|
180
|
+
async syncIncremental(startHistoryId, limit) {
|
|
181
|
+
const records = [];
|
|
182
|
+
const errors = [];
|
|
183
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
184
|
+
let pageToken;
|
|
185
|
+
let latestHistoryId = startHistoryId;
|
|
186
|
+
do {
|
|
187
|
+
const res = await this.gmail.users.history.list({
|
|
188
|
+
userId: "me",
|
|
189
|
+
startHistoryId,
|
|
190
|
+
historyTypes: ["messageAdded"],
|
|
191
|
+
...pageToken ? { pageToken } : {}
|
|
192
|
+
});
|
|
193
|
+
latestHistoryId = res.data.historyId ?? latestHistoryId;
|
|
194
|
+
const histories = res.data.history ?? [];
|
|
195
|
+
for (const h of histories) {
|
|
196
|
+
for (const added of h.messagesAdded ?? []) {
|
|
197
|
+
const msgId = added.message?.id;
|
|
198
|
+
if (!msgId || seenIds.has(msgId)) continue;
|
|
199
|
+
seenIds.add(msgId);
|
|
200
|
+
try {
|
|
201
|
+
const record = await this.fetchMessage(msgId);
|
|
202
|
+
records.push(record);
|
|
203
|
+
} catch (err) {
|
|
204
|
+
errors.push({ id: msgId, error: errorMessage(err) });
|
|
205
|
+
}
|
|
206
|
+
if (limit && records.length >= limit) {
|
|
207
|
+
return { records, cursor: latestHistoryId, hasMore: true, errors };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
pageToken = res.data.nextPageToken ?? void 0;
|
|
212
|
+
} while (pageToken);
|
|
213
|
+
return { records, cursor: latestHistoryId, hasMore: false, errors };
|
|
214
|
+
}
|
|
215
|
+
/** Full sync — list messages and fetch each one. */
|
|
216
|
+
async syncFull(options) {
|
|
217
|
+
const records = [];
|
|
218
|
+
const errors = [];
|
|
219
|
+
const maxResults = options?.limit ?? 100;
|
|
220
|
+
let query = "";
|
|
221
|
+
if (options?.since) {
|
|
222
|
+
const epoch = Math.floor(new Date(options.since).getTime() / 1e3);
|
|
223
|
+
query = `after:${epoch}`;
|
|
224
|
+
}
|
|
225
|
+
if (options?.filters?.q) {
|
|
226
|
+
query = query ? `${query} ${options.filters.q}` : String(options.filters.q);
|
|
227
|
+
}
|
|
228
|
+
let pageToken;
|
|
229
|
+
let collected = 0;
|
|
230
|
+
do {
|
|
231
|
+
const res = await this.gmail.users.messages.list({
|
|
232
|
+
userId: "me",
|
|
233
|
+
maxResults: Math.min(maxResults - collected, 100),
|
|
234
|
+
...query ? { q: query } : {},
|
|
235
|
+
...pageToken ? { pageToken } : {}
|
|
236
|
+
});
|
|
237
|
+
const messages = res.data.messages ?? [];
|
|
238
|
+
for (const msg of messages) {
|
|
239
|
+
try {
|
|
240
|
+
const record = await this.fetchMessage(msg.id);
|
|
241
|
+
records.push(record);
|
|
242
|
+
} catch (err) {
|
|
243
|
+
errors.push({ id: msg.id, error: errorMessage(err) });
|
|
244
|
+
}
|
|
245
|
+
collected++;
|
|
246
|
+
if (collected >= maxResults) break;
|
|
247
|
+
}
|
|
248
|
+
pageToken = res.data.nextPageToken ?? void 0;
|
|
249
|
+
} while (pageToken && collected < maxResults);
|
|
250
|
+
const profile = await this.gmail.users.getProfile({ userId: "me" });
|
|
251
|
+
const cursor = profile.data.historyId ?? void 0;
|
|
252
|
+
return {
|
|
253
|
+
records,
|
|
254
|
+
cursor,
|
|
255
|
+
hasMore: !!pageToken,
|
|
256
|
+
errors
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
// ── Push (send email) ─────────────────────────────────────────
|
|
260
|
+
async push(payload) {
|
|
261
|
+
this.ensureConnected();
|
|
262
|
+
try {
|
|
263
|
+
const toHeader = payload.to.map(formatAddress).join(", ");
|
|
264
|
+
const ccHeader = payload.cc.length ? `Cc: ${payload.cc.map(formatAddress).join(", ")}\r
|
|
265
|
+
` : "";
|
|
266
|
+
const bccHeader = payload.bcc.length ? `Bcc: ${payload.bcc.map(formatAddress).join(", ")}\r
|
|
267
|
+
` : "";
|
|
268
|
+
const mime = [
|
|
269
|
+
`To: ${toHeader}\r
|
|
270
|
+
`,
|
|
271
|
+
ccHeader,
|
|
272
|
+
bccHeader,
|
|
273
|
+
`Subject: ${payload.subject}\r
|
|
274
|
+
`,
|
|
275
|
+
`Content-Type: text/plain; charset="UTF-8"\r
|
|
276
|
+
`,
|
|
277
|
+
`\r
|
|
278
|
+
`,
|
|
279
|
+
payload.body ?? ""
|
|
280
|
+
].join("");
|
|
281
|
+
const encoded = Buffer.from(mime).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
282
|
+
const res = await this.gmail.users.messages.send({
|
|
283
|
+
userId: "me",
|
|
284
|
+
requestBody: { raw: encoded }
|
|
285
|
+
});
|
|
286
|
+
return { success: true, externalId: res.data.id };
|
|
287
|
+
} catch (err) {
|
|
288
|
+
return { success: false, error: errorMessage(err) };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// ── Internals ─────────────────────────────────────────────────
|
|
292
|
+
ensureConnected() {
|
|
293
|
+
if (!this.gmail || !this.config) {
|
|
294
|
+
throw new Error("GoogleGmailConnector is not connected. Call connect() first.");
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/** Fetch a single message by ID and parse into an EmailRecord. */
|
|
298
|
+
async fetchMessage(messageId) {
|
|
299
|
+
const res = await this.gmail.users.messages.get({
|
|
300
|
+
userId: "me",
|
|
301
|
+
id: messageId,
|
|
302
|
+
format: "full"
|
|
303
|
+
});
|
|
304
|
+
const msg = res.data;
|
|
305
|
+
const headers = msg.payload?.headers ?? [];
|
|
306
|
+
const getHeader = (name) => headers.find((h) => h.name.toLowerCase() === name.toLowerCase())?.value ?? "";
|
|
307
|
+
return {
|
|
308
|
+
gmailId: msg.id,
|
|
309
|
+
threadId: msg.threadId,
|
|
310
|
+
account: this.config.account,
|
|
311
|
+
subject: getHeader("Subject"),
|
|
312
|
+
from: parseAddress(getHeader("From")),
|
|
313
|
+
to: parseAddressList(getHeader("To")),
|
|
314
|
+
cc: parseAddressList(getHeader("Cc")),
|
|
315
|
+
bcc: parseAddressList(getHeader("Bcc")),
|
|
316
|
+
date: new Date(getHeader("Date")).toISOString(),
|
|
317
|
+
snippet: msg.snippet ?? "",
|
|
318
|
+
body: extractPlainTextBody(msg.payload),
|
|
319
|
+
labels: msg.labelIds ?? [],
|
|
320
|
+
isRead: !(msg.labelIds ?? []).includes("UNREAD"),
|
|
321
|
+
attachments: extractAttachments(msg.payload)
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
function extractAttachments(payload) {
|
|
326
|
+
if (!payload) return [];
|
|
327
|
+
const out = [];
|
|
328
|
+
const walk = (part) => {
|
|
329
|
+
if (!part) return;
|
|
330
|
+
const filename = part.filename ?? "";
|
|
331
|
+
const attachmentId = part.body?.attachmentId;
|
|
332
|
+
if (filename && attachmentId) {
|
|
333
|
+
const dispositionHeader = (part.headers ?? []).find(
|
|
334
|
+
(h) => h.name.toLowerCase() === "content-disposition"
|
|
335
|
+
);
|
|
336
|
+
const disposition = dispositionHeader?.value ?? "";
|
|
337
|
+
const isInline = disposition.toLowerCase().startsWith("inline");
|
|
338
|
+
if (!isInline) {
|
|
339
|
+
out.push({
|
|
340
|
+
attachmentId,
|
|
341
|
+
filename,
|
|
342
|
+
mimeType: part.mimeType ?? "application/octet-stream",
|
|
343
|
+
size: typeof part.body?.size === "number" ? part.body.size : 0
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (Array.isArray(part.parts)) {
|
|
348
|
+
for (const child of part.parts) walk(child);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
walk(payload);
|
|
352
|
+
return out;
|
|
353
|
+
}
|
|
354
|
+
function extractPlainTextBody(payload) {
|
|
355
|
+
if (!payload) return void 0;
|
|
356
|
+
if (payload.mimeType === "text/plain" && payload.body?.data) {
|
|
357
|
+
return decodeBase64Url(payload.body.data);
|
|
358
|
+
}
|
|
359
|
+
if (payload.parts) {
|
|
360
|
+
for (const part of payload.parts) {
|
|
361
|
+
if (part.mimeType === "text/plain" && part.body?.data) {
|
|
362
|
+
return decodeBase64Url(part.body.data);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
for (const part of payload.parts) {
|
|
366
|
+
if (part.mimeType?.startsWith("multipart/")) {
|
|
367
|
+
const result = extractPlainTextBody(part);
|
|
368
|
+
if (result) return result;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return void 0;
|
|
373
|
+
}
|
|
374
|
+
function decodeBase64Url(data) {
|
|
375
|
+
const base64 = data.replace(/-/g, "+").replace(/_/g, "/");
|
|
376
|
+
return Buffer.from(base64, "base64").toString("utf-8");
|
|
377
|
+
}
|
|
378
|
+
function parseAddress(raw) {
|
|
379
|
+
const match = raw.match(/^(.+?)\s*<([^>]+)>$/);
|
|
380
|
+
if (match) {
|
|
381
|
+
return { name: match[1].replace(/^["']|["']$/g, "").trim(), email: match[2] };
|
|
382
|
+
}
|
|
383
|
+
return { email: raw.trim() };
|
|
384
|
+
}
|
|
385
|
+
function parseAddressList(raw) {
|
|
386
|
+
if (!raw.trim()) return [];
|
|
387
|
+
const results = [];
|
|
388
|
+
let current = "";
|
|
389
|
+
let depth = 0;
|
|
390
|
+
for (const ch of raw) {
|
|
391
|
+
if (ch === "<") depth++;
|
|
392
|
+
else if (ch === ">") depth--;
|
|
393
|
+
else if (ch === "," && depth === 0) {
|
|
394
|
+
if (current.trim()) results.push(parseAddress(current.trim()));
|
|
395
|
+
current = "";
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
current += ch;
|
|
399
|
+
}
|
|
400
|
+
if (current.trim()) results.push(parseAddress(current.trim()));
|
|
401
|
+
return results;
|
|
402
|
+
}
|
|
403
|
+
function formatAddress(addr) {
|
|
404
|
+
return addr.name ? `${addr.name} <${addr.email}>` : addr.email;
|
|
405
|
+
}
|
|
406
|
+
function errorMessage(err) {
|
|
407
|
+
return err instanceof Error ? err.message : String(err);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export {
|
|
411
|
+
createOAuth2Client,
|
|
412
|
+
getAuthUrl,
|
|
413
|
+
exchangeCode,
|
|
414
|
+
createServiceAccountClient,
|
|
415
|
+
loadTokens,
|
|
416
|
+
saveTokens,
|
|
417
|
+
refreshIfNeeded,
|
|
418
|
+
GoogleGmailConnector
|
|
419
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -179,7 +179,7 @@ async function authGoogle(args) {
|
|
|
179
179
|
});
|
|
180
180
|
}
|
|
181
181
|
};
|
|
182
|
-
const { GoogleGmailConnector } = await import("./gmail-connector-
|
|
182
|
+
const { GoogleGmailConnector } = await import("./gmail-connector-VP5FF56J.js");
|
|
183
183
|
const connector = new GoogleGmailConnector({ tokenLoader, tokenSaver });
|
|
184
184
|
connector.config = {
|
|
185
185
|
account,
|
|
@@ -38,6 +38,14 @@ interface EmailAddress {
|
|
|
38
38
|
name?: string;
|
|
39
39
|
email: string;
|
|
40
40
|
}
|
|
41
|
+
interface EmailAttachment {
|
|
42
|
+
/** The Gmail attachment ID — can be used with users.messages.attachments.get */
|
|
43
|
+
attachmentId: string;
|
|
44
|
+
filename: string;
|
|
45
|
+
mimeType: string;
|
|
46
|
+
/** Size in bytes as reported by Gmail */
|
|
47
|
+
size: number;
|
|
48
|
+
}
|
|
41
49
|
interface EmailRecord {
|
|
42
50
|
gmailId: string;
|
|
43
51
|
threadId: string;
|
|
@@ -53,6 +61,7 @@ interface EmailRecord {
|
|
|
53
61
|
body?: string;
|
|
54
62
|
labels: string[];
|
|
55
63
|
isRead: boolean;
|
|
64
|
+
attachments: EmailAttachment[];
|
|
56
65
|
}
|
|
57
66
|
interface DriveOwner {
|
|
58
67
|
displayName: string;
|
|
@@ -275,4 +284,4 @@ declare class GoogleDriveConnector implements Connector<DriveFileRecord> {
|
|
|
275
284
|
private mapFile;
|
|
276
285
|
}
|
|
277
286
|
|
|
278
|
-
export { type CalendarAttendee, type CalendarConnectorOpts, type CalendarEventRecord, type DriveConnectorOpts, type DriveFileRecord, type DriveOwner, type EmailAddress, type EmailRecord, type GmailConnectorOpts, GoogleCalendarConnector, type GoogleConnectorConfig, GoogleDriveConnector, GoogleGmailConnector, type GoogleOAuthConfig, type GoogleServiceAccountConfig, type GoogleTokens, createOAuth2Client, createServiceAccountClient, exchangeCode, getAuthUrl, loadTokens, refreshIfNeeded, saveTokens };
|
|
287
|
+
export { type CalendarAttendee, type CalendarConnectorOpts, type CalendarEventRecord, type DriveConnectorOpts, type DriveFileRecord, type DriveOwner, type EmailAddress, type EmailAttachment, type EmailRecord, type GmailConnectorOpts, GoogleCalendarConnector, type GoogleConnectorConfig, GoogleDriveConnector, GoogleGmailConnector, type GoogleOAuthConfig, type GoogleServiceAccountConfig, type GoogleTokens, createOAuth2Client, createServiceAccountClient, exchangeCode, getAuthUrl, loadTokens, refreshIfNeeded, saveTokens };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { T as TokenUsage, L as LLMProvider, M as ModelInfo, R as ResolvedModel,
|
|
|
4
4
|
export { a as ChatParams, b as ChatResult, c as ContentBlock, d as ToolUse } from './provider-DLGUfnNx.js';
|
|
5
5
|
import { C as ConnectorConfig } from './connector-B4Mj0P1b.js';
|
|
6
6
|
export { A as AuthResult, a as Connector, b as ConnectorMeta, P as PushResult, S as SyncOptions, c as SyncResult } from './connector-B4Mj0P1b.js';
|
|
7
|
-
import { C as ChatResponderConfig, D as DataStore, H as HookBus, M as MessageStore, a as ChatResponder, b as MessageInterpreter, E as Extractor } from './chat-pipeline-
|
|
8
|
-
export { c as ChatPipeline, d as ChatPipelineConfig, e as DataStoreError, f as EntityContextDef, g as EntityFileSpec, h as EntitySource, i as ExtractedFile, j as ExtractedMemory, k as ExtractedTask, l as ExtractedUserContext, F as Filter, m as HookHandler, n as HookOptions, o as HookRegistration, I as InterpretationResult, L as LLMCallFn, p as MessageInterpreterConfig, P as PkLookup, Q as QueryOptions, R as RelationDef, q as RoutingDecision, r as RoutingRule, s as Row, S as SeedItem, t as SqliteAdapter, u as StoreResult, v as StoredAttachment, T as TableDefinition, w as TableInfoRow, x as TriageRouter, y as TriageRouterConfig, U as Unsubscribe } from './chat-pipeline-
|
|
7
|
+
import { C as ChatResponderConfig, D as DataStore, H as HookBus, M as MessageStore, a as ChatResponder, b as MessageInterpreter, E as Extractor } from './chat-pipeline-DuNX5WoL.js';
|
|
8
|
+
export { c as ChatPipeline, d as ChatPipelineConfig, e as DataStoreError, f as EntityContextDef, g as EntityFileSpec, h as EntitySource, i as ExtractedFile, j as ExtractedMemory, k as ExtractedTask, l as ExtractedUserContext, F as Filter, m as HookHandler, n as HookOptions, o as HookRegistration, I as InterpretationResult, L as LLMCallFn, p as MessageInterpreterConfig, P as PkLookup, Q as QueryOptions, R as RelationDef, q as RoutingDecision, r as RoutingRule, s as Row, S as SeedItem, t as SqliteAdapter, u as StoreResult, v as StoredAttachment, T as TableDefinition, w as TableInfoRow, x as TriageRouter, y as TriageRouterConfig, U as Unsubscribe } from './chat-pipeline-DuNX5WoL.js';
|
|
9
9
|
import 'better-sqlite3';
|
|
10
10
|
|
|
11
11
|
/** Execution adapter types — Story 1.5 / 3.4 / 3.5 */
|
|
@@ -1791,6 +1791,7 @@ declare class LoopDetector {
|
|
|
1791
1791
|
|
|
1792
1792
|
interface FeedbackEntry {
|
|
1793
1793
|
agentId: string;
|
|
1794
|
+
userId?: string;
|
|
1794
1795
|
taskId?: string;
|
|
1795
1796
|
issue: string;
|
|
1796
1797
|
rootCause?: string;
|
|
@@ -1805,6 +1806,7 @@ interface PlaybookEntry {
|
|
|
1805
1806
|
rule: string;
|
|
1806
1807
|
feedbackIds: string[];
|
|
1807
1808
|
projectScoped: boolean;
|
|
1809
|
+
clientId?: string;
|
|
1808
1810
|
agentIds?: string[];
|
|
1809
1811
|
}
|
|
1810
1812
|
interface SkillEntry {
|
|
@@ -1839,6 +1841,7 @@ declare class LearningPipeline {
|
|
|
1839
1841
|
*/
|
|
1840
1842
|
listFeedback(filter?: {
|
|
1841
1843
|
agentId?: string;
|
|
1844
|
+
userId?: string;
|
|
1842
1845
|
severity?: string;
|
|
1843
1846
|
repeatable?: boolean;
|
|
1844
1847
|
}): Promise<Array<Record<string, unknown>>>;
|
|
@@ -2163,6 +2166,16 @@ declare const searchConversationTool: {
|
|
|
2163
2166
|
handler: ToolHandler;
|
|
2164
2167
|
};
|
|
2165
2168
|
|
|
2169
|
+
/**
|
|
2170
|
+
* Shared agent resolution: slug → role → name (case-insensitive).
|
|
2171
|
+
*
|
|
2172
|
+
* LLMs see list_agents output like "AgentName (role)" and naturally use
|
|
2173
|
+
* the role as the identifier. All agent-resolving tools must accept
|
|
2174
|
+
* slug, role, OR name — not just slug.
|
|
2175
|
+
*/
|
|
2176
|
+
|
|
2177
|
+
declare function resolveAgent(db: DataStore, ref: string): Promise<Record<string, unknown> | null>;
|
|
2178
|
+
|
|
2166
2179
|
/**
|
|
2167
2180
|
* Built-in tools: Entity creation — agents, projects.
|
|
2168
2181
|
*/
|
|
@@ -2287,4 +2300,4 @@ declare function isLoginRequired(stdout: string): boolean;
|
|
|
2287
2300
|
/** Rewrite local image paths to prevent CLI auto-embedding as vision content. */
|
|
2288
2301
|
declare function deactivateLocalImagePaths(prompt: string): string;
|
|
2289
2302
|
|
|
2290
|
-
export { AGENT_STATUSES, type AgentConfig, type AgentDefinition, type AgentFilter, type AgentRecord, AgentRegistry, type AgentStatus, ApiExecutionAdapter, type ApprovalResponse, type ApprovalStatus, AuditEmitter, type AuditEvent, BackupManager, type BotConfig, BreakerState, type BudgetCheck, type BudgetConfig, BudgetController, CORE_MIGRATIONS, ChannelAdapter, ChannelRegistry, ChannelRegistryError, type ChatConfig, ChatMessage, ChatPipelineV2, type ChatPipelineV2Config, ChatResponder, ChatResponderConfig, ChatSessionManager, CircuitBreaker, type CircuitBreakerConfig, CliExecutionAdapter, type ColumnValidator, ColumnValidatorImpl, type ConfigLoadError, type ConfigLoadResult, ConnectorConfig, DEFAULTS, DEFAULT_CONFIG, type DataConfig, DataStore, type DefaultLLMCallConfig, DeterministicAdapter, type DeterministicConfig, type DomainEntityContextOptions, type DomainSchemaOptions, DriftGate, EVENTS, type EntityColumnDef, type EntityConfig, type ExecutionAdapter, type ExecutionConfig, type ExecutionEngineConfig, Extractor, type FeedbackEntry, type GateFinding, type GateInput, type GateResult, GateRunner, type GateVerdict, GovernanceGate, HealthStatus, HookBus, InboundMessage, LLMProvider, LearningPipeline, type LearningPipelineConfig, type LoopDetection, LoopDetector, type LoopDetectorConfig, LoopType, MAX_CHAIN_DEPTH, MessageInterpreter, MessagePipeline, MessageStore, type ModelConfig, ModelInfo, ModelRouter, NdjsonLogger, NotificationQueue, type PackageMigration, type PackageUpdate, type ParsedStream, type PermissionPrompt, type PermissionProvider, PermissionRelay, type PermissionRelayConfig, type PlaybookEntry, ProviderRegistry, QAGate, QualityGate, RUN_STATUSES, type RenderConfig, ResolvedModel, type RetryPolicy, type RoutingConfig, type RunContext, RunManager, type RunResult, type RunStatus, type SafetyConfig, type SanitizerOptions, type Schedule, type ScheduleDef, Scheduler, type SchemaError, type SecretInput, type SecretMeta, SecretStore, type SecurityConfig, SessionKey, SessionManager, type SkillEntry, type StepRef, type SystemContextOptions, TASK_STATUSES, type TaskDefinition, TaskQueue, type TaskRecord, type TaskStatus, TokenUsage, type ToolContext, type ToolDefinition, type ToolHandler, UpdateChecker, type UpdateConfig, UpdateManager, type UpdateManifest, type UsageSummary, type User, type UserInput, UserRegistry, WakeupQueue, type WorkflowConfigEntry, type WorkflowDefinition$1 as WorkflowDefinition, WorkflowEngine, type WorkflowRunRecord, type WorkflowRunStatus, type WorkflowStep$1 as WorkflowStep, type WorkflowStepConfig, type WorkflowTrigger, _resetConfig, addTaskCommentTool, areDependenciesMet, autoUpdate, buildAgentBindings, buildChainOrigin, buildProcessEnv, buildSystemContext, cancelTaskTool, checkAllowlist, checkChainDepth, checkMentionGate, chunkText, classifyUpdate, compareVersions, coordinatorTools, createAgentTool, createConfigRevision, createDefaultLLMCall, createProjectTool, deactivateLocalImagePaths, defineCoreEntityContexts, defineCoreTables, defineDomainEntityContexts, defineDomainTables, detectCycle, discoverChannels, discoverProviders, dispatchTaskTool, formatText, getActiveTasksTool, getAgentDetailTool, getAgentStatusTool, getConfig, getSystemStatusTool, getTaskStatusTool, initConfig, interpolate, interpolateEnv, isLoginRequired, isMaxTurns, listAgentsTool, listFilesTool, listProjectsTool, loadConfig, nativeTools, parseClaudeStream, parseVersion, readConversationTool, readFileTool, reassignTaskTool, registerExecutionEngine, registerFileTool, runPackageMigrations, sanitize, searchConversationTool, sendFileTool, sendMessageTool, topologicalSort, truncateAtWord, validateConfig };
|
|
2303
|
+
export { AGENT_STATUSES, type AgentConfig, type AgentDefinition, type AgentFilter, type AgentRecord, AgentRegistry, type AgentStatus, ApiExecutionAdapter, type ApprovalResponse, type ApprovalStatus, AuditEmitter, type AuditEvent, BackupManager, type BotConfig, BreakerState, type BudgetCheck, type BudgetConfig, BudgetController, CORE_MIGRATIONS, ChannelAdapter, ChannelRegistry, ChannelRegistryError, type ChatConfig, ChatMessage, ChatPipelineV2, type ChatPipelineV2Config, ChatResponder, ChatResponderConfig, ChatSessionManager, CircuitBreaker, type CircuitBreakerConfig, CliExecutionAdapter, type ColumnValidator, ColumnValidatorImpl, type ConfigLoadError, type ConfigLoadResult, ConnectorConfig, DEFAULTS, DEFAULT_CONFIG, type DataConfig, DataStore, type DefaultLLMCallConfig, DeterministicAdapter, type DeterministicConfig, type DomainEntityContextOptions, type DomainSchemaOptions, DriftGate, EVENTS, type EntityColumnDef, type EntityConfig, type ExecutionAdapter, type ExecutionConfig, type ExecutionEngineConfig, Extractor, type FeedbackEntry, type GateFinding, type GateInput, type GateResult, GateRunner, type GateVerdict, GovernanceGate, HealthStatus, HookBus, InboundMessage, LLMProvider, LearningPipeline, type LearningPipelineConfig, type LoopDetection, LoopDetector, type LoopDetectorConfig, LoopType, MAX_CHAIN_DEPTH, MessageInterpreter, MessagePipeline, MessageStore, type ModelConfig, ModelInfo, ModelRouter, NdjsonLogger, NotificationQueue, type PackageMigration, type PackageUpdate, type ParsedStream, type PermissionPrompt, type PermissionProvider, PermissionRelay, type PermissionRelayConfig, type PlaybookEntry, ProviderRegistry, QAGate, QualityGate, RUN_STATUSES, type RenderConfig, ResolvedModel, type RetryPolicy, type RoutingConfig, type RunContext, RunManager, type RunResult, type RunStatus, type SafetyConfig, type SanitizerOptions, type Schedule, type ScheduleDef, Scheduler, type SchemaError, type SecretInput, type SecretMeta, SecretStore, type SecurityConfig, SessionKey, SessionManager, type SkillEntry, type StepRef, type SystemContextOptions, TASK_STATUSES, type TaskDefinition, TaskQueue, type TaskRecord, type TaskStatus, TokenUsage, type ToolContext, type ToolDefinition, type ToolHandler, UpdateChecker, type UpdateConfig, UpdateManager, type UpdateManifest, type UsageSummary, type User, type UserInput, UserRegistry, WakeupQueue, type WorkflowConfigEntry, type WorkflowDefinition$1 as WorkflowDefinition, WorkflowEngine, type WorkflowRunRecord, type WorkflowRunStatus, type WorkflowStep$1 as WorkflowStep, type WorkflowStepConfig, type WorkflowTrigger, _resetConfig, addTaskCommentTool, areDependenciesMet, autoUpdate, buildAgentBindings, buildChainOrigin, buildProcessEnv, buildSystemContext, cancelTaskTool, checkAllowlist, checkChainDepth, checkMentionGate, chunkText, classifyUpdate, compareVersions, coordinatorTools, createAgentTool, createConfigRevision, createDefaultLLMCall, createProjectTool, deactivateLocalImagePaths, defineCoreEntityContexts, defineCoreTables, defineDomainEntityContexts, defineDomainTables, detectCycle, discoverChannels, discoverProviders, dispatchTaskTool, formatText, getActiveTasksTool, getAgentDetailTool, getAgentStatusTool, getConfig, getSystemStatusTool, getTaskStatusTool, initConfig, interpolate, interpolateEnv, isLoginRequired, isMaxTurns, listAgentsTool, listFilesTool, listProjectsTool, loadConfig, nativeTools, parseClaudeStream, parseVersion, readConversationTool, readFileTool, reassignTaskTool, registerExecutionEngine, registerFileTool, resolveAgent, runPackageMigrations, sanitize, searchConversationTool, sendFileTool, sendMessageTool, topologicalSort, truncateAtWord, validateConfig };
|
package/dist/index.js
CHANGED
|
@@ -1662,6 +1662,7 @@ var ChatPipeline = class {
|
|
|
1662
1662
|
this.messageFilter = config.messageFilter;
|
|
1663
1663
|
this.capabilities = config.capabilities;
|
|
1664
1664
|
this.dedupWindowMs = config.dedupWindowMs ?? DEFAULT_DEDUP_WINDOW_MS;
|
|
1665
|
+
this.skipAck = config.skipAck ?? false;
|
|
1665
1666
|
this.tasks = config.tasks;
|
|
1666
1667
|
this.wakeups = config.wakeups;
|
|
1667
1668
|
this.messageStore = new MessageStore(db, hooks);
|
|
@@ -1695,6 +1696,7 @@ var ChatPipeline = class {
|
|
|
1695
1696
|
dedupWindowMs;
|
|
1696
1697
|
tasks;
|
|
1697
1698
|
wakeups;
|
|
1699
|
+
skipAck;
|
|
1698
1700
|
// In-memory thread → channel mapping for response routing
|
|
1699
1701
|
// (before thread_task_map exists)
|
|
1700
1702
|
threadChannelMap = /* @__PURE__ */ new Map();
|
|
@@ -1731,24 +1733,26 @@ var ChatPipeline = class {
|
|
|
1731
1733
|
const dir = m.direction === "inbound" ? "User" : "Bot";
|
|
1732
1734
|
return `${dir}: ${m.body?.slice(0, 200) ?? ""}`;
|
|
1733
1735
|
}).join("\n");
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1736
|
+
if (!this.skipAck) {
|
|
1737
|
+
const ackResponse = await this.responder.respond({
|
|
1738
|
+
messageBody: msg.body,
|
|
1739
|
+
threadId: threadTs,
|
|
1740
|
+
channel: this.channel,
|
|
1741
|
+
capabilities: this.capabilities,
|
|
1742
|
+
additionalContext: historyContext ? `
|
|
1740
1743
|
|
|
1741
1744
|
Recent conversation history:
|
|
1742
1745
|
${historyContext}` : void 0
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1746
|
+
});
|
|
1747
|
+
await this.responder.sendResponse({
|
|
1748
|
+
text: ackResponse,
|
|
1749
|
+
channel: this.channel,
|
|
1750
|
+
threadId: threadTs,
|
|
1751
|
+
source: "responder",
|
|
1752
|
+
skipFilter: true,
|
|
1753
|
+
skipRedundancyCheck: true
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1752
1756
|
const dispatchPromise = this.interpretAndDispatch(messageId, msg, threadTs, channelId);
|
|
1753
1757
|
this.lastDispatch = dispatchPromise;
|
|
1754
1758
|
void dispatchPromise;
|
|
@@ -2966,6 +2970,7 @@ function defineCoreTables(db) {
|
|
|
2966
2970
|
columns: {
|
|
2967
2971
|
id: "TEXT PRIMARY KEY",
|
|
2968
2972
|
agent_id: "TEXT NOT NULL",
|
|
2973
|
+
user_id: "TEXT",
|
|
2969
2974
|
task_id: "TEXT",
|
|
2970
2975
|
issue: "TEXT NOT NULL",
|
|
2971
2976
|
root_cause: "TEXT",
|
|
@@ -2978,7 +2983,8 @@ function defineCoreTables(db) {
|
|
|
2978
2983
|
},
|
|
2979
2984
|
tableConstraints: [
|
|
2980
2985
|
"CREATE INDEX IF NOT EXISTS idx_feedback_agent ON feedback(agent_id, created_at)",
|
|
2981
|
-
"CREATE INDEX IF NOT EXISTS idx_feedback_issue ON feedback(issue)"
|
|
2986
|
+
"CREATE INDEX IF NOT EXISTS idx_feedback_issue ON feedback(issue)",
|
|
2987
|
+
"CREATE INDEX IF NOT EXISTS idx_feedback_user ON feedback(user_id)"
|
|
2982
2988
|
]
|
|
2983
2989
|
});
|
|
2984
2990
|
db.define("playbooks", {
|
|
@@ -2988,6 +2994,7 @@ function defineCoreTables(db) {
|
|
|
2988
2994
|
rule: "TEXT NOT NULL",
|
|
2989
2995
|
feedback_ids: "TEXT NOT NULL DEFAULT '[]'",
|
|
2990
2996
|
project_scoped: "INTEGER NOT NULL DEFAULT 1",
|
|
2997
|
+
client_id: "TEXT",
|
|
2991
2998
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
2992
2999
|
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
2993
3000
|
deleted_at: "TEXT"
|
|
@@ -3113,6 +3120,18 @@ var CORE_MIGRATIONS = [
|
|
|
3113
3120
|
{
|
|
3114
3121
|
version: "006_schedules_next_index",
|
|
3115
3122
|
sql: `CREATE INDEX IF NOT EXISTS idx_schedules_next ON schedules(enabled, next_fire_at) WHERE deleted_at IS NULL`
|
|
3123
|
+
},
|
|
3124
|
+
{
|
|
3125
|
+
version: "biab:2.5.0:feedback-user-id",
|
|
3126
|
+
sql: `ALTER TABLE feedback ADD COLUMN user_id TEXT`
|
|
3127
|
+
},
|
|
3128
|
+
{
|
|
3129
|
+
version: "biab:2.5.0:feedback-user-idx",
|
|
3130
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_feedback_user ON feedback(user_id)`
|
|
3131
|
+
},
|
|
3132
|
+
{
|
|
3133
|
+
version: "biab:2.5.0:playbooks-client-id",
|
|
3134
|
+
sql: `ALTER TABLE playbooks ADD COLUMN client_id TEXT`
|
|
3116
3135
|
}
|
|
3117
3136
|
];
|
|
3118
3137
|
|
|
@@ -5932,6 +5951,7 @@ var LearningPipeline = class {
|
|
|
5932
5951
|
async captureFeedback(entry) {
|
|
5933
5952
|
const row = await this.db.insert("feedback", {
|
|
5934
5953
|
agent_id: entry.agentId,
|
|
5954
|
+
user_id: entry.userId,
|
|
5935
5955
|
task_id: entry.taskId,
|
|
5936
5956
|
issue: entry.issue,
|
|
5937
5957
|
root_cause: entry.rootCause,
|
|
@@ -5945,6 +5965,7 @@ var LearningPipeline = class {
|
|
|
5945
5965
|
await this.hooks.emit("learning.feedback_captured", {
|
|
5946
5966
|
feedbackId,
|
|
5947
5967
|
agentId: entry.agentId,
|
|
5968
|
+
userId: entry.userId,
|
|
5948
5969
|
issue: entry.issue,
|
|
5949
5970
|
severity: entry.severity
|
|
5950
5971
|
});
|
|
@@ -5959,6 +5980,7 @@ var LearningPipeline = class {
|
|
|
5959
5980
|
async listFeedback(filter) {
|
|
5960
5981
|
const where = {};
|
|
5961
5982
|
if (filter?.agentId) where["agent_id"] = filter.agentId;
|
|
5983
|
+
if (filter?.userId) where["user_id"] = filter.userId;
|
|
5962
5984
|
if (filter?.severity) where["severity"] = filter.severity;
|
|
5963
5985
|
if (filter?.repeatable !== void 0) where["repeatable"] = filter.repeatable ? 1 : 0;
|
|
5964
5986
|
return this.db.query("feedback", Object.keys(where).length ? { where } : void 0);
|
|
@@ -6000,7 +6022,8 @@ var LearningPipeline = class {
|
|
|
6000
6022
|
pattern: entry.pattern,
|
|
6001
6023
|
rule: entry.rule,
|
|
6002
6024
|
feedback_ids: JSON.stringify(entry.feedbackIds),
|
|
6003
|
-
project_scoped: entry.projectScoped ? 1 : 0
|
|
6025
|
+
project_scoped: entry.projectScoped ? 1 : 0,
|
|
6026
|
+
client_id: entry.clientId
|
|
6004
6027
|
});
|
|
6005
6028
|
const playbookId = row["id"];
|
|
6006
6029
|
if (entry.agentIds) {
|
|
@@ -6662,6 +6685,25 @@ var registerFileTool = {
|
|
|
6662
6685
|
|
|
6663
6686
|
// src/core/orchestrator/tools/task-ops.ts
|
|
6664
6687
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6688
|
+
|
|
6689
|
+
// src/core/orchestrator/tools/resolve-agent.ts
|
|
6690
|
+
async function resolveAgent(db, ref) {
|
|
6691
|
+
const bySlug = await db.query("agents", { where: { slug: ref } });
|
|
6692
|
+
if (bySlug[0] && !bySlug[0].deleted_at) return bySlug[0];
|
|
6693
|
+
const byRole = await db.query("agents", { where: { role: ref } });
|
|
6694
|
+
const activeByRole = byRole.find((a) => !a.deleted_at);
|
|
6695
|
+
if (activeByRole) return activeByRole;
|
|
6696
|
+
const byName = await db.query("agents", { where: { name: ref } });
|
|
6697
|
+
const activeByName = byName.find((a) => !a.deleted_at);
|
|
6698
|
+
if (activeByName) return activeByName;
|
|
6699
|
+
const lower = ref.toLowerCase();
|
|
6700
|
+
const all = await db.query("agents");
|
|
6701
|
+
return all.find(
|
|
6702
|
+
(a) => !a.deleted_at && (a.slug?.toLowerCase() === lower || a.role?.toLowerCase() === lower || a.name?.toLowerCase() === lower)
|
|
6703
|
+
) ?? null;
|
|
6704
|
+
}
|
|
6705
|
+
|
|
6706
|
+
// src/core/orchestrator/tools/task-ops.ts
|
|
6665
6707
|
var dispatchTaskTool = {
|
|
6666
6708
|
definition: {
|
|
6667
6709
|
name: "dispatch_task",
|
|
@@ -6671,15 +6713,14 @@ var dispatchTaskTool = {
|
|
|
6671
6713
|
properties: {
|
|
6672
6714
|
title: { type: "string", description: "Short task title" },
|
|
6673
6715
|
description: { type: "string", description: "Detailed task description" },
|
|
6674
|
-
agent_slug: { type: "string", description: 'Agent slug
|
|
6716
|
+
agent_slug: { type: "string", description: 'Agent slug, role, or name (e.g. "eddy", "engineer", "Eddy")' },
|
|
6675
6717
|
priority: { type: "number", description: "Priority 1-10 (lower = higher priority). Default: 5" }
|
|
6676
6718
|
},
|
|
6677
6719
|
required: ["title", "agent_slug"]
|
|
6678
6720
|
}
|
|
6679
6721
|
},
|
|
6680
6722
|
handler: async (input, ctx) => {
|
|
6681
|
-
const
|
|
6682
|
-
const agent = agents[0];
|
|
6723
|
+
const agent = await resolveAgent(ctx.db, input.agent_slug);
|
|
6683
6724
|
if (!agent) return `Error: agent "${input.agent_slug}" not found.`;
|
|
6684
6725
|
const row = await ctx.db.insert("tasks", {
|
|
6685
6726
|
id: randomUUID2(),
|
|
@@ -6719,7 +6760,7 @@ var reassignTaskTool = {
|
|
|
6719
6760
|
type: "object",
|
|
6720
6761
|
properties: {
|
|
6721
6762
|
task_id: { type: "string", description: "Task ID to reassign" },
|
|
6722
|
-
new_agent_slug: { type: "string", description: "Agent slug to reassign to" }
|
|
6763
|
+
new_agent_slug: { type: "string", description: "Agent slug, role, or name to reassign to" }
|
|
6723
6764
|
},
|
|
6724
6765
|
required: ["task_id", "new_agent_slug"]
|
|
6725
6766
|
}
|
|
@@ -6727,8 +6768,7 @@ var reassignTaskTool = {
|
|
|
6727
6768
|
handler: async (input, ctx) => {
|
|
6728
6769
|
const task = await ctx.db.get("tasks", { id: input.task_id });
|
|
6729
6770
|
if (!task) return `Error: task ${input.task_id} not found.`;
|
|
6730
|
-
const
|
|
6731
|
-
const newAgent = agents[0];
|
|
6771
|
+
const newAgent = await resolveAgent(ctx.db, input.new_agent_slug);
|
|
6732
6772
|
if (!newAgent) return `Error: agent "${input.new_agent_slug}" not found.`;
|
|
6733
6773
|
await ctx.db.update("tasks", { id: input.task_id }, { status: "cancelled" });
|
|
6734
6774
|
const row = await ctx.db.insert("tasks", {
|
|
@@ -6776,14 +6816,13 @@ var getAgentStatusTool = {
|
|
|
6776
6816
|
input_schema: {
|
|
6777
6817
|
type: "object",
|
|
6778
6818
|
properties: {
|
|
6779
|
-
agent_slug: { type: "string", description: 'Agent slug (e.g. "engineer")' }
|
|
6819
|
+
agent_slug: { type: "string", description: 'Agent slug, role, or name (e.g. "eddy", "engineer", "Eddy")' }
|
|
6780
6820
|
},
|
|
6781
6821
|
required: ["agent_slug"]
|
|
6782
6822
|
}
|
|
6783
6823
|
},
|
|
6784
6824
|
handler: async (input, ctx) => {
|
|
6785
|
-
const
|
|
6786
|
-
const agent = agents[0];
|
|
6825
|
+
const agent = await resolveAgent(ctx.db, input.agent_slug);
|
|
6787
6826
|
if (!agent) return `Agent "${input.agent_slug}" not found.`;
|
|
6788
6827
|
const tasks = await ctx.db.query("tasks", { where: { assignee_id: agent.id, status: "todo" } });
|
|
6789
6828
|
const runs = await ctx.db.query("runs", { where: { agent_id: agent.id }, limit: 5 });
|
|
@@ -6867,8 +6906,7 @@ var getAgentDetailTool = {
|
|
|
6867
6906
|
}
|
|
6868
6907
|
},
|
|
6869
6908
|
handler: async (input, ctx) => {
|
|
6870
|
-
const
|
|
6871
|
-
const agent = agents[0];
|
|
6909
|
+
const agent = await resolveAgent(ctx.db, input.agent_slug);
|
|
6872
6910
|
if (!agent) return `Agent "${input.agent_slug}" not found.`;
|
|
6873
6911
|
const skills = await ctx.db.query("agent_skills", { where: { agent_id: agent.id } }).catch(() => []);
|
|
6874
6912
|
const skillNames = [];
|
|
@@ -7544,6 +7582,7 @@ export {
|
|
|
7544
7582
|
reassignTaskTool,
|
|
7545
7583
|
registerExecutionEngine,
|
|
7546
7584
|
registerFileTool,
|
|
7585
|
+
resolveAgent,
|
|
7547
7586
|
runPackageMigrations,
|
|
7548
7587
|
sanitize,
|
|
7549
7588
|
searchConversationTool,
|