botinabox 2.5.0 → 2.5.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/README.md +2 -2
- package/bin/botinabox.mjs +1 -1
- package/dist/channels/slack/index.d.ts +1 -1
- package/dist/{chat-pipeline-DuNX5WoL.d.ts → chat-pipeline-BWrtVqEP.d.ts} +0 -3
- package/dist/connectors/google/index.d.ts +67 -1
- package/dist/connectors/google/index.js +240 -0
- package/dist/index.d.ts +2 -5
- package/dist/index.js +17 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ A modular TypeScript framework for building multi-agent bots with LLM orchestrat
|
|
|
24
24
|
- **Event-driven hooks** -- Priority-ordered, filter-based event bus for decoupled communication.
|
|
25
25
|
- **Budget controls** -- Per-agent and global cost tracking with warning thresholds and hard stops.
|
|
26
26
|
- **Scheduling** -- Database-backed cron and one-time schedules.
|
|
27
|
-
- **Connectors** -- Google Gmail and
|
|
27
|
+
- **Connectors** -- Google Gmail, Calendar, and Drive via OAuth2 and service account.
|
|
28
28
|
- **Security** -- Input sanitization, field length enforcement, audit logging, HMAC webhook verification.
|
|
29
29
|
|
|
30
30
|
## Install
|
|
@@ -110,7 +110,7 @@ tasks.startPolling();
|
|
|
110
110
|
| `botinabox/slack` | Slack channel adapter (`SlackAdapter`) |
|
|
111
111
|
| `botinabox/discord` | Discord channel adapter (`DiscordAdapter`) |
|
|
112
112
|
| `botinabox/webhook` | Webhook channel adapter with HMAC verification (`WebhookAdapter`) |
|
|
113
|
-
| `botinabox/google` | Google connectors -- Gmail and
|
|
113
|
+
| `botinabox/google` | Google connectors -- Gmail, Calendar, and Drive via OAuth2 |
|
|
114
114
|
|
|
115
115
|
## Architecture
|
|
116
116
|
|
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-BWrtVqEP.js';
|
|
3
3
|
import 'better-sqlite3';
|
|
4
4
|
import '../../provider-DLGUfnNx.js';
|
|
5
5
|
|
|
@@ -584,8 +584,6 @@ 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;
|
|
589
587
|
/** TaskQueue instance — required for task dispatch */
|
|
590
588
|
tasks: {
|
|
591
589
|
create(task: Record<string, unknown>): Promise<string>;
|
|
@@ -610,7 +608,6 @@ declare class ChatPipeline {
|
|
|
610
608
|
private readonly dedupWindowMs;
|
|
611
609
|
private readonly tasks;
|
|
612
610
|
private readonly wakeups;
|
|
613
|
-
private readonly skipAck;
|
|
614
611
|
private readonly threadChannelMap;
|
|
615
612
|
/** Last dispatch promise — exposed for testing. */
|
|
616
613
|
lastDispatch: Promise<void>;
|
|
@@ -54,6 +54,29 @@ interface EmailRecord {
|
|
|
54
54
|
labels: string[];
|
|
55
55
|
isRead: boolean;
|
|
56
56
|
}
|
|
57
|
+
interface DriveOwner {
|
|
58
|
+
displayName: string;
|
|
59
|
+
emailAddress: string;
|
|
60
|
+
}
|
|
61
|
+
interface DriveFileRecord {
|
|
62
|
+
driveFileId: string;
|
|
63
|
+
account: string;
|
|
64
|
+
name: string;
|
|
65
|
+
mimeType: string;
|
|
66
|
+
webViewLink: string;
|
|
67
|
+
webContentLink?: string;
|
|
68
|
+
/** ISO 8601 */
|
|
69
|
+
modifiedTime: string;
|
|
70
|
+
/** ISO 8601 */
|
|
71
|
+
createdTime: string;
|
|
72
|
+
size?: number;
|
|
73
|
+
parents?: string[];
|
|
74
|
+
description?: string;
|
|
75
|
+
owners: DriveOwner[];
|
|
76
|
+
lastModifyingUser?: DriveOwner;
|
|
77
|
+
starred: boolean;
|
|
78
|
+
trashed: boolean;
|
|
79
|
+
}
|
|
57
80
|
interface CalendarAttendee {
|
|
58
81
|
email: string;
|
|
59
82
|
displayName?: string;
|
|
@@ -209,4 +232,47 @@ declare class GoogleCalendarConnector implements Connector<CalendarEventRecord>
|
|
|
209
232
|
private mapEvent;
|
|
210
233
|
}
|
|
211
234
|
|
|
212
|
-
|
|
235
|
+
/**
|
|
236
|
+
* Google Drive connector — pulls file metadata from Drive.
|
|
237
|
+
*
|
|
238
|
+
* Produces `DriveFileRecord` objects. Does NOT write to any database
|
|
239
|
+
* table; the consuming application decides how to store records.
|
|
240
|
+
*
|
|
241
|
+
* Supports incremental sync via Drive Changes API (startPageToken)
|
|
242
|
+
* and full sync via files.list with optional folder/MIME filters.
|
|
243
|
+
*/
|
|
244
|
+
|
|
245
|
+
interface DriveConnectorOpts {
|
|
246
|
+
/** Load persisted tokens for a given account key (OAuth2 flow only). */
|
|
247
|
+
tokenLoader?: (key: string) => Promise<string | null>;
|
|
248
|
+
/** Persist tokens for a given account key (OAuth2 flow only). */
|
|
249
|
+
tokenSaver?: (key: string, value: string) => Promise<void>;
|
|
250
|
+
}
|
|
251
|
+
declare class GoogleDriveConnector implements Connector<DriveFileRecord> {
|
|
252
|
+
readonly id = "google-drive";
|
|
253
|
+
readonly meta: ConnectorMeta;
|
|
254
|
+
private tokenLoader?;
|
|
255
|
+
private tokenSaver?;
|
|
256
|
+
private client;
|
|
257
|
+
private config;
|
|
258
|
+
private tokens;
|
|
259
|
+
private drive;
|
|
260
|
+
constructor(opts?: DriveConnectorOpts);
|
|
261
|
+
connect(config: GoogleConnectorConfig): Promise<void>;
|
|
262
|
+
disconnect(): Promise<void>;
|
|
263
|
+
healthCheck(): Promise<{
|
|
264
|
+
ok: boolean;
|
|
265
|
+
account?: string;
|
|
266
|
+
error?: string;
|
|
267
|
+
}>;
|
|
268
|
+
authenticate(codeProvider: (authUrl: string) => Promise<string>): Promise<AuthResult>;
|
|
269
|
+
sync(options?: SyncOptions): Promise<SyncResult<DriveFileRecord>>;
|
|
270
|
+
/** Incremental sync using Drive Changes API. */
|
|
271
|
+
private syncIncremental;
|
|
272
|
+
/** Full sync using files.list. */
|
|
273
|
+
private syncFull;
|
|
274
|
+
private ensureConnected;
|
|
275
|
+
private mapFile;
|
|
276
|
+
}
|
|
277
|
+
|
|
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 };
|
|
@@ -235,8 +235,248 @@ var GoogleCalendarConnector = class {
|
|
|
235
235
|
function errorMessage(err) {
|
|
236
236
|
return err instanceof Error ? err.message : String(err);
|
|
237
237
|
}
|
|
238
|
+
|
|
239
|
+
// src/connectors/google/drive-connector.ts
|
|
240
|
+
var FILE_FIELDS = "id, name, mimeType, webViewLink, webContentLink, modifiedTime, createdTime, size, parents, description, owners, lastModifyingUser, starred, trashed";
|
|
241
|
+
var GoogleDriveConnector = class {
|
|
242
|
+
id = "google-drive";
|
|
243
|
+
meta = {
|
|
244
|
+
displayName: "Google Drive",
|
|
245
|
+
provider: "google",
|
|
246
|
+
dataType: "document"
|
|
247
|
+
};
|
|
248
|
+
tokenLoader;
|
|
249
|
+
tokenSaver;
|
|
250
|
+
client = null;
|
|
251
|
+
config = null;
|
|
252
|
+
tokens = null;
|
|
253
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
254
|
+
drive = null;
|
|
255
|
+
constructor(opts = {}) {
|
|
256
|
+
this.tokenLoader = opts.tokenLoader;
|
|
257
|
+
this.tokenSaver = opts.tokenSaver;
|
|
258
|
+
}
|
|
259
|
+
// ── Lifecycle ──────────────────────────────────────────────────
|
|
260
|
+
async connect(config) {
|
|
261
|
+
this.config = config;
|
|
262
|
+
const scopes = config.scopes ?? [
|
|
263
|
+
"https://www.googleapis.com/auth/drive.readonly"
|
|
264
|
+
];
|
|
265
|
+
if (config.serviceAccount) {
|
|
266
|
+
this.client = await createServiceAccountClient(config.serviceAccount, scopes);
|
|
267
|
+
} else if (config.oauth) {
|
|
268
|
+
this.client = await createOAuth2Client(config.oauth);
|
|
269
|
+
if (!this.tokenLoader) {
|
|
270
|
+
throw new Error("tokenLoader required for OAuth2 flow");
|
|
271
|
+
}
|
|
272
|
+
this.tokens = await loadTokens(this.tokenLoader, config.account);
|
|
273
|
+
if (!this.tokens) {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`No stored tokens for account ${config.account}. Complete the OAuth flow first.`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
this.tokens = await refreshIfNeeded(
|
|
279
|
+
this.client,
|
|
280
|
+
this.tokens,
|
|
281
|
+
this.tokenSaver ? async (t) => saveTokens(this.tokenSaver, config.account, t) : void 0
|
|
282
|
+
);
|
|
283
|
+
this.client.setCredentials(this.tokens);
|
|
284
|
+
} else {
|
|
285
|
+
throw new Error("Either serviceAccount or oauth config is required");
|
|
286
|
+
}
|
|
287
|
+
const { google } = await import("googleapis");
|
|
288
|
+
this.drive = google.drive({ version: "v3", auth: this.client });
|
|
289
|
+
}
|
|
290
|
+
async disconnect() {
|
|
291
|
+
this.client = null;
|
|
292
|
+
this.drive = null;
|
|
293
|
+
this.tokens = null;
|
|
294
|
+
this.config = null;
|
|
295
|
+
}
|
|
296
|
+
async healthCheck() {
|
|
297
|
+
try {
|
|
298
|
+
this.ensureConnected();
|
|
299
|
+
const res = await this.drive.about.get({ fields: "user" });
|
|
300
|
+
return { ok: true, account: res.data.user?.emailAddress ?? this.config.account };
|
|
301
|
+
} catch (err) {
|
|
302
|
+
return { ok: false, error: errorMessage2(err) };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// ── Auth ───────────────────────────────────────────────────────
|
|
306
|
+
async authenticate(codeProvider) {
|
|
307
|
+
if (!this.config) {
|
|
308
|
+
return { success: false, error: "Call connect() first." };
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
if (!this.config.oauth) {
|
|
312
|
+
return { success: false, error: "OAuth config required. Use serviceAccount for headless auth." };
|
|
313
|
+
}
|
|
314
|
+
if (!this.tokenSaver) {
|
|
315
|
+
return { success: false, error: "tokenSaver required for authenticate() flow." };
|
|
316
|
+
}
|
|
317
|
+
const client = await createOAuth2Client(this.config.oauth);
|
|
318
|
+
const scopes = this.config.scopes ?? [
|
|
319
|
+
"https://www.googleapis.com/auth/drive.readonly"
|
|
320
|
+
];
|
|
321
|
+
const authUrl = getAuthUrl(client, scopes);
|
|
322
|
+
const code = await codeProvider(authUrl);
|
|
323
|
+
const tokens = await exchangeCode(client, code);
|
|
324
|
+
await saveTokens(this.tokenSaver, this.config.account, tokens);
|
|
325
|
+
this.tokens = tokens;
|
|
326
|
+
this.client = client;
|
|
327
|
+
this.client.setCredentials(tokens);
|
|
328
|
+
const { google } = await import("googleapis");
|
|
329
|
+
this.drive = google.drive({ version: "v3", auth: this.client });
|
|
330
|
+
return { success: true, account: this.config.account };
|
|
331
|
+
} catch (err) {
|
|
332
|
+
return { success: false, error: errorMessage2(err) };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// ── Sync ───────────────────────────────────────────────────────
|
|
336
|
+
async sync(options) {
|
|
337
|
+
this.ensureConnected();
|
|
338
|
+
if (options?.cursor) {
|
|
339
|
+
return this.syncIncremental(options.cursor, options);
|
|
340
|
+
}
|
|
341
|
+
return this.syncFull(options);
|
|
342
|
+
}
|
|
343
|
+
/** Incremental sync using Drive Changes API. */
|
|
344
|
+
async syncIncremental(startPageToken, options) {
|
|
345
|
+
const records = [];
|
|
346
|
+
const errors = [];
|
|
347
|
+
let pageToken = startPageToken;
|
|
348
|
+
let newStartPageToken;
|
|
349
|
+
try {
|
|
350
|
+
do {
|
|
351
|
+
const res = await this.drive.changes.list({
|
|
352
|
+
pageToken,
|
|
353
|
+
fields: `nextPageToken, newStartPageToken, changes(fileId, removed, file(${FILE_FIELDS}))`,
|
|
354
|
+
pageSize: options?.limit ? Math.min(options.limit - records.length, 100) : 100
|
|
355
|
+
});
|
|
356
|
+
for (const change of res.data.changes ?? []) {
|
|
357
|
+
try {
|
|
358
|
+
if (change.removed || !change.file) {
|
|
359
|
+
if (change.fileId) {
|
|
360
|
+
records.push({
|
|
361
|
+
driveFileId: change.fileId,
|
|
362
|
+
account: this.config.account,
|
|
363
|
+
name: "",
|
|
364
|
+
mimeType: "",
|
|
365
|
+
webViewLink: "",
|
|
366
|
+
modifiedTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
367
|
+
createdTime: "",
|
|
368
|
+
owners: [],
|
|
369
|
+
starred: false,
|
|
370
|
+
trashed: true
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
} else {
|
|
374
|
+
records.push(this.mapFile(change.file));
|
|
375
|
+
}
|
|
376
|
+
} catch (err) {
|
|
377
|
+
errors.push({ id: change.fileId ?? "unknown", error: errorMessage2(err) });
|
|
378
|
+
}
|
|
379
|
+
if (options?.limit && records.length >= options.limit) break;
|
|
380
|
+
}
|
|
381
|
+
pageToken = res.data.nextPageToken ?? void 0;
|
|
382
|
+
newStartPageToken = res.data.newStartPageToken ?? void 0;
|
|
383
|
+
} while (pageToken && (!options?.limit || records.length < options.limit));
|
|
384
|
+
} catch (err) {
|
|
385
|
+
if (err?.code === 403 || err?.code === 404) {
|
|
386
|
+
return this.syncFull(options);
|
|
387
|
+
}
|
|
388
|
+
throw err;
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
records,
|
|
392
|
+
cursor: newStartPageToken,
|
|
393
|
+
hasMore: !!pageToken,
|
|
394
|
+
errors
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
/** Full sync using files.list. */
|
|
398
|
+
async syncFull(options) {
|
|
399
|
+
const records = [];
|
|
400
|
+
const errors = [];
|
|
401
|
+
const maxResults = options?.limit ?? 500;
|
|
402
|
+
const queryParts = ["trashed = false"];
|
|
403
|
+
const folderId = options?.filters?.folderId;
|
|
404
|
+
if (folderId) {
|
|
405
|
+
queryParts.push(`'${folderId}' in parents`);
|
|
406
|
+
}
|
|
407
|
+
const mimeType = options?.filters?.mimeType;
|
|
408
|
+
if (mimeType) {
|
|
409
|
+
queryParts.push(`mimeType = '${mimeType}'`);
|
|
410
|
+
}
|
|
411
|
+
if (options?.since) {
|
|
412
|
+
queryParts.push(`modifiedTime > '${new Date(options.since).toISOString()}'`);
|
|
413
|
+
}
|
|
414
|
+
const q = queryParts.join(" and ");
|
|
415
|
+
let pageToken;
|
|
416
|
+
do {
|
|
417
|
+
const res = await this.drive.files.list({
|
|
418
|
+
q,
|
|
419
|
+
fields: `nextPageToken, files(${FILE_FIELDS})`,
|
|
420
|
+
orderBy: "modifiedTime desc",
|
|
421
|
+
pageSize: Math.min(maxResults - records.length, 100),
|
|
422
|
+
...pageToken ? { pageToken } : {}
|
|
423
|
+
});
|
|
424
|
+
for (const file of res.data.files ?? []) {
|
|
425
|
+
try {
|
|
426
|
+
records.push(this.mapFile(file));
|
|
427
|
+
} catch (err) {
|
|
428
|
+
errors.push({ id: file.id, error: errorMessage2(err) });
|
|
429
|
+
}
|
|
430
|
+
if (records.length >= maxResults) break;
|
|
431
|
+
}
|
|
432
|
+
pageToken = res.data.nextPageToken ?? void 0;
|
|
433
|
+
} while (pageToken && records.length < maxResults);
|
|
434
|
+
const tokenRes = await this.drive.changes.getStartPageToken({});
|
|
435
|
+
const cursor = tokenRes.data.startPageToken ?? void 0;
|
|
436
|
+
return {
|
|
437
|
+
records,
|
|
438
|
+
cursor,
|
|
439
|
+
hasMore: !!pageToken,
|
|
440
|
+
errors
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
// ── Internals ─────────────────────────────────────────────────
|
|
444
|
+
ensureConnected() {
|
|
445
|
+
if (!this.drive || !this.config) {
|
|
446
|
+
throw new Error("GoogleDriveConnector is not connected. Call connect() first.");
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
450
|
+
mapFile(file) {
|
|
451
|
+
const mapOwner = (o) => ({
|
|
452
|
+
displayName: o?.displayName ?? "",
|
|
453
|
+
emailAddress: o?.emailAddress ?? ""
|
|
454
|
+
});
|
|
455
|
+
return {
|
|
456
|
+
driveFileId: file.id,
|
|
457
|
+
account: this.config.account,
|
|
458
|
+
name: file.name ?? "(Untitled)",
|
|
459
|
+
mimeType: file.mimeType ?? "",
|
|
460
|
+
webViewLink: file.webViewLink ?? "",
|
|
461
|
+
webContentLink: file.webContentLink ?? void 0,
|
|
462
|
+
modifiedTime: file.modifiedTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
463
|
+
createdTime: file.createdTime ?? "",
|
|
464
|
+
size: file.size ? parseInt(file.size, 10) : void 0,
|
|
465
|
+
parents: file.parents ?? void 0,
|
|
466
|
+
description: file.description ?? void 0,
|
|
467
|
+
owners: (file.owners ?? []).map(mapOwner),
|
|
468
|
+
lastModifyingUser: file.lastModifyingUser ? mapOwner(file.lastModifyingUser) : void 0,
|
|
469
|
+
starred: file.starred ?? false,
|
|
470
|
+
trashed: file.trashed ?? false
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
function errorMessage2(err) {
|
|
475
|
+
return err instanceof Error ? err.message : String(err);
|
|
476
|
+
}
|
|
238
477
|
export {
|
|
239
478
|
GoogleCalendarConnector,
|
|
479
|
+
GoogleDriveConnector,
|
|
240
480
|
GoogleGmailConnector,
|
|
241
481
|
createOAuth2Client,
|
|
242
482
|
createServiceAccountClient,
|
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-BWrtVqEP.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-BWrtVqEP.js';
|
|
9
9
|
import 'better-sqlite3';
|
|
10
10
|
|
|
11
11
|
/** Execution adapter types — Story 1.5 / 3.4 / 3.5 */
|
|
@@ -1791,7 +1791,6 @@ declare class LoopDetector {
|
|
|
1791
1791
|
|
|
1792
1792
|
interface FeedbackEntry {
|
|
1793
1793
|
agentId: string;
|
|
1794
|
-
userId?: string;
|
|
1795
1794
|
taskId?: string;
|
|
1796
1795
|
issue: string;
|
|
1797
1796
|
rootCause?: string;
|
|
@@ -1806,7 +1805,6 @@ interface PlaybookEntry {
|
|
|
1806
1805
|
rule: string;
|
|
1807
1806
|
feedbackIds: string[];
|
|
1808
1807
|
projectScoped: boolean;
|
|
1809
|
-
clientId?: string;
|
|
1810
1808
|
agentIds?: string[];
|
|
1811
1809
|
}
|
|
1812
1810
|
interface SkillEntry {
|
|
@@ -1841,7 +1839,6 @@ declare class LearningPipeline {
|
|
|
1841
1839
|
*/
|
|
1842
1840
|
listFeedback(filter?: {
|
|
1843
1841
|
agentId?: string;
|
|
1844
|
-
userId?: string;
|
|
1845
1842
|
severity?: string;
|
|
1846
1843
|
repeatable?: boolean;
|
|
1847
1844
|
}): Promise<Array<Record<string, unknown>>>;
|
package/dist/index.js
CHANGED
|
@@ -1662,7 +1662,6 @@ 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;
|
|
1666
1665
|
this.tasks = config.tasks;
|
|
1667
1666
|
this.wakeups = config.wakeups;
|
|
1668
1667
|
this.messageStore = new MessageStore(db, hooks);
|
|
@@ -1696,7 +1695,6 @@ var ChatPipeline = class {
|
|
|
1696
1695
|
dedupWindowMs;
|
|
1697
1696
|
tasks;
|
|
1698
1697
|
wakeups;
|
|
1699
|
-
skipAck;
|
|
1700
1698
|
// In-memory thread → channel mapping for response routing
|
|
1701
1699
|
// (before thread_task_map exists)
|
|
1702
1700
|
threadChannelMap = /* @__PURE__ */ new Map();
|
|
@@ -1733,26 +1731,24 @@ var ChatPipeline = class {
|
|
|
1733
1731
|
const dir = m.direction === "inbound" ? "User" : "Bot";
|
|
1734
1732
|
return `${dir}: ${m.body?.slice(0, 200) ?? ""}`;
|
|
1735
1733
|
}).join("\n");
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
additionalContext: historyContext ? `
|
|
1734
|
+
const ackResponse = await this.responder.respond({
|
|
1735
|
+
messageBody: msg.body,
|
|
1736
|
+
threadId: threadTs,
|
|
1737
|
+
channel: this.channel,
|
|
1738
|
+
capabilities: this.capabilities,
|
|
1739
|
+
additionalContext: historyContext ? `
|
|
1743
1740
|
|
|
1744
1741
|
Recent conversation history:
|
|
1745
1742
|
${historyContext}` : void 0
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
}
|
|
1743
|
+
});
|
|
1744
|
+
await this.responder.sendResponse({
|
|
1745
|
+
text: ackResponse,
|
|
1746
|
+
channel: this.channel,
|
|
1747
|
+
threadId: threadTs,
|
|
1748
|
+
source: "responder",
|
|
1749
|
+
skipFilter: true,
|
|
1750
|
+
skipRedundancyCheck: true
|
|
1751
|
+
});
|
|
1756
1752
|
const dispatchPromise = this.interpretAndDispatch(messageId, msg, threadTs, channelId);
|
|
1757
1753
|
this.lastDispatch = dispatchPromise;
|
|
1758
1754
|
void dispatchPromise;
|
|
@@ -2970,7 +2966,6 @@ function defineCoreTables(db) {
|
|
|
2970
2966
|
columns: {
|
|
2971
2967
|
id: "TEXT PRIMARY KEY",
|
|
2972
2968
|
agent_id: "TEXT NOT NULL",
|
|
2973
|
-
user_id: "TEXT",
|
|
2974
2969
|
task_id: "TEXT",
|
|
2975
2970
|
issue: "TEXT NOT NULL",
|
|
2976
2971
|
root_cause: "TEXT",
|
|
@@ -2983,8 +2978,7 @@ function defineCoreTables(db) {
|
|
|
2983
2978
|
},
|
|
2984
2979
|
tableConstraints: [
|
|
2985
2980
|
"CREATE INDEX IF NOT EXISTS idx_feedback_agent ON feedback(agent_id, created_at)",
|
|
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)"
|
|
2981
|
+
"CREATE INDEX IF NOT EXISTS idx_feedback_issue ON feedback(issue)"
|
|
2988
2982
|
]
|
|
2989
2983
|
});
|
|
2990
2984
|
db.define("playbooks", {
|
|
@@ -2994,7 +2988,6 @@ function defineCoreTables(db) {
|
|
|
2994
2988
|
rule: "TEXT NOT NULL",
|
|
2995
2989
|
feedback_ids: "TEXT NOT NULL DEFAULT '[]'",
|
|
2996
2990
|
project_scoped: "INTEGER NOT NULL DEFAULT 1",
|
|
2997
|
-
client_id: "TEXT",
|
|
2998
2991
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
2999
2992
|
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
3000
2993
|
deleted_at: "TEXT"
|
|
@@ -3120,18 +3113,6 @@ var CORE_MIGRATIONS = [
|
|
|
3120
3113
|
{
|
|
3121
3114
|
version: "006_schedules_next_index",
|
|
3122
3115
|
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`
|
|
3135
3116
|
}
|
|
3136
3117
|
];
|
|
3137
3118
|
|
|
@@ -5951,7 +5932,6 @@ var LearningPipeline = class {
|
|
|
5951
5932
|
async captureFeedback(entry) {
|
|
5952
5933
|
const row = await this.db.insert("feedback", {
|
|
5953
5934
|
agent_id: entry.agentId,
|
|
5954
|
-
user_id: entry.userId,
|
|
5955
5935
|
task_id: entry.taskId,
|
|
5956
5936
|
issue: entry.issue,
|
|
5957
5937
|
root_cause: entry.rootCause,
|
|
@@ -5965,7 +5945,6 @@ var LearningPipeline = class {
|
|
|
5965
5945
|
await this.hooks.emit("learning.feedback_captured", {
|
|
5966
5946
|
feedbackId,
|
|
5967
5947
|
agentId: entry.agentId,
|
|
5968
|
-
userId: entry.userId,
|
|
5969
5948
|
issue: entry.issue,
|
|
5970
5949
|
severity: entry.severity
|
|
5971
5950
|
});
|
|
@@ -5980,7 +5959,6 @@ var LearningPipeline = class {
|
|
|
5980
5959
|
async listFeedback(filter) {
|
|
5981
5960
|
const where = {};
|
|
5982
5961
|
if (filter?.agentId) where["agent_id"] = filter.agentId;
|
|
5983
|
-
if (filter?.userId) where["user_id"] = filter.userId;
|
|
5984
5962
|
if (filter?.severity) where["severity"] = filter.severity;
|
|
5985
5963
|
if (filter?.repeatable !== void 0) where["repeatable"] = filter.repeatable ? 1 : 0;
|
|
5986
5964
|
return this.db.query("feedback", Object.keys(where).length ? { where } : void 0);
|
|
@@ -6022,8 +6000,7 @@ var LearningPipeline = class {
|
|
|
6022
6000
|
pattern: entry.pattern,
|
|
6023
6001
|
rule: entry.rule,
|
|
6024
6002
|
feedback_ids: JSON.stringify(entry.feedbackIds),
|
|
6025
|
-
project_scoped: entry.projectScoped ? 1 : 0
|
|
6026
|
-
client_id: entry.clientId
|
|
6003
|
+
project_scoped: entry.projectScoped ? 1 : 0
|
|
6027
6004
|
});
|
|
6028
6005
|
const playbookId = row["id"];
|
|
6029
6006
|
if (entry.agentIds) {
|