@everworker/oneringai 0.4.1 → 0.4.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/dist/index.cjs CHANGED
@@ -17,7 +17,7 @@ var z = require('zod/v4');
17
17
  var spawn = require('cross-spawn');
18
18
  var process2 = require('process');
19
19
  var stream = require('stream');
20
- var fs18 = require('fs/promises');
20
+ var fs17 = require('fs/promises');
21
21
  var simpleIcons = require('simple-icons');
22
22
  var child_process = require('child_process');
23
23
  var util = require('util');
@@ -55,7 +55,7 @@ var z4mini__namespace = /*#__PURE__*/_interopNamespace(z4mini);
55
55
  var z__namespace = /*#__PURE__*/_interopNamespace(z);
56
56
  var spawn__default = /*#__PURE__*/_interopDefault(spawn);
57
57
  var process2__default = /*#__PURE__*/_interopDefault(process2);
58
- var fs18__namespace = /*#__PURE__*/_interopNamespace(fs18);
58
+ var fs17__namespace = /*#__PURE__*/_interopNamespace(fs17);
59
59
  var simpleIcons__namespace = /*#__PURE__*/_interopNamespace(simpleIcons);
60
60
  var vm__namespace = /*#__PURE__*/_interopNamespace(vm);
61
61
 
@@ -195,6 +195,12 @@ var init_MemoryStorage = __esm({
195
195
  size() {
196
196
  return this.tokens.size;
197
197
  }
198
+ /**
199
+ * List all storage keys (for account enumeration)
200
+ */
201
+ async listKeys() {
202
+ return Array.from(this.tokens.keys());
203
+ }
198
204
  };
199
205
  }
200
206
  });
@@ -212,14 +218,23 @@ var init_TokenStore = __esm({
212
218
  this.storage = storage || new exports.MemoryStorage();
213
219
  }
214
220
  /**
215
- * Get user-scoped storage key
216
- * For multi-user support, keys are scoped per user: "provider:userId"
217
- * For single-user (backward compatible), userId is omitted or "default"
221
+ * Get user-scoped (and optionally account-scoped) storage key
222
+ *
223
+ * Key format (backward compatible):
224
+ * - No userId, no accountId → baseKey
225
+ * - userId only → baseKey:userId
226
+ * - userId + accountId → baseKey:userId:accountId
227
+ * - accountId only → baseKey:default:accountId
218
228
  *
219
229
  * @param userId - User identifier (optional, defaults to single-user mode)
220
- * @returns Storage key scoped to user
230
+ * @param accountId - Account alias for multi-account support (optional)
231
+ * @returns Storage key scoped to user and account
221
232
  */
222
- getScopedKey(userId) {
233
+ getScopedKey(userId, accountId) {
234
+ if (accountId) {
235
+ const userPart = userId && userId !== "default" ? userId : "default";
236
+ return `${this.baseStorageKey}:${userPart}:${accountId}`;
237
+ }
223
238
  if (!userId || userId === "default") {
224
239
  return this.baseStorageKey;
225
240
  }
@@ -229,8 +244,9 @@ var init_TokenStore = __esm({
229
244
  * Store token (encrypted by storage layer)
230
245
  * @param tokenResponse - Token response from OAuth provider
231
246
  * @param userId - Optional user identifier for multi-user support
247
+ * @param accountId - Optional account alias for multi-account support
232
248
  */
233
- async storeToken(tokenResponse, userId) {
249
+ async storeToken(tokenResponse, userId, accountId) {
234
250
  if (!tokenResponse.access_token) {
235
251
  throw new Error("OAuth response missing required access_token field");
236
252
  }
@@ -248,39 +264,46 @@ var init_TokenStore = __esm({
248
264
  scope: tokenResponse.scope,
249
265
  obtained_at: Date.now()
250
266
  };
251
- const key = this.getScopedKey(userId);
267
+ const key = this.getScopedKey(userId, accountId);
252
268
  await this.storage.storeToken(key, token);
253
269
  }
254
270
  /**
255
271
  * Get access token
256
272
  * @param userId - Optional user identifier for multi-user support
273
+ * @param accountId - Optional account alias for multi-account support
257
274
  */
258
- async getAccessToken(userId) {
259
- const key = this.getScopedKey(userId);
275
+ async getAccessToken(userId, accountId) {
276
+ const key = this.getScopedKey(userId, accountId);
260
277
  const token = await this.storage.getToken(key);
261
278
  if (!token) {
262
- throw new Error(`No token stored for ${userId ? `user: ${userId}` : "default user"}`);
279
+ const userLabel = userId ? `user: ${userId}` : "default user";
280
+ const accountLabel = accountId ? `, account: ${accountId}` : "";
281
+ throw new Error(`No token stored for ${userLabel}${accountLabel}`);
263
282
  }
264
283
  return token.access_token;
265
284
  }
266
285
  /**
267
286
  * Get refresh token
268
287
  * @param userId - Optional user identifier for multi-user support
288
+ * @param accountId - Optional account alias for multi-account support
269
289
  */
270
- async getRefreshToken(userId) {
271
- const key = this.getScopedKey(userId);
290
+ async getRefreshToken(userId, accountId) {
291
+ const key = this.getScopedKey(userId, accountId);
272
292
  const token = await this.storage.getToken(key);
273
293
  if (!token?.refresh_token) {
274
- throw new Error(`No refresh token available for ${userId ? `user: ${userId}` : "default user"}`);
294
+ const userLabel = userId ? `user: ${userId}` : "default user";
295
+ const accountLabel = accountId ? `, account: ${accountId}` : "";
296
+ throw new Error(`No refresh token available for ${userLabel}${accountLabel}`);
275
297
  }
276
298
  return token.refresh_token;
277
299
  }
278
300
  /**
279
301
  * Check if has refresh token
280
302
  * @param userId - Optional user identifier for multi-user support
303
+ * @param accountId - Optional account alias for multi-account support
281
304
  */
282
- async hasRefreshToken(userId) {
283
- const key = this.getScopedKey(userId);
305
+ async hasRefreshToken(userId, accountId) {
306
+ const key = this.getScopedKey(userId, accountId);
284
307
  const token = await this.storage.getToken(key);
285
308
  return !!token?.refresh_token;
286
309
  }
@@ -289,9 +312,10 @@ var init_TokenStore = __esm({
289
312
  *
290
313
  * @param bufferSeconds - Refresh this many seconds before expiry (default: 300 = 5 min)
291
314
  * @param userId - Optional user identifier for multi-user support
315
+ * @param accountId - Optional account alias for multi-account support
292
316
  */
293
- async isValid(bufferSeconds = 300, userId) {
294
- const key = this.getScopedKey(userId);
317
+ async isValid(bufferSeconds = 300, userId, accountId) {
318
+ const key = this.getScopedKey(userId, accountId);
295
319
  const token = await this.storage.getToken(key);
296
320
  if (!token) {
297
321
  return false;
@@ -303,19 +327,46 @@ var init_TokenStore = __esm({
303
327
  /**
304
328
  * Clear stored token
305
329
  * @param userId - Optional user identifier for multi-user support
330
+ * @param accountId - Optional account alias for multi-account support
306
331
  */
307
- async clear(userId) {
308
- const key = this.getScopedKey(userId);
332
+ async clear(userId, accountId) {
333
+ const key = this.getScopedKey(userId, accountId);
309
334
  await this.storage.deleteToken(key);
310
335
  }
311
336
  /**
312
337
  * Get full token info
313
338
  * @param userId - Optional user identifier for multi-user support
339
+ * @param accountId - Optional account alias for multi-account support
314
340
  */
315
- async getTokenInfo(userId) {
316
- const key = this.getScopedKey(userId);
341
+ async getTokenInfo(userId, accountId) {
342
+ const key = this.getScopedKey(userId, accountId);
317
343
  return this.storage.getToken(key);
318
344
  }
345
+ /**
346
+ * List account aliases for a user on this connector.
347
+ * Returns account IDs that have stored tokens.
348
+ *
349
+ * @param userId - Optional user identifier
350
+ * @returns Array of account aliases (e.g., ['work', 'personal'])
351
+ */
352
+ async listAccounts(userId) {
353
+ if (!this.storage.listKeys) {
354
+ return [];
355
+ }
356
+ const allKeys = await this.storage.listKeys();
357
+ const userPart = userId && userId !== "default" ? userId : "default";
358
+ const prefix = `${this.baseStorageKey}:${userPart}:`;
359
+ const accounts = [];
360
+ for (const key of allKeys) {
361
+ if (key.startsWith(prefix)) {
362
+ const accountId = key.slice(prefix.length);
363
+ if (accountId && !accountId.includes(":")) {
364
+ accounts.push(accountId);
365
+ }
366
+ }
367
+ }
368
+ return accounts;
369
+ }
319
370
  };
320
371
  }
321
372
  });
@@ -356,20 +407,28 @@ var init_AuthCodePKCE = __esm({
356
407
  this.tokenStore = new TokenStore(storageKey, config.storage);
357
408
  }
358
409
  tokenStore;
359
- // Store PKCE data per user with timestamps for cleanup
410
+ // Store PKCE data per user+account with timestamps for cleanup
360
411
  codeVerifiers = /* @__PURE__ */ new Map();
361
412
  states = /* @__PURE__ */ new Map();
362
- // Store refresh locks per user to prevent concurrent refresh
413
+ // Store refresh locks per user+account to prevent concurrent refresh
363
414
  refreshLocks = /* @__PURE__ */ new Map();
364
415
  // PKCE data TTL: 15 minutes (auth flows should complete within this time)
365
416
  PKCE_TTL = 15 * 60 * 1e3;
417
+ /**
418
+ * Build a map key from userId and accountId for internal PKCE/state/lock maps.
419
+ */
420
+ getMapKey(userId, accountId) {
421
+ const userPart = userId || "default";
422
+ return accountId ? `${userPart}:${accountId}` : userPart;
423
+ }
366
424
  /**
367
425
  * Generate authorization URL for user to visit
368
426
  * Opens browser or redirects user to this URL
369
427
  *
370
428
  * @param userId - User identifier for multi-user support (optional)
429
+ * @param accountId - Account alias for multi-account support (optional)
371
430
  */
372
- async getAuthorizationUrl(userId) {
431
+ async getAuthorizationUrl(userId, accountId) {
373
432
  if (!this.config.authorizationUrl) {
374
433
  throw new Error("authorizationUrl is required for authorization_code flow");
375
434
  }
@@ -377,11 +436,11 @@ var init_AuthCodePKCE = __esm({
377
436
  throw new Error("redirectUri is required for authorization_code flow");
378
437
  }
379
438
  this.cleanupExpiredPKCE();
380
- const userKey = userId || "default";
439
+ const mapKey = this.getMapKey(userId, accountId);
381
440
  const { codeVerifier, codeChallenge } = generatePKCE();
382
- this.codeVerifiers.set(userKey, { verifier: codeVerifier, timestamp: Date.now() });
441
+ this.codeVerifiers.set(mapKey, { verifier: codeVerifier, timestamp: Date.now() });
383
442
  const state = generateState();
384
- this.states.set(userKey, { state, timestamp: Date.now() });
443
+ this.states.set(mapKey, { state, timestamp: Date.now() });
385
444
  const params = new URLSearchParams({
386
445
  response_type: "code",
387
446
  client_id: this.config.clientId,
@@ -395,33 +454,48 @@ var init_AuthCodePKCE = __esm({
395
454
  params.append("code_challenge", codeChallenge);
396
455
  params.append("code_challenge_method", "S256");
397
456
  }
398
- const stateWithUser = userId ? `${state}::${userId}` : state;
399
- params.set("state", stateWithUser);
457
+ let stateWithMetadata = state;
458
+ if (userId || accountId) {
459
+ stateWithMetadata = `${state}::${userId || ""}`;
460
+ if (accountId) {
461
+ stateWithMetadata += `::${accountId}`;
462
+ }
463
+ }
464
+ params.set("state", stateWithMetadata);
400
465
  return `${this.config.authorizationUrl}?${params.toString()}`;
401
466
  }
402
467
  /**
403
468
  * Exchange authorization code for access token
404
469
  *
405
470
  * @param code - Authorization code from callback
406
- * @param state - State parameter from callback (for CSRF verification, may include userId)
471
+ * @param state - State parameter from callback (for CSRF verification, may include userId/accountId)
407
472
  * @param userId - User identifier (optional, can be extracted from state)
473
+ * @param accountId - Account alias (optional, can be extracted from state)
408
474
  */
409
- async exchangeCode(code, state, userId) {
475
+ async exchangeCode(code, state, userId, accountId) {
410
476
  let actualState = state;
411
477
  let actualUserId = userId;
478
+ let actualAccountId = accountId;
412
479
  if (state.includes("::")) {
413
480
  const parts = state.split("::");
414
481
  actualState = parts[0];
415
- actualUserId = parts[1];
482
+ if (!actualUserId && parts[1]) {
483
+ actualUserId = parts[1];
484
+ }
485
+ if (!actualAccountId && parts[2]) {
486
+ actualAccountId = parts[2];
487
+ }
416
488
  }
417
- const userKey = actualUserId || "default";
418
- const stateData = this.states.get(userKey);
489
+ const mapKey = this.getMapKey(actualUserId, actualAccountId);
490
+ const stateData = this.states.get(mapKey);
419
491
  if (!stateData) {
420
- throw new Error(`No PKCE state found for user ${actualUserId}. Authorization flow may have expired (15 min TTL).`);
492
+ const label = actualAccountId ? `user ${actualUserId}, account ${actualAccountId}` : `user ${actualUserId}`;
493
+ throw new Error(`No PKCE state found for ${label}. Authorization flow may have expired (15 min TTL).`);
421
494
  }
422
495
  const expectedState = stateData.state;
423
496
  if (actualState !== expectedState) {
424
- throw new Error(`State mismatch for user ${actualUserId} - possible CSRF attack. Expected: ${expectedState}, Got: ${actualState}`);
497
+ const label = actualAccountId ? `user ${actualUserId}, account ${actualAccountId}` : `user ${actualUserId}`;
498
+ throw new Error(`State mismatch for ${label} - possible CSRF attack. Expected: ${expectedState}, Got: ${actualState}`);
425
499
  }
426
500
  if (!this.config.redirectUri) {
427
501
  throw new Error("redirectUri is required");
@@ -435,7 +509,7 @@ var init_AuthCodePKCE = __esm({
435
509
  if (this.config.clientSecret) {
436
510
  params.append("client_secret", this.config.clientSecret);
437
511
  }
438
- const verifierData = this.codeVerifiers.get(userKey);
512
+ const verifierData = this.codeVerifiers.get(mapKey);
439
513
  if (this.config.usePKCE !== false && verifierData) {
440
514
  params.append("code_verifier", verifierData.verifier);
441
515
  }
@@ -469,39 +543,43 @@ var init_AuthCodePKCE = __esm({
469
543
  throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${error}`);
470
544
  }
471
545
  const data = await response.json();
472
- await this.tokenStore.storeToken(data, actualUserId);
473
- this.codeVerifiers.delete(userKey);
474
- this.states.delete(userKey);
546
+ await this.tokenStore.storeToken(data, actualUserId, actualAccountId);
547
+ this.codeVerifiers.delete(mapKey);
548
+ this.states.delete(mapKey);
475
549
  }
476
550
  /**
477
551
  * Get valid token (auto-refreshes if needed)
478
552
  * @param userId - User identifier for multi-user support
553
+ * @param accountId - Account alias for multi-account support
479
554
  */
480
- async getToken(userId) {
481
- const key = userId || "default";
482
- if (this.refreshLocks.has(key)) {
483
- return this.refreshLocks.get(key);
555
+ async getToken(userId, accountId) {
556
+ const mapKey = this.getMapKey(userId, accountId);
557
+ if (this.refreshLocks.has(mapKey)) {
558
+ return this.refreshLocks.get(mapKey);
484
559
  }
485
- if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId)) {
486
- return this.tokenStore.getAccessToken(userId);
560
+ if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId)) {
561
+ return this.tokenStore.getAccessToken(userId, accountId);
487
562
  }
488
- if (await this.tokenStore.hasRefreshToken(userId)) {
489
- const refreshPromise = this.refreshToken(userId);
490
- this.refreshLocks.set(key, refreshPromise);
563
+ if (await this.tokenStore.hasRefreshToken(userId, accountId)) {
564
+ const refreshPromise = this.refreshToken(userId, accountId);
565
+ this.refreshLocks.set(mapKey, refreshPromise);
491
566
  try {
492
567
  return await refreshPromise;
493
568
  } finally {
494
- this.refreshLocks.delete(key);
569
+ this.refreshLocks.delete(mapKey);
495
570
  }
496
571
  }
497
- throw new Error(`No valid token available for ${userId ? `user: ${userId}` : "default user"}. User needs to authorize (call startAuthFlow).`);
572
+ const userLabel = userId ? `user: ${userId}` : "default user";
573
+ const accountLabel = accountId ? `, account: ${accountId}` : "";
574
+ throw new Error(`No valid token available for ${userLabel}${accountLabel}. User needs to authorize (call startAuthFlow).`);
498
575
  }
499
576
  /**
500
577
  * Refresh access token using refresh token
501
578
  * @param userId - User identifier for multi-user support
579
+ * @param accountId - Account alias for multi-account support
502
580
  */
503
- async refreshToken(userId) {
504
- const refreshToken = await this.tokenStore.getRefreshToken(userId);
581
+ async refreshToken(userId, accountId) {
582
+ const refreshToken = await this.tokenStore.getRefreshToken(userId, accountId);
505
583
  const params = new URLSearchParams({
506
584
  grant_type: "refresh_token",
507
585
  refresh_token: refreshToken,
@@ -540,28 +618,30 @@ var init_AuthCodePKCE = __esm({
540
618
  throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${error}`);
541
619
  }
542
620
  const data = await response.json();
543
- await this.tokenStore.storeToken(data, userId);
621
+ await this.tokenStore.storeToken(data, userId, accountId);
544
622
  return data.access_token;
545
623
  }
546
624
  /**
547
625
  * Check if token is valid
548
626
  * @param userId - User identifier for multi-user support
627
+ * @param accountId - Account alias for multi-account support
549
628
  */
550
- async isTokenValid(userId) {
551
- return this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId);
629
+ async isTokenValid(userId, accountId) {
630
+ return this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId);
552
631
  }
553
632
  /**
554
633
  * Revoke token (if supported by provider)
555
634
  * @param revocationUrl - Optional revocation endpoint
556
635
  * @param userId - User identifier for multi-user support
636
+ * @param accountId - Account alias for multi-account support
557
637
  */
558
- async revokeToken(revocationUrl, userId) {
638
+ async revokeToken(revocationUrl, userId, accountId) {
559
639
  if (!revocationUrl) {
560
- await this.tokenStore.clear(userId);
640
+ await this.tokenStore.clear(userId, accountId);
561
641
  return;
562
642
  }
563
643
  try {
564
- const token = await this.tokenStore.getAccessToken(userId);
644
+ const token = await this.tokenStore.getAccessToken(userId, accountId);
565
645
  await fetch(revocationUrl, {
566
646
  method: "POST",
567
647
  headers: {
@@ -573,9 +653,16 @@ var init_AuthCodePKCE = __esm({
573
653
  })
574
654
  });
575
655
  } finally {
576
- await this.tokenStore.clear(userId);
656
+ await this.tokenStore.clear(userId, accountId);
577
657
  }
578
658
  }
659
+ /**
660
+ * List account aliases for a user.
661
+ * @param userId - User identifier (optional)
662
+ */
663
+ async listAccounts(userId) {
664
+ return this.tokenStore.listAccounts(userId);
665
+ }
579
666
  /**
580
667
  * Clean up expired PKCE data to prevent memory leaks
581
668
  * Removes verifiers and states older than PKCE_TTL (15 minutes)
@@ -607,17 +694,19 @@ var init_ClientCredentials = __esm({
607
694
  tokenStore;
608
695
  /**
609
696
  * Get token using client credentials
697
+ * @param userId - User identifier for multi-user support (optional, rarely used for client_credentials)
698
+ * @param accountId - Account alias for multi-account support (optional)
610
699
  */
611
- async getToken() {
612
- if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry)) {
613
- return this.tokenStore.getAccessToken();
700
+ async getToken(userId, accountId) {
701
+ if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId)) {
702
+ return this.tokenStore.getAccessToken(userId, accountId);
614
703
  }
615
- return this.requestToken();
704
+ return this.requestToken(userId, accountId);
616
705
  }
617
706
  /**
618
707
  * Request a new token from the authorization server
619
708
  */
620
- async requestToken() {
709
+ async requestToken(userId, accountId) {
621
710
  const auth2 = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString(
622
711
  "base64"
623
712
  );
@@ -640,22 +729,26 @@ var init_ClientCredentials = __esm({
640
729
  throw new Error(`Token request failed: ${response.status} ${response.statusText} - ${error}`);
641
730
  }
642
731
  const data = await response.json();
643
- await this.tokenStore.storeToken(data);
732
+ await this.tokenStore.storeToken(data, userId, accountId);
644
733
  return data.access_token;
645
734
  }
646
735
  /**
647
736
  * Refresh token (client credentials don't use refresh tokens)
648
737
  * Just requests a new token
738
+ * @param userId - User identifier for multi-user support (optional)
739
+ * @param accountId - Account alias for multi-account support (optional)
649
740
  */
650
- async refreshToken() {
651
- await this.tokenStore.clear();
652
- return this.requestToken();
741
+ async refreshToken(userId, accountId) {
742
+ await this.tokenStore.clear(userId, accountId);
743
+ return this.requestToken(userId, accountId);
653
744
  }
654
745
  /**
655
746
  * Check if token is valid
747
+ * @param userId - User identifier for multi-user support (optional)
748
+ * @param accountId - Account alias for multi-account support (optional)
656
749
  */
657
- async isTokenValid() {
658
- return this.tokenStore.isValid(this.config.refreshBeforeExpiry);
750
+ async isTokenValid(userId, accountId) {
751
+ return this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId);
659
752
  }
660
753
  };
661
754
  }
@@ -697,17 +790,19 @@ var init_JWTBearer = __esm({
697
790
  }
698
791
  /**
699
792
  * Get token using JWT Bearer assertion
793
+ * @param userId - User identifier for multi-user support (optional)
794
+ * @param accountId - Account alias for multi-account support (optional)
700
795
  */
701
- async getToken() {
702
- if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry)) {
703
- return this.tokenStore.getAccessToken();
796
+ async getToken(userId, accountId) {
797
+ if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId)) {
798
+ return this.tokenStore.getAccessToken(userId, accountId);
704
799
  }
705
- return this.requestToken();
800
+ return this.requestToken(userId, accountId);
706
801
  }
707
802
  /**
708
803
  * Request token using JWT assertion
709
804
  */
710
- async requestToken() {
805
+ async requestToken(userId, accountId) {
711
806
  const assertion = await this.generateJWT();
712
807
  const params = new URLSearchParams({
713
808
  grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
@@ -725,21 +820,25 @@ var init_JWTBearer = __esm({
725
820
  throw new Error(`JWT Bearer token request failed: ${response.status} ${response.statusText} - ${error}`);
726
821
  }
727
822
  const data = await response.json();
728
- await this.tokenStore.storeToken(data);
823
+ await this.tokenStore.storeToken(data, userId, accountId);
729
824
  return data.access_token;
730
825
  }
731
826
  /**
732
827
  * Refresh token (generate new JWT and request new token)
828
+ * @param userId - User identifier for multi-user support (optional)
829
+ * @param accountId - Account alias for multi-account support (optional)
733
830
  */
734
- async refreshToken() {
735
- await this.tokenStore.clear();
736
- return this.requestToken();
831
+ async refreshToken(userId, accountId) {
832
+ await this.tokenStore.clear(userId, accountId);
833
+ return this.requestToken(userId, accountId);
737
834
  }
738
835
  /**
739
836
  * Check if token is valid
837
+ * @param userId - User identifier for multi-user support (optional)
838
+ * @param accountId - Account alias for multi-account support (optional)
740
839
  */
741
- async isTokenValid() {
742
- return this.tokenStore.isValid(this.config.refreshBeforeExpiry);
840
+ async isTokenValid(userId, accountId) {
841
+ return this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId);
743
842
  }
744
843
  };
745
844
  }
@@ -819,25 +918,28 @@ var init_OAuthManager = __esm({
819
918
  * Automatically refreshes if expired
820
919
  *
821
920
  * @param userId - User identifier for multi-user support (optional)
921
+ * @param accountId - Account alias for multi-account support (optional)
822
922
  */
823
- async getToken(userId) {
824
- return this.flow.getToken(userId);
923
+ async getToken(userId, accountId) {
924
+ return this.flow.getToken(userId, accountId);
825
925
  }
826
926
  /**
827
927
  * Force refresh the token
828
928
  *
829
929
  * @param userId - User identifier for multi-user support (optional)
930
+ * @param accountId - Account alias for multi-account support (optional)
830
931
  */
831
- async refreshToken(userId) {
832
- return this.flow.refreshToken(userId);
932
+ async refreshToken(userId, accountId) {
933
+ return this.flow.refreshToken(userId, accountId);
833
934
  }
834
935
  /**
835
936
  * Check if current token is valid
836
937
  *
837
938
  * @param userId - User identifier for multi-user support (optional)
939
+ * @param accountId - Account alias for multi-account support (optional)
838
940
  */
839
- async isTokenValid(userId) {
840
- return this.flow.isTokenValid(userId);
941
+ async isTokenValid(userId, accountId) {
942
+ return this.flow.isTokenValid(userId, accountId);
841
943
  }
842
944
  // ==================== Authorization Code Flow Methods ====================
843
945
  /**
@@ -845,13 +947,14 @@ var init_OAuthManager = __esm({
845
947
  * Returns URL for user to visit
846
948
  *
847
949
  * @param userId - User identifier for multi-user support (optional)
950
+ * @param accountId - Account alias for multi-account support (optional)
848
951
  * @returns Authorization URL for the user to visit
849
952
  */
850
- async startAuthFlow(userId) {
953
+ async startAuthFlow(userId, accountId) {
851
954
  if (!(this.flow instanceof AuthCodePKCEFlow)) {
852
955
  throw new Error("startAuthFlow() is only available for authorization_code flow");
853
956
  }
854
- return this.flow.getAuthorizationUrl(userId);
957
+ return this.flow.getAuthorizationUrl(userId, accountId);
855
958
  }
856
959
  /**
857
960
  * Handle OAuth callback (Authorization Code only)
@@ -859,8 +962,9 @@ var init_OAuthManager = __esm({
859
962
  *
860
963
  * @param callbackUrl - Full callback URL with code and state parameters
861
964
  * @param userId - Optional user identifier (can be extracted from state if embedded)
965
+ * @param accountId - Optional account alias (can be extracted from state if embedded)
862
966
  */
863
- async handleCallback(callbackUrl, userId) {
967
+ async handleCallback(callbackUrl, userId, accountId) {
864
968
  if (!(this.flow instanceof AuthCodePKCEFlow)) {
865
969
  throw new Error("handleCallback() is only available for authorization_code flow");
866
970
  }
@@ -873,21 +977,34 @@ var init_OAuthManager = __esm({
873
977
  if (!state) {
874
978
  throw new Error("Missing state parameter in callback URL");
875
979
  }
876
- await this.flow.exchangeCode(code, state, userId);
980
+ await this.flow.exchangeCode(code, state, userId, accountId);
877
981
  }
878
982
  /**
879
983
  * Revoke token (if supported by provider)
880
984
  *
881
985
  * @param revocationUrl - Optional revocation endpoint URL
882
986
  * @param userId - User identifier for multi-user support (optional)
987
+ * @param accountId - Account alias for multi-account support (optional)
883
988
  */
884
- async revokeToken(revocationUrl, userId) {
989
+ async revokeToken(revocationUrl, userId, accountId) {
885
990
  if (this.flow instanceof AuthCodePKCEFlow) {
886
- await this.flow.revokeToken(revocationUrl, userId);
991
+ await this.flow.revokeToken(revocationUrl, userId, accountId);
887
992
  } else {
888
993
  throw new Error("Token revocation not implemented for this flow");
889
994
  }
890
995
  }
996
+ /**
997
+ * List account aliases for a user (Authorization Code only)
998
+ *
999
+ * @param userId - User identifier (optional)
1000
+ * @returns Array of account aliases (e.g., ['work', 'personal'])
1001
+ */
1002
+ async listAccounts(userId) {
1003
+ if (this.flow instanceof AuthCodePKCEFlow) {
1004
+ return this.flow.listAccounts(userId);
1005
+ }
1006
+ return [];
1007
+ }
891
1008
  // ==================== Validation ====================
892
1009
  validateConfig(config) {
893
1010
  if (!config.flow) {
@@ -2093,46 +2210,59 @@ var init_Connector = __esm({
2093
2210
  /**
2094
2211
  * Get the current access token (for OAuth, JWT, or API key)
2095
2212
  * Handles automatic refresh if needed
2213
+ *
2214
+ * @param userId - Optional user identifier for multi-user support
2215
+ * @param accountId - Optional account alias for multi-account support (e.g., 'work', 'personal')
2096
2216
  */
2097
- async getToken(userId) {
2217
+ async getToken(userId, accountId) {
2098
2218
  if (this.config.auth.type === "api_key") {
2099
2219
  return this.config.auth.apiKey;
2100
2220
  }
2101
2221
  if (!this.oauthManager) {
2102
2222
  throw new Error(`OAuth manager not initialized for connector '${this.name}'`);
2103
2223
  }
2104
- return this.oauthManager.getToken(userId);
2224
+ return this.oauthManager.getToken(userId, accountId);
2105
2225
  }
2106
2226
  /**
2107
2227
  * Start OAuth authorization flow
2108
2228
  * Returns the URL to redirect the user to
2229
+ *
2230
+ * @param userId - Optional user identifier for multi-user support
2231
+ * @param accountId - Optional account alias for multi-account support (e.g., 'work', 'personal')
2109
2232
  */
2110
- async startAuth(userId) {
2233
+ async startAuth(userId, accountId) {
2111
2234
  if (!this.oauthManager) {
2112
2235
  throw new Error(`Connector '${this.name}' is not an OAuth connector`);
2113
2236
  }
2114
- return this.oauthManager.startAuthFlow(userId);
2237
+ return this.oauthManager.startAuthFlow(userId, accountId);
2115
2238
  }
2116
2239
  /**
2117
2240
  * Handle OAuth callback
2118
2241
  * Call this after user is redirected back from OAuth provider
2242
+ *
2243
+ * @param callbackUrl - Full callback URL with code and state parameters
2244
+ * @param userId - Optional user identifier (can be extracted from state if embedded)
2245
+ * @param accountId - Optional account alias (can be extracted from state if embedded)
2119
2246
  */
2120
- async handleCallback(callbackUrl, userId) {
2247
+ async handleCallback(callbackUrl, userId, accountId) {
2121
2248
  if (!this.oauthManager) {
2122
2249
  throw new Error(`Connector '${this.name}' is not an OAuth connector`);
2123
2250
  }
2124
- await this.oauthManager.handleCallback(callbackUrl, userId);
2251
+ await this.oauthManager.handleCallback(callbackUrl, userId, accountId);
2125
2252
  }
2126
2253
  /**
2127
2254
  * Check if the connector has a valid token
2255
+ *
2256
+ * @param userId - Optional user identifier for multi-user support
2257
+ * @param accountId - Optional account alias for multi-account support
2128
2258
  */
2129
- async hasValidToken(userId) {
2259
+ async hasValidToken(userId, accountId) {
2130
2260
  try {
2131
2261
  if (this.config.auth.type === "api_key") {
2132
2262
  return true;
2133
2263
  }
2134
2264
  if (this.oauthManager) {
2135
- const token = await this.oauthManager.getToken(userId);
2265
+ const token = await this.oauthManager.getToken(userId, accountId);
2136
2266
  return !!token;
2137
2267
  }
2138
2268
  return false;
@@ -2140,6 +2270,19 @@ var init_Connector = __esm({
2140
2270
  return false;
2141
2271
  }
2142
2272
  }
2273
+ /**
2274
+ * List account aliases for a user on this connector.
2275
+ * Only applicable for OAuth connectors with multi-account support.
2276
+ *
2277
+ * @param userId - Optional user identifier
2278
+ * @returns Array of account aliases (e.g., ['work', 'personal'])
2279
+ */
2280
+ async listAccounts(userId) {
2281
+ if (!this.oauthManager) {
2282
+ return [];
2283
+ }
2284
+ return this.oauthManager.listAccounts(userId);
2285
+ }
2143
2286
  /**
2144
2287
  * Get vendor-specific options from config
2145
2288
  */
@@ -2183,9 +2326,10 @@ var init_Connector = __esm({
2183
2326
  * @param endpoint - API endpoint (relative to baseURL) or full URL
2184
2327
  * @param options - Fetch options with connector-specific settings
2185
2328
  * @param userId - Optional user ID for multi-user OAuth
2329
+ * @param accountId - Optional account alias for multi-account OAuth
2186
2330
  * @returns Fetch Response
2187
2331
  */
2188
- async fetch(endpoint, options, userId) {
2332
+ async fetch(endpoint, options, userId, accountId) {
2189
2333
  if (this.disposed) {
2190
2334
  throw new Error(`Connector '${this.name}' has been disposed`);
2191
2335
  }
@@ -2204,7 +2348,7 @@ var init_Connector = __esm({
2204
2348
  this.logRequest(url2, options);
2205
2349
  }
2206
2350
  const doFetch = async () => {
2207
- const token = await this.getToken(userId);
2351
+ const token = await this.getToken(userId, accountId);
2208
2352
  const auth2 = this.config.auth;
2209
2353
  let headerName = "Authorization";
2210
2354
  let headerValue = `Bearer ${token}`;
@@ -2316,10 +2460,11 @@ var init_Connector = __esm({
2316
2460
  * @param endpoint - API endpoint (relative to baseURL) or full URL
2317
2461
  * @param options - Fetch options with connector-specific settings
2318
2462
  * @param userId - Optional user ID for multi-user OAuth
2463
+ * @param accountId - Optional account alias for multi-account OAuth
2319
2464
  * @returns Parsed JSON response
2320
2465
  */
2321
- async fetchJSON(endpoint, options, userId) {
2322
- const response = await this.fetch(endpoint, options, userId);
2466
+ async fetchJSON(endpoint, options, userId, accountId) {
2467
+ const response = await this.fetch(endpoint, options, userId, accountId);
2323
2468
  const text = await response.text();
2324
2469
  let data;
2325
2470
  try {
@@ -11268,6 +11413,18 @@ var ToolManager = class extends eventemitter3.EventEmitter {
11268
11413
  this.register(tool, options);
11269
11414
  }
11270
11415
  }
11416
+ /**
11417
+ * Register tools produced by a specific connector.
11418
+ * Sets `source: 'connector:<connectorName>'` (or `'connector:<name>:<accountId>'` for identity-bound tools)
11419
+ * so agent-level filtering can restrict which connector tools are visible to a given agent.
11420
+ */
11421
+ registerConnectorTools(connectorName, tools, options = {}) {
11422
+ const { accountId, ...toolOptions } = options;
11423
+ const source = accountId ? `connector:${connectorName}:${accountId}` : `connector:${connectorName}`;
11424
+ for (const tool of tools) {
11425
+ this.register(tool, { ...toolOptions, source });
11426
+ }
11427
+ }
11271
11428
  /**
11272
11429
  * Unregister a tool by name
11273
11430
  */
@@ -11458,6 +11615,14 @@ var ToolManager = class extends eventemitter3.EventEmitter {
11458
11615
  getEnabled() {
11459
11616
  return this.getSortedByPriority().filter((reg) => reg.enabled).map((reg) => reg.tool);
11460
11617
  }
11618
+ /**
11619
+ * Get all enabled registrations (sorted by priority).
11620
+ * Includes full registration metadata (source, namespace, etc.)
11621
+ * for use in connector-aware filtering.
11622
+ */
11623
+ getEnabledRegistrations() {
11624
+ return this.getSortedByPriority().filter((reg) => reg.enabled);
11625
+ }
11461
11626
  /**
11462
11627
  * Get all tools (enabled and disabled)
11463
11628
  */
@@ -11650,6 +11815,13 @@ var ToolManager = class extends eventemitter3.EventEmitter {
11650
11815
  if (!registration.enabled) {
11651
11816
  throw new ToolExecutionError(toolName, "Tool is disabled");
11652
11817
  }
11818
+ if (registration.source?.startsWith("connector:")) {
11819
+ const connName = registration.source.slice("connector:".length);
11820
+ const registry = this._toolContext?.connectorRegistry;
11821
+ if (registry && !registry.has(connName)) {
11822
+ throw new ToolExecutionError(toolName, `Connector '${connName}' is not available to this agent`);
11823
+ }
11824
+ }
11653
11825
  const breaker = this.getOrCreateCircuitBreaker(toolName, registration);
11654
11826
  this.toolLogger.debug({ toolName, args }, "Tool execution started");
11655
11827
  const startTime = Date.now();
@@ -11943,6 +12115,9 @@ var ToolManager = class extends eventemitter3.EventEmitter {
11943
12115
  }
11944
12116
  };
11945
12117
 
12118
+ // src/core/context-nextgen/AgentContextNextGen.ts
12119
+ init_Logger();
12120
+
11946
12121
  // src/core/Vendor.ts
11947
12122
  var Vendor = {
11948
12123
  OpenAI: "openai",
@@ -13316,6 +13491,7 @@ var ContentType = /* @__PURE__ */ ((ContentType2) => {
13316
13491
  ContentType2["OUTPUT_TEXT"] = "output_text";
13317
13492
  ContentType2["TOOL_USE"] = "tool_use";
13318
13493
  ContentType2["TOOL_RESULT"] = "tool_result";
13494
+ ContentType2["THINKING"] = "thinking";
13319
13495
  return ContentType2;
13320
13496
  })(ContentType || {});
13321
13497
 
@@ -13578,6 +13754,11 @@ var BasePluginNextGen = class {
13578
13754
  }
13579
13755
  };
13580
13756
 
13757
+ // src/core/context-nextgen/snapshot.ts
13758
+ function formatPluginDisplayName(name) {
13759
+ return name.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
13760
+ }
13761
+
13581
13762
  // src/core/context-nextgen/AgentContextNextGen.ts
13582
13763
  init_Connector();
13583
13764
 
@@ -16256,8 +16437,11 @@ var AlgorithmicCompactionStrategy = class {
16256
16437
  * Emergency compaction when context exceeds threshold.
16257
16438
  *
16258
16439
  * Strategy:
16259
- * 1. Run consolidate() first to move tool results to memory
16440
+ * 1. Run consolidate() first to move tool results to memory (if working memory available)
16260
16441
  * 2. If still need space, apply rolling window (remove oldest messages)
16442
+ *
16443
+ * Gracefully degrades: if working memory plugin is not registered,
16444
+ * skips step 1 and only uses rolling window compaction.
16261
16445
  */
16262
16446
  async compact(context, targetToFree) {
16263
16447
  const log = [];
@@ -16270,7 +16454,7 @@ var AlgorithmicCompactionStrategy = class {
16270
16454
  tokensFreed += Math.abs(consolidateResult.tokensChanged);
16271
16455
  log.push(...consolidateResult.actions);
16272
16456
  }
16273
- let remaining = targetToFree - tokensFreed;
16457
+ const remaining = targetToFree - tokensFreed;
16274
16458
  if (remaining > 0 && context.conversation.length > 0) {
16275
16459
  log.push(`Rolling window: need to free ~${remaining} more tokens`);
16276
16460
  const result = await this.applyRollingWindow(context, remaining, log);
@@ -16284,8 +16468,11 @@ var AlgorithmicCompactionStrategy = class {
16284
16468
  * Post-cycle consolidation.
16285
16469
  *
16286
16470
  * 1. Find all tool pairs in conversation
16287
- * 2. Move large tool results (> threshold) to Working Memory
16471
+ * 2. Move large tool results (> threshold) to Working Memory (if available)
16288
16472
  * 3. Limit remaining tool pairs to maxToolPairs
16473
+ *
16474
+ * Gracefully degrades: if working memory is not available, skips step 2
16475
+ * and only limits tool pairs + removes excess via rolling window.
16289
16476
  */
16290
16477
  async consolidate(context) {
16291
16478
  const log = [];
@@ -16296,23 +16483,25 @@ var AlgorithmicCompactionStrategy = class {
16296
16483
  return { performed: false, tokensChanged: 0, actions: [] };
16297
16484
  }
16298
16485
  const indicesToRemove = [];
16299
- for (const pair of toolPairs) {
16300
- if (pair.resultSizeBytes > this.toolResultSizeThreshold) {
16301
- const key = this.generateKey(pair.toolName, pair.toolUseId);
16302
- const desc = this.generateDescription(pair.toolName, pair.toolArgs);
16303
- await memory.store(key, desc, pair.resultContent, {
16304
- tier: "raw",
16305
- priority: "normal"
16306
- });
16307
- if (!indicesToRemove.includes(pair.toolUseIndex)) {
16308
- indicesToRemove.push(pair.toolUseIndex);
16309
- }
16310
- if (!indicesToRemove.includes(pair.toolResultIndex)) {
16311
- indicesToRemove.push(pair.toolResultIndex);
16486
+ if (memory) {
16487
+ for (const pair of toolPairs) {
16488
+ if (pair.resultSizeBytes > this.toolResultSizeThreshold) {
16489
+ const key = this.generateKey(pair.toolName, pair.toolUseId);
16490
+ const desc = this.generateDescription(pair.toolName, pair.toolArgs);
16491
+ await memory.store(key, desc, pair.resultContent, {
16492
+ tier: "raw",
16493
+ priority: "normal"
16494
+ });
16495
+ if (!indicesToRemove.includes(pair.toolUseIndex)) {
16496
+ indicesToRemove.push(pair.toolUseIndex);
16497
+ }
16498
+ if (!indicesToRemove.includes(pair.toolResultIndex)) {
16499
+ indicesToRemove.push(pair.toolResultIndex);
16500
+ }
16501
+ log.push(
16502
+ `Moved ${pair.toolName} result (${this.formatBytes(pair.resultSizeBytes)}) to memory: ${key}`
16503
+ );
16312
16504
  }
16313
- log.push(
16314
- `Moved ${pair.toolName} result (${this.formatBytes(pair.resultSizeBytes)}) to memory: ${key}`
16315
- );
16316
16505
  }
16317
16506
  }
16318
16507
  const remainingPairs = toolPairs.filter(
@@ -16341,15 +16530,12 @@ var AlgorithmicCompactionStrategy = class {
16341
16530
  };
16342
16531
  }
16343
16532
  /**
16344
- * Get the Working Memory plugin from context.
16345
- * @throws Error if plugin is not available
16533
+ * Get the Working Memory plugin from context, or null if not available.
16534
+ * When null, the strategy degrades gracefully (skips memory operations).
16346
16535
  */
16347
16536
  getWorkingMemory(context) {
16348
16537
  const plugin = context.plugins.find((p) => p.name === "working_memory");
16349
- if (!plugin) {
16350
- throw new Error("AlgorithmicCompactionStrategy requires working_memory plugin");
16351
- }
16352
- return plugin;
16538
+ return plugin ? plugin : null;
16353
16539
  }
16354
16540
  /**
16355
16541
  * Find all tool_use/tool_result pairs in conversation.
@@ -16728,12 +16914,14 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
16728
16914
  _agentId;
16729
16915
  /** User ID for multi-user scenarios */
16730
16916
  _userId;
16731
- /** Allowed connector names (when agent is restricted to a subset) */
16732
- _allowedConnectors;
16917
+ /** Auth identities this agent is scoped to (connector + optional accountId) */
16918
+ _identities;
16733
16919
  /** Storage backend */
16734
16920
  _storage;
16735
16921
  /** Destroyed flag */
16736
16922
  _destroyed = false;
16923
+ /** Last thinking/reasoning content from the most recent assistant response */
16924
+ _lastThinking = null;
16737
16925
  /** Cached budget from last prepare() call */
16738
16926
  _cachedBudget = null;
16739
16927
  /** Callback for beforeCompaction hook (set by Agent) */
@@ -16767,7 +16955,7 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
16767
16955
  this._systemPrompt = config.systemPrompt;
16768
16956
  this._agentId = this._config.agentId;
16769
16957
  this._userId = config.userId;
16770
- this._allowedConnectors = config.connectors;
16958
+ this._identities = config.identities;
16771
16959
  const sessionFactory = exports.StorageRegistry.get("sessions");
16772
16960
  const storageCtx = exports.StorageRegistry.getContext() ?? (config.userId ? { userId: config.userId } : void 0);
16773
16961
  this._storage = config.storage ?? (sessionFactory ? sessionFactory(this._agentId, storageCtx) : void 0);
@@ -16821,15 +17009,16 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
16821
17009
  }
16822
17010
  /**
16823
17011
  * Validate that a strategy's required plugins are registered.
16824
- * @throws Error if any required plugin is missing
17012
+ * Logs a warning if required plugins are missing — the strategy should degrade gracefully.
16825
17013
  */
16826
17014
  validateStrategyDependencies(strategy) {
16827
17015
  if (!strategy.requiredPlugins?.length) return;
16828
17016
  const availablePlugins = new Set(this._plugins.keys());
16829
17017
  const missing = strategy.requiredPlugins.filter((name) => !availablePlugins.has(name));
16830
17018
  if (missing.length > 0) {
16831
- throw new Error(
16832
- `Strategy '${strategy.name}' requires plugins that are not registered: ${missing.join(", ")}. Available plugins: ${Array.from(availablePlugins).join(", ") || "none"}`
17019
+ exports.logger.warn(
17020
+ { strategy: strategy.name, missing, available: Array.from(availablePlugins) },
17021
+ `Strategy '${strategy.name}' recommends plugins that are not registered: ${missing.join(", ")}. Strategy will degrade gracefully.`
16833
17022
  );
16834
17023
  }
16835
17024
  }
@@ -16838,7 +17027,7 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
16838
17027
  * Merges with existing ToolContext to preserve other fields (memory, signal, taskId).
16839
17028
  *
16840
17029
  * Connector registry resolution order:
16841
- * 1. If `connectors` (allowed names) is set → filtered view of global registry
17030
+ * 1. If `identities` is set → filtered view showing only identity connectors
16842
17031
  * 2. If access policy + userId → scoped view via Connector.scoped()
16843
17032
  * 3. Otherwise → full global registry
16844
17033
  */
@@ -16848,6 +17037,7 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
16848
17037
  ...existing,
16849
17038
  agentId: this._agentId,
16850
17039
  userId: this._userId,
17040
+ identities: this._identities,
16851
17041
  connectorRegistry: this.buildConnectorRegistry()
16852
17042
  });
16853
17043
  }
@@ -16855,13 +17045,13 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
16855
17045
  * Build the connector registry appropriate for this agent's config.
16856
17046
  */
16857
17047
  buildConnectorRegistry() {
16858
- if (this._allowedConnectors?.length) {
16859
- const allowedSet = new Set(this._allowedConnectors);
17048
+ if (this._identities?.length) {
17049
+ const allowedSet = new Set(this._identities.map((id) => id.connector));
16860
17050
  const base = this._userId && exports.Connector.getAccessPolicy() ? exports.Connector.scoped({ userId: this._userId }) : exports.Connector.asRegistry();
16861
17051
  return {
16862
17052
  get: (name) => {
16863
17053
  if (!allowedSet.has(name)) {
16864
- const available = this._allowedConnectors.filter((n) => base.has(n)).join(", ") || "none";
17054
+ const available = [...allowedSet].filter((n) => base.has(n)).join(", ") || "none";
16865
17055
  throw new Error(`Connector '${name}' not found. Available: ${available}`);
16866
17056
  }
16867
17057
  return base.get(name);
@@ -16913,13 +17103,13 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
16913
17103
  this._userId = value;
16914
17104
  this.syncToolContext();
16915
17105
  }
16916
- /** Get the allowed connector names (undefined = all visible connectors) */
16917
- get connectors() {
16918
- return this._allowedConnectors;
17106
+ /** Get the auth identities this agent is scoped to (undefined = all visible connectors) */
17107
+ get identities() {
17108
+ return this._identities;
16919
17109
  }
16920
- /** Set allowed connector names. Updates ToolContext.connectorRegistry. */
16921
- set connectors(value) {
16922
- this._allowedConnectors = value;
17110
+ /** Set auth identities. Updates ToolContext.connectorRegistry and identity-aware descriptions. */
17111
+ set identities(value) {
17112
+ this._identities = value;
16923
17113
  this.syncToolContext();
16924
17114
  }
16925
17115
  /** Get/set system prompt */
@@ -16945,6 +17135,13 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
16945
17135
  get storage() {
16946
17136
  return this._storage ?? null;
16947
17137
  }
17138
+ /**
17139
+ * Get the last thinking/reasoning content from the most recent assistant response.
17140
+ * Updated on every assistant response, always available regardless of persistence setting.
17141
+ */
17142
+ get lastThinking() {
17143
+ return this._lastThinking;
17144
+ }
16948
17145
  /** Get max context tokens */
16949
17146
  get maxContextTokens() {
16950
17147
  return this._maxContextTokens;
@@ -17126,6 +17323,7 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
17126
17323
  }
17127
17324
  const id = this.generateId();
17128
17325
  const contentArray = [];
17326
+ let thinkingText = null;
17129
17327
  for (const item of output) {
17130
17328
  if (item.type === "message" && "content" in item) {
17131
17329
  const msg = item;
@@ -17137,12 +17335,19 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
17137
17335
  });
17138
17336
  } else if (c.type === "tool_use" /* TOOL_USE */) {
17139
17337
  contentArray.push(c);
17338
+ } else if (c.type === "thinking" /* THINKING */) {
17339
+ const thinking = c;
17340
+ thinkingText = thinking.thinking;
17341
+ if (thinking.persistInHistory) {
17342
+ contentArray.push(c);
17343
+ }
17140
17344
  }
17141
17345
  }
17142
17346
  } else if (item.type === "compaction" || item.type === "reasoning") {
17143
17347
  continue;
17144
17348
  }
17145
17349
  }
17350
+ this._lastThinking = thinkingText;
17146
17351
  if (contentArray.length > 0) {
17147
17352
  const message = {
17148
17353
  type: "message",
@@ -17241,6 +17446,7 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
17241
17446
  */
17242
17447
  async prepare() {
17243
17448
  this.assertNotDestroyed();
17449
+ this._lastThinking = null;
17244
17450
  const compactionLog = [];
17245
17451
  const toolsTokens = this.calculateToolsTokens();
17246
17452
  const availableForContent = this._maxContextTokens - this._config.responseReserve - toolsTokens;
@@ -17453,6 +17659,8 @@ ${content}`);
17453
17659
  total += this._estimateImageTokens();
17454
17660
  }
17455
17661
  }
17662
+ } else if (c.type === "thinking" /* THINKING */) {
17663
+ total += this._estimator.estimateTokens(c.thinking || "");
17456
17664
  } else if (c.type === "input_image_url" /* INPUT_IMAGE_URL */) {
17457
17665
  const imgContent = c;
17458
17666
  const detail = imgContent.image_url?.detail;
@@ -17950,6 +18158,188 @@ ${content}`);
17950
18158
  get strategy() {
17951
18159
  return this._compactionStrategy.name;
17952
18160
  }
18161
+ /**
18162
+ * Get a complete, serializable snapshot of the context state.
18163
+ *
18164
+ * Returns all data needed by UI "Look Inside" panels without reaching
18165
+ * into plugin internals. Plugin data is auto-discovered from the plugin
18166
+ * registry — new/custom plugins appear automatically.
18167
+ *
18168
+ * @param toolStats - Optional tool usage stats (from ToolManager.getStats())
18169
+ * @returns Serializable context snapshot
18170
+ */
18171
+ async getSnapshot(toolStats) {
18172
+ const resolveContents = async (raw) => {
18173
+ const resolved = raw instanceof Promise ? await raw : raw;
18174
+ if (resolved instanceof Map) return Array.from(resolved.values());
18175
+ return resolved;
18176
+ };
18177
+ if (this._destroyed) {
18178
+ const emptyBudget = this._cachedBudget ?? {
18179
+ maxTokens: this._maxContextTokens,
18180
+ responseReserve: this._config.responseReserve,
18181
+ systemMessageTokens: 0,
18182
+ toolsTokens: 0,
18183
+ conversationTokens: 0,
18184
+ currentInputTokens: 0,
18185
+ totalUsed: 0,
18186
+ available: this._maxContextTokens - this._config.responseReserve,
18187
+ utilizationPercent: 0,
18188
+ breakdown: {
18189
+ systemPrompt: 0,
18190
+ persistentInstructions: 0,
18191
+ pluginInstructions: 0,
18192
+ pluginContents: {},
18193
+ tools: 0,
18194
+ conversation: 0,
18195
+ currentInput: 0
18196
+ }
18197
+ };
18198
+ return {
18199
+ available: false,
18200
+ agentId: this._agentId,
18201
+ model: this._config.model,
18202
+ features: this._config.features,
18203
+ budget: emptyBudget,
18204
+ strategy: this._compactionStrategy.name,
18205
+ messagesCount: 0,
18206
+ toolCallsCount: 0,
18207
+ systemPrompt: null,
18208
+ plugins: [],
18209
+ tools: []
18210
+ };
18211
+ }
18212
+ const budget = await this.calculateBudget();
18213
+ const plugins = [];
18214
+ for (const plugin of this._plugins.values()) {
18215
+ let formattedContent = null;
18216
+ try {
18217
+ formattedContent = await plugin.getContent();
18218
+ } catch {
18219
+ }
18220
+ plugins.push({
18221
+ name: plugin.name,
18222
+ displayName: formatPluginDisplayName(plugin.name),
18223
+ enabled: true,
18224
+ tokenSize: plugin.getTokenSize(),
18225
+ instructionsTokenSize: plugin.getInstructionsTokenSize(),
18226
+ compactable: plugin.isCompactable(),
18227
+ contents: await resolveContents(plugin.getContents()),
18228
+ formattedContent
18229
+ });
18230
+ }
18231
+ const usageCounts = /* @__PURE__ */ new Map();
18232
+ if (toolStats?.mostUsed) {
18233
+ for (const { name, count } of toolStats.mostUsed) {
18234
+ usageCounts.set(name, count);
18235
+ }
18236
+ }
18237
+ const tools = [];
18238
+ for (const toolName of this._tools.list()) {
18239
+ const reg = this._tools.getRegistration(toolName);
18240
+ if (!reg) continue;
18241
+ tools.push({
18242
+ name: toolName,
18243
+ description: reg.tool.definition.function.description || "",
18244
+ enabled: reg.enabled,
18245
+ callCount: reg.metadata.usageCount ?? usageCounts.get(toolName) ?? 0,
18246
+ namespace: reg.namespace || void 0
18247
+ });
18248
+ }
18249
+ let toolCallsCount = 0;
18250
+ for (const item of this._conversation) {
18251
+ if (item.type === "message" && item.role === "assistant" /* ASSISTANT */) {
18252
+ for (const c of item.content) {
18253
+ if (c.type === "tool_use" /* TOOL_USE */) toolCallsCount++;
18254
+ }
18255
+ }
18256
+ }
18257
+ return {
18258
+ available: true,
18259
+ agentId: this._agentId,
18260
+ model: this._config.model,
18261
+ features: this._config.features,
18262
+ budget,
18263
+ strategy: this._compactionStrategy.name,
18264
+ messagesCount: this._conversation.length,
18265
+ toolCallsCount,
18266
+ systemPrompt: this._systemPrompt ?? null,
18267
+ plugins,
18268
+ tools
18269
+ };
18270
+ }
18271
+ /**
18272
+ * Get a human-readable breakdown of the prepared context.
18273
+ *
18274
+ * Calls `prepare()` internally, then maps each InputItem to a named
18275
+ * component with content text and token estimate. Used by "View Full Context" UIs.
18276
+ *
18277
+ * @returns View context data with components and raw text for "Copy All"
18278
+ */
18279
+ async getViewContext() {
18280
+ if (this._destroyed) {
18281
+ return { available: false, components: [], totalTokens: 0, rawContext: "" };
18282
+ }
18283
+ const { input, budget } = await this.prepare();
18284
+ const components = [];
18285
+ let rawParts = [];
18286
+ for (const item of input) {
18287
+ if (item.type === "compaction") {
18288
+ components.push({
18289
+ name: "Compaction Block",
18290
+ content: "[Compacted content]",
18291
+ tokenEstimate: 0
18292
+ });
18293
+ continue;
18294
+ }
18295
+ const msg = item;
18296
+ const roleName = msg.role === "developer" /* DEVELOPER */ ? "System Message" : msg.role === "user" /* USER */ ? "User Message" : "Assistant Message";
18297
+ for (const block of msg.content) {
18298
+ let name = roleName;
18299
+ let text = "";
18300
+ switch (block.type) {
18301
+ case "input_text" /* INPUT_TEXT */:
18302
+ text = block.text;
18303
+ break;
18304
+ case "output_text" /* OUTPUT_TEXT */:
18305
+ text = block.text;
18306
+ break;
18307
+ case "tool_use" /* TOOL_USE */:
18308
+ name = `Tool Call: ${block.name}`;
18309
+ text = `${block.name}(${block.arguments})`;
18310
+ break;
18311
+ case "tool_result" /* TOOL_RESULT */:
18312
+ name = `Tool Result: ${block.tool_use_id}`;
18313
+ text = typeof block.content === "string" ? block.content : JSON.stringify(block.content, null, 2);
18314
+ if (block.error) text = `[Error] ${block.error}
18315
+ ${text}`;
18316
+ break;
18317
+ case "input_image_url" /* INPUT_IMAGE_URL */:
18318
+ name = "Image Input";
18319
+ text = `[Image: ${block.image_url.url.substring(0, 100)}...]`;
18320
+ break;
18321
+ case "input_file" /* INPUT_FILE */:
18322
+ name = "File Input";
18323
+ text = `[File: ${block.file_id}]`;
18324
+ break;
18325
+ case "thinking" /* THINKING */:
18326
+ name = "Thinking";
18327
+ text = block.thinking || "";
18328
+ break;
18329
+ }
18330
+ const tokenEstimate = this._estimator.estimateTokens(text);
18331
+ components.push({ name, content: text, tokenEstimate });
18332
+ rawParts.push(`--- ${name} ---
18333
+ ${text}`);
18334
+ }
18335
+ }
18336
+ return {
18337
+ available: true,
18338
+ components,
18339
+ totalTokens: budget.totalUsed,
18340
+ rawContext: rawParts.join("\n\n")
18341
+ };
18342
+ }
17953
18343
  // ============================================================================
17954
18344
  // Utilities
17955
18345
  // ============================================================================
@@ -18208,6 +18598,13 @@ var BaseTextProvider = class extends BaseProvider {
18208
18598
  }
18209
18599
  return textParts.join("\n");
18210
18600
  }
18601
+ /**
18602
+ * List available models from the provider's API.
18603
+ * Default returns empty array; providers override when they have SDK support.
18604
+ */
18605
+ async listModels() {
18606
+ return [];
18607
+ }
18211
18608
  /**
18212
18609
  * Clean up provider resources (circuit breaker listeners, etc.)
18213
18610
  * Should be called when the provider is no longer needed.
@@ -18360,12 +18757,21 @@ var OpenAIResponsesConverter = class {
18360
18757
  } else if (item.type === "reasoning") {
18361
18758
  const reasoning = item;
18362
18759
  if (reasoning.summary) {
18363
- content.push({
18364
- type: "reasoning",
18365
- summary: reasoning.summary,
18366
- // effort field may not exist in all versions
18367
- ..."effort" in reasoning && { effort: reasoning.effort }
18368
- });
18760
+ let summaryText;
18761
+ if (typeof reasoning.summary === "string") {
18762
+ summaryText = reasoning.summary;
18763
+ } else if (Array.isArray(reasoning.summary)) {
18764
+ summaryText = reasoning.summary.map((s) => s.text || "").filter(Boolean).join("\n");
18765
+ } else {
18766
+ summaryText = "";
18767
+ }
18768
+ if (summaryText) {
18769
+ content.push({
18770
+ type: "thinking" /* THINKING */,
18771
+ thinking: summaryText,
18772
+ persistInHistory: false
18773
+ });
18774
+ }
18369
18775
  }
18370
18776
  }
18371
18777
  }
@@ -18388,10 +18794,20 @@ var OpenAIResponsesConverter = class {
18388
18794
  }
18389
18795
  ],
18390
18796
  output_text: outputText,
18797
+ // Extract thinking text from content for convenience field
18798
+ ...(() => {
18799
+ const thinkingTexts = content.filter((c) => c.type === "thinking" /* THINKING */).map((c) => c.thinking).filter(Boolean);
18800
+ return thinkingTexts.length > 0 ? { thinking: thinkingTexts.join("\n") } : {};
18801
+ })(),
18391
18802
  usage: {
18392
18803
  input_tokens: response.usage?.input_tokens || 0,
18393
18804
  output_tokens: response.usage?.output_tokens || 0,
18394
- total_tokens: response.usage?.total_tokens || 0
18805
+ total_tokens: response.usage?.total_tokens || 0,
18806
+ ...response.usage?.output_tokens_details?.reasoning_tokens != null && {
18807
+ output_tokens_details: {
18808
+ reasoning_tokens: response.usage.output_tokens_details.reasoning_tokens
18809
+ }
18810
+ }
18395
18811
  }
18396
18812
  };
18397
18813
  }
@@ -18496,6 +18912,8 @@ var StreamEventType = /* @__PURE__ */ ((StreamEventType2) => {
18496
18912
  StreamEventType2["TOOL_EXECUTION_START"] = "response.tool_execution.start";
18497
18913
  StreamEventType2["TOOL_EXECUTION_DONE"] = "response.tool_execution.done";
18498
18914
  StreamEventType2["ITERATION_COMPLETE"] = "response.iteration.complete";
18915
+ StreamEventType2["REASONING_DELTA"] = "response.reasoning.delta";
18916
+ StreamEventType2["REASONING_DONE"] = "response.reasoning.done";
18499
18917
  StreamEventType2["RESPONSE_COMPLETE"] = "response.complete";
18500
18918
  StreamEventType2["ERROR"] = "response.error";
18501
18919
  return StreamEventType2;
@@ -18515,6 +18933,12 @@ function isToolCallArgumentsDelta(event) {
18515
18933
  function isToolCallArgumentsDone(event) {
18516
18934
  return event.type === "response.tool_call_arguments.done" /* TOOL_CALL_ARGUMENTS_DONE */;
18517
18935
  }
18936
+ function isReasoningDelta(event) {
18937
+ return event.type === "response.reasoning.delta" /* REASONING_DELTA */;
18938
+ }
18939
+ function isReasoningDone(event) {
18940
+ return event.type === "response.reasoning.done" /* REASONING_DONE */;
18941
+ }
18518
18942
  function isResponseComplete(event) {
18519
18943
  return event.type === "response.complete" /* RESPONSE_COMPLETE */;
18520
18944
  }
@@ -18532,6 +18956,8 @@ var OpenAIResponsesStreamConverter = class {
18532
18956
  let sequenceNumber = 0;
18533
18957
  const activeItems = /* @__PURE__ */ new Map();
18534
18958
  const toolCallBuffers = /* @__PURE__ */ new Map();
18959
+ const reasoningBuffers = /* @__PURE__ */ new Map();
18960
+ const reasoningDoneEmitted = /* @__PURE__ */ new Set();
18535
18961
  for await (const event of stream) {
18536
18962
  if (process.env.DEBUG_OPENAI) {
18537
18963
  console.error("[DEBUG] Responses API event:", event.type);
@@ -18553,6 +18979,12 @@ var OpenAIResponsesStreamConverter = class {
18553
18979
  activeItems.set(addedEvent.output_index.toString(), {
18554
18980
  type: item.type
18555
18981
  });
18982
+ if (item.type === "reasoning") {
18983
+ activeItems.set(addedEvent.output_index.toString(), {
18984
+ type: "reasoning"
18985
+ });
18986
+ reasoningBuffers.set(addedEvent.output_index.toString(), []);
18987
+ }
18556
18988
  if (item.type === "function_call") {
18557
18989
  const functionCall = item;
18558
18990
  const toolCallId = functionCall.call_id;
@@ -18610,9 +19042,53 @@ var OpenAIResponsesStreamConverter = class {
18610
19042
  }
18611
19043
  break;
18612
19044
  }
19045
+ case "response.reasoning_summary_text.delta":
19046
+ case "response.reasoning_text.delta": {
19047
+ const reasoningEvent = event;
19048
+ const outputIdx = reasoningEvent.output_index?.toString();
19049
+ const buffer = outputIdx ? reasoningBuffers.get(outputIdx) : void 0;
19050
+ if (buffer) {
19051
+ buffer.push(reasoningEvent.delta || "");
19052
+ }
19053
+ yield {
19054
+ type: "response.reasoning.delta" /* REASONING_DELTA */,
19055
+ response_id: responseId,
19056
+ item_id: reasoningEvent.item_id || `reasoning_${responseId}`,
19057
+ delta: reasoningEvent.delta || "",
19058
+ sequence_number: sequenceNumber++
19059
+ };
19060
+ break;
19061
+ }
19062
+ case "response.reasoning_text.done": {
19063
+ const doneEvent = event;
19064
+ const outputIdx = doneEvent.output_index.toString();
19065
+ const rBuf = reasoningBuffers.get(outputIdx);
19066
+ const thinkingText = rBuf ? rBuf.join("") : doneEvent.text || "";
19067
+ reasoningDoneEmitted.add(outputIdx);
19068
+ yield {
19069
+ type: "response.reasoning.done" /* REASONING_DONE */,
19070
+ response_id: responseId,
19071
+ item_id: doneEvent.item_id || `reasoning_${responseId}`,
19072
+ thinking: thinkingText
19073
+ };
19074
+ break;
19075
+ }
18613
19076
  case "response.output_item.done": {
18614
19077
  const doneEvent = event;
18615
19078
  const item = doneEvent.item;
19079
+ if (item.type === "reasoning") {
19080
+ const outputIdx = doneEvent.output_index.toString();
19081
+ if (!reasoningDoneEmitted.has(outputIdx)) {
19082
+ const rBuf = reasoningBuffers.get(outputIdx);
19083
+ const thinkingText = rBuf ? rBuf.join("") : "";
19084
+ yield {
19085
+ type: "response.reasoning.done" /* REASONING_DONE */,
19086
+ response_id: responseId,
19087
+ item_id: item.id || `reasoning_${responseId}`,
19088
+ thinking: thinkingText
19089
+ };
19090
+ }
19091
+ }
18616
19092
  if (item.type === "function_call") {
18617
19093
  const functionCall = item;
18618
19094
  const buffer = toolCallBuffers.get(functionCall.call_id);
@@ -18644,7 +19120,12 @@ var OpenAIResponsesStreamConverter = class {
18644
19120
  usage: {
18645
19121
  input_tokens: response.usage?.input_tokens || 0,
18646
19122
  output_tokens: response.usage?.output_tokens || 0,
18647
- total_tokens: response.usage?.total_tokens || 0
19123
+ total_tokens: response.usage?.total_tokens || 0,
19124
+ ...response.usage?.output_tokens_details?.reasoning_tokens != null && {
19125
+ output_tokens_details: {
19126
+ reasoning_tokens: response.usage.output_tokens_details.reasoning_tokens
19127
+ }
19128
+ }
18648
19129
  },
18649
19130
  iterations: 1
18650
19131
  };
@@ -18685,6 +19166,26 @@ function resolveMaxContextTokens(model, fallback) {
18685
19166
  return info ? info.features.input.tokens : fallback;
18686
19167
  }
18687
19168
 
19169
+ // src/infrastructure/providers/shared/validateThinkingConfig.ts
19170
+ function validateThinkingConfig(thinking) {
19171
+ if (!thinking.enabled) return;
19172
+ if (thinking.budgetTokens !== void 0) {
19173
+ if (typeof thinking.budgetTokens !== "number" || thinking.budgetTokens < 1) {
19174
+ throw new Error(
19175
+ `Invalid thinking budgetTokens: ${thinking.budgetTokens}. Must be a positive number.`
19176
+ );
19177
+ }
19178
+ }
19179
+ if (thinking.effort !== void 0) {
19180
+ const validEfforts = ["low", "medium", "high"];
19181
+ if (!validEfforts.includes(thinking.effort)) {
19182
+ throw new Error(
19183
+ `Invalid thinking effort: '${thinking.effort}'. Must be one of: ${validEfforts.join(", ")}`
19184
+ );
19185
+ }
19186
+ }
19187
+ }
19188
+
18688
19189
  // src/infrastructure/providers/openai/OpenAITextProvider.ts
18689
19190
  var OpenAITextProvider = class extends BaseTextProvider {
18690
19191
  name = "openai";
@@ -18752,6 +19253,7 @@ var OpenAITextProvider = class extends BaseTextProvider {
18752
19253
  },
18753
19254
  ...options.metadata && { metadata: options.metadata }
18754
19255
  };
19256
+ this.applyReasoningConfig(params, options);
18755
19257
  const response = await this.client.responses.create(params);
18756
19258
  return this.converter.convertResponse(response);
18757
19259
  } catch (error) {
@@ -18793,6 +19295,7 @@ var OpenAITextProvider = class extends BaseTextProvider {
18793
19295
  ...options.metadata && { metadata: options.metadata },
18794
19296
  stream: true
18795
19297
  };
19298
+ this.applyReasoningConfig(params, options);
18796
19299
  const stream = await this.client.responses.create(params);
18797
19300
  yield* this.streamConverter.convertStream(stream);
18798
19301
  } catch (error) {
@@ -18814,6 +19317,27 @@ var OpenAITextProvider = class extends BaseTextProvider {
18814
19317
  maxOutputTokens: 16384
18815
19318
  });
18816
19319
  }
19320
+ /**
19321
+ * List available models from the OpenAI API
19322
+ */
19323
+ async listModels() {
19324
+ const models = [];
19325
+ for await (const model of this.client.models.list()) {
19326
+ models.push(model.id);
19327
+ }
19328
+ return models.sort();
19329
+ }
19330
+ /**
19331
+ * Apply reasoning config from unified thinking option to request params
19332
+ */
19333
+ applyReasoningConfig(params, options) {
19334
+ if (options.thinking?.enabled) {
19335
+ validateThinkingConfig(options.thinking);
19336
+ params.reasoning = {
19337
+ effort: options.thinking.effort || "medium"
19338
+ };
19339
+ }
19340
+ }
18817
19341
  /**
18818
19342
  * Handle OpenAI-specific errors
18819
19343
  */
@@ -18856,6 +19380,7 @@ function buildLLMResponse(options) {
18856
19380
  }
18857
19381
  ];
18858
19382
  const outputText = extractTextFromContent(content);
19383
+ const thinking = extractThinkingFromContent(content);
18859
19384
  return {
18860
19385
  id: responseId,
18861
19386
  object: "response",
@@ -18864,6 +19389,7 @@ function buildLLMResponse(options) {
18864
19389
  model,
18865
19390
  output,
18866
19391
  output_text: outputText,
19392
+ ...thinking && { thinking },
18867
19393
  usage: {
18868
19394
  input_tokens: usage.inputTokens,
18869
19395
  output_tokens: usage.outputTokens,
@@ -18876,6 +19402,10 @@ function extractTextFromContent(content) {
18876
19402
  (c) => c.type === "output_text" /* OUTPUT_TEXT */
18877
19403
  ).map((c) => c.text).join("\n");
18878
19404
  }
19405
+ function extractThinkingFromContent(content) {
19406
+ const thinkingTexts = content.filter((c) => c.type === "thinking" /* THINKING */).map((c) => c.thinking).filter(Boolean);
19407
+ return thinkingTexts.length > 0 ? thinkingTexts.join("\n") : void 0;
19408
+ }
18879
19409
  function createTextContent(text) {
18880
19410
  return {
18881
19411
  type: "output_text" /* OUTPUT_TEXT */,
@@ -19125,7 +19655,15 @@ var AnthropicConverter = class extends BaseConverter {
19125
19655
  if (tools && tools.length > 0) {
19126
19656
  params.tools = tools;
19127
19657
  }
19128
- if (options.temperature !== void 0) {
19658
+ if (options.thinking?.enabled) {
19659
+ validateThinkingConfig(options.thinking);
19660
+ const budgetTokens = options.thinking.budgetTokens || 1e4;
19661
+ params.thinking = {
19662
+ type: "enabled",
19663
+ budget_tokens: budgetTokens
19664
+ };
19665
+ params.temperature = 1;
19666
+ } else if (options.temperature !== void 0) {
19129
19667
  params.temperature = options.temperature;
19130
19668
  }
19131
19669
  return params;
@@ -19171,6 +19709,14 @@ var AnthropicConverter = class extends BaseConverter {
19171
19709
  content.push(this.createText(block.text));
19172
19710
  } else if (block.type === "tool_use") {
19173
19711
  content.push(this.createToolUse(block.id, block.name, block.input));
19712
+ } else if (block.type === "thinking") {
19713
+ const thinkingBlock = block;
19714
+ content.push({
19715
+ type: "thinking" /* THINKING */,
19716
+ thinking: thinkingBlock.thinking || "",
19717
+ signature: thinkingBlock.signature,
19718
+ persistInHistory: true
19719
+ });
19174
19720
  }
19175
19721
  }
19176
19722
  return content;
@@ -19249,6 +19795,17 @@ var AnthropicConverter = class extends BaseConverter {
19249
19795
  });
19250
19796
  break;
19251
19797
  }
19798
+ case "thinking" /* THINKING */: {
19799
+ const thinkingContent = c;
19800
+ if (thinkingContent.signature) {
19801
+ blocks.push({
19802
+ type: "thinking",
19803
+ thinking: thinkingContent.thinking,
19804
+ signature: thinkingContent.signature
19805
+ });
19806
+ }
19807
+ break;
19808
+ }
19252
19809
  }
19253
19810
  }
19254
19811
  if (blocks.length === 1 && blocks[0]?.type === "text") {
@@ -19381,6 +19938,8 @@ var BaseStreamConverter = class {
19381
19938
  usage = { inputTokens: 0, outputTokens: 0 };
19382
19939
  /** Buffers for accumulating tool call arguments */
19383
19940
  toolCallBuffers = /* @__PURE__ */ new Map();
19941
+ /** Buffer for accumulating reasoning/thinking content */
19942
+ reasoningBuffer = "";
19384
19943
  // ==========================================================================
19385
19944
  // Public API
19386
19945
  // ==========================================================================
@@ -19415,6 +19974,7 @@ var BaseStreamConverter = class {
19415
19974
  this.sequenceNumber = 0;
19416
19975
  this.usage = { inputTokens: 0, outputTokens: 0 };
19417
19976
  this.toolCallBuffers.clear();
19977
+ this.reasoningBuffer = "";
19418
19978
  }
19419
19979
  /**
19420
19980
  * Reset converter state for a new stream
@@ -19469,6 +20029,33 @@ var BaseStreamConverter = class {
19469
20029
  sequence_number: this.nextSequence()
19470
20030
  };
19471
20031
  }
20032
+ /**
20033
+ * Create REASONING_DELTA event and accumulate reasoning buffer
20034
+ */
20035
+ emitReasoningDelta(delta, itemId) {
20036
+ this.reasoningBuffer += delta;
20037
+ return {
20038
+ type: "response.reasoning.delta" /* REASONING_DELTA */,
20039
+ response_id: this.responseId,
20040
+ item_id: itemId || `reasoning_${this.responseId}`,
20041
+ delta,
20042
+ sequence_number: this.nextSequence()
20043
+ };
20044
+ }
20045
+ /**
20046
+ * Create REASONING_DONE event with accumulated reasoning
20047
+ */
20048
+ emitReasoningDone(itemId) {
20049
+ const id = itemId || `reasoning_${this.responseId}`;
20050
+ const thinking = this.reasoningBuffer;
20051
+ this.reasoningBuffer = "";
20052
+ return {
20053
+ type: "response.reasoning.done" /* REASONING_DONE */,
20054
+ response_id: this.responseId,
20055
+ item_id: id,
20056
+ thinking
20057
+ };
20058
+ }
19472
20059
  /**
19473
20060
  * Create TOOL_CALL_START event
19474
20061
  */
@@ -19613,7 +20200,10 @@ var AnthropicStreamConverter = class extends BaseStreamConverter {
19613
20200
  handleContentBlockStart(event) {
19614
20201
  const index = event.index;
19615
20202
  const block = event.content_block;
19616
- if (block.type === "text") {
20203
+ if (block.type === "thinking") {
20204
+ this.contentBlockIndex.set(index, { type: "thinking" });
20205
+ return [];
20206
+ } else if (block.type === "text") {
19617
20207
  this.contentBlockIndex.set(index, { type: "text" });
19618
20208
  return [];
19619
20209
  } else if (block.type === "tool_use") {
@@ -19634,7 +20224,12 @@ var AnthropicStreamConverter = class extends BaseStreamConverter {
19634
20224
  const delta = event.delta;
19635
20225
  const blockInfo = this.contentBlockIndex.get(index);
19636
20226
  if (!blockInfo) return [];
19637
- if (delta.type === "text_delta") {
20227
+ if (delta.type === "thinking_delta") {
20228
+ const thinkingDelta = delta;
20229
+ return [
20230
+ this.emitReasoningDelta(thinkingDelta.thinking || "", `thinking_${this.responseId}`)
20231
+ ];
20232
+ } else if (delta.type === "text_delta") {
19638
20233
  return [
19639
20234
  this.emitTextDelta(delta.text, {
19640
20235
  itemId: `msg_${this.responseId}`,
@@ -19654,6 +20249,9 @@ var AnthropicStreamConverter = class extends BaseStreamConverter {
19654
20249
  const index = event.index;
19655
20250
  const blockInfo = this.contentBlockIndex.get(index);
19656
20251
  if (!blockInfo) return [];
20252
+ if (blockInfo.type === "thinking") {
20253
+ return [this.emitReasoningDone(`thinking_${this.responseId}`)];
20254
+ }
19657
20255
  if (blockInfo.type === "tool_use") {
19658
20256
  return [this.emitToolCallArgsDone(blockInfo.id || "", blockInfo.name)];
19659
20257
  }
@@ -19752,6 +20350,16 @@ var AnthropicTextProvider = class extends BaseTextProvider {
19752
20350
  caps.supportsJSONSchema = false;
19753
20351
  return caps;
19754
20352
  }
20353
+ /**
20354
+ * List available models from the Anthropic API
20355
+ */
20356
+ async listModels() {
20357
+ const models = [];
20358
+ for await (const model of this.client.models.list()) {
20359
+ models.push(model.id);
20360
+ }
20361
+ return models.sort();
20362
+ }
19755
20363
  /**
19756
20364
  * Handle Anthropic-specific errors
19757
20365
  */
@@ -19930,6 +20538,11 @@ var GoogleConverter = class {
19930
20538
  request.generationConfig.thinkingConfig = {
19931
20539
  thinkingLevel: options.vendorOptions.thinkingLevel
19932
20540
  };
20541
+ } else if (options.thinking?.enabled) {
20542
+ validateThinkingConfig(options.thinking);
20543
+ request.generationConfig.thinkingConfig = {
20544
+ thinkingBudget: options.thinking.budgetTokens || 8192
20545
+ };
19933
20546
  }
19934
20547
  if (tools && tools.length > 0) {
19935
20548
  request.generationConfig.allowCodeExecution = false;
@@ -20145,7 +20758,13 @@ var GoogleConverter = class {
20145
20758
  convertGeminiPartsToContent(parts) {
20146
20759
  const content = [];
20147
20760
  for (const part of parts) {
20148
- if ("text" in part && part.text) {
20761
+ if ("thought" in part && part.thought === true && "text" in part && part.text) {
20762
+ content.push({
20763
+ type: "thinking" /* THINKING */,
20764
+ thinking: part.text,
20765
+ persistInHistory: false
20766
+ });
20767
+ } else if ("text" in part && part.text) {
20149
20768
  content.push(createTextContent(part.text));
20150
20769
  } else if ("functionCall" in part && part.functionCall) {
20151
20770
  const toolId = generateToolCallId("google");
@@ -20223,6 +20842,8 @@ var GoogleStreamConverter = class {
20223
20842
  isFirst = true;
20224
20843
  toolCallBuffers = /* @__PURE__ */ new Map();
20225
20844
  hadToolCalls = false;
20845
+ reasoningBuffer = "";
20846
+ wasThinking = false;
20226
20847
  // External storage for thought signatures (shared with GoogleConverter)
20227
20848
  thoughtSignatureStorage = null;
20228
20849
  // External storage for tool call ID → name mapping (shared with GoogleConverter)
@@ -20250,6 +20871,8 @@ var GoogleStreamConverter = class {
20250
20871
  this.isFirst = true;
20251
20872
  this.toolCallBuffers.clear();
20252
20873
  this.hadToolCalls = false;
20874
+ this.reasoningBuffer = "";
20875
+ this.wasThinking = false;
20253
20876
  let lastUsage = {
20254
20877
  input_tokens: 0,
20255
20878
  output_tokens: 0,
@@ -20275,6 +20898,16 @@ var GoogleStreamConverter = class {
20275
20898
  yield event;
20276
20899
  }
20277
20900
  }
20901
+ if (this.wasThinking && this.reasoningBuffer) {
20902
+ yield {
20903
+ type: "response.reasoning.done" /* REASONING_DONE */,
20904
+ response_id: this.responseId,
20905
+ item_id: `thinking_${this.responseId}`,
20906
+ thinking: this.reasoningBuffer
20907
+ };
20908
+ this.reasoningBuffer = "";
20909
+ this.wasThinking = false;
20910
+ }
20278
20911
  if (this.toolCallBuffers.size > 0) {
20279
20912
  for (const [toolCallId, buffer] of this.toolCallBuffers) {
20280
20913
  yield {
@@ -20314,7 +20947,28 @@ var GoogleStreamConverter = class {
20314
20947
  const candidate = chunk.candidates?.[0];
20315
20948
  if (!candidate?.content?.parts) return events;
20316
20949
  for (const part of candidate.content.parts) {
20317
- if (part.text) {
20950
+ const isThought = "thought" in part && part.thought === true;
20951
+ if (isThought && part.text) {
20952
+ this.wasThinking = true;
20953
+ this.reasoningBuffer += part.text;
20954
+ events.push({
20955
+ type: "response.reasoning.delta" /* REASONING_DELTA */,
20956
+ response_id: this.responseId,
20957
+ item_id: `thinking_${this.responseId}`,
20958
+ delta: part.text,
20959
+ sequence_number: this.sequenceNumber++
20960
+ });
20961
+ } else if (part.text) {
20962
+ if (this.wasThinking) {
20963
+ this.wasThinking = false;
20964
+ events.push({
20965
+ type: "response.reasoning.done" /* REASONING_DONE */,
20966
+ response_id: this.responseId,
20967
+ item_id: `thinking_${this.responseId}`,
20968
+ thinking: this.reasoningBuffer
20969
+ });
20970
+ this.reasoningBuffer = "";
20971
+ }
20318
20972
  events.push({
20319
20973
  type: "response.output_text.delta" /* OUTPUT_TEXT_DELTA */,
20320
20974
  response_id: this.responseId,
@@ -20413,6 +21067,8 @@ var GoogleStreamConverter = class {
20413
21067
  this.isFirst = true;
20414
21068
  this.toolCallBuffers.clear();
20415
21069
  this.hadToolCalls = false;
21070
+ this.reasoningBuffer = "";
21071
+ this.wasThinking = false;
20416
21072
  }
20417
21073
  /**
20418
21074
  * Reset converter state for a new stream
@@ -20542,6 +21198,18 @@ var GoogleTextProvider = class extends BaseTextProvider {
20542
21198
  maxOutputTokens: 65536
20543
21199
  });
20544
21200
  }
21201
+ /**
21202
+ * List available models from the Google Gemini API
21203
+ */
21204
+ async listModels() {
21205
+ const models = [];
21206
+ const pager = await this.client.models.list();
21207
+ for await (const model of pager) {
21208
+ const name = model.name?.replace(/^models\//, "") ?? "";
21209
+ if (name) models.push(name);
21210
+ }
21211
+ return models.sort();
21212
+ }
20545
21213
  /**
20546
21214
  * Handle Google-specific errors
20547
21215
  */
@@ -20649,6 +21317,18 @@ var VertexAITextProvider = class extends BaseTextProvider {
20649
21317
  maxOutputTokens: 65536
20650
21318
  });
20651
21319
  }
21320
+ /**
21321
+ * List available models from the Vertex AI API
21322
+ */
21323
+ async listModels() {
21324
+ const models = [];
21325
+ const pager = await this.client.models.list();
21326
+ for await (const model of pager) {
21327
+ const name = model.name?.replace(/^models\//, "") ?? "";
21328
+ if (name) models.push(name);
21329
+ }
21330
+ return models.sort();
21331
+ }
20652
21332
  /**
20653
21333
  * Handle Vertex AI-specific errors
20654
21334
  */
@@ -20694,6 +21374,23 @@ var GenericOpenAIProvider = class extends OpenAITextProvider {
20694
21374
  };
20695
21375
  }
20696
21376
  }
21377
+ /**
21378
+ * Override API key validation for generic providers.
21379
+ * Services like Ollama don't require authentication, so accept any key including mock/placeholder keys.
21380
+ */
21381
+ validateApiKey() {
21382
+ return { isValid: true };
21383
+ }
21384
+ /**
21385
+ * Override listModels for error safety — some OpenAI-compatible APIs may not support /v1/models
21386
+ */
21387
+ async listModels() {
21388
+ try {
21389
+ return await super.listModels();
21390
+ } catch {
21391
+ return [];
21392
+ }
21393
+ }
20697
21394
  /**
20698
21395
  * Override model capabilities for generic providers (registry-driven with conservative defaults)
20699
21396
  */
@@ -20891,7 +21588,7 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
20891
21588
  model: config.model,
20892
21589
  agentId: config.name,
20893
21590
  userId: config.userId,
20894
- connectors: config.connectors,
21591
+ identities: config.identities,
20895
21592
  // Include storage and sessionId if session config is provided
20896
21593
  storage: config.session?.storage,
20897
21594
  // Thread tool execution timeout to ToolManager
@@ -21061,16 +21758,16 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
21061
21758
  this._agentContext.userId = value;
21062
21759
  }
21063
21760
  /**
21064
- * Get the allowed connector names (undefined = all visible connectors).
21761
+ * Get the auth identities this agent is scoped to (undefined = all visible connectors).
21065
21762
  */
21066
- get connectors() {
21067
- return this._agentContext.connectors;
21763
+ get identities() {
21764
+ return this._agentContext.identities;
21068
21765
  }
21069
21766
  /**
21070
- * Restrict this agent to a subset of connectors. Updates ToolContext.connectorRegistry.
21767
+ * Set auth identities at runtime. Updates ToolContext.connectorRegistry and tool descriptions.
21071
21768
  */
21072
- set connectors(value) {
21073
- this._agentContext.connectors = value;
21769
+ set identities(value) {
21770
+ this._agentContext.identities = value;
21074
21771
  }
21075
21772
  /**
21076
21773
  * Permission management. Returns ToolPermissionManager for approval control.
@@ -21082,9 +21779,12 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
21082
21779
  /**
21083
21780
  * Add a tool to the agent.
21084
21781
  * Tools are registered with AgentContext (single source of truth).
21782
+ *
21783
+ * @param tool - The tool function to register
21784
+ * @param options - Optional registration options (namespace, source, priority, etc.)
21085
21785
  */
21086
- addTool(tool) {
21087
- this._agentContext.tools.register(tool);
21786
+ addTool(tool, options) {
21787
+ this._agentContext.tools.register(tool, options);
21088
21788
  if (tool.permission) {
21089
21789
  this._permissionManager.setToolConfig(tool.definition.function.name, tool.permission);
21090
21790
  }
@@ -21124,7 +21824,34 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
21124
21824
  */
21125
21825
  getEnabledToolDefinitions() {
21126
21826
  const toolContext = this._agentContext.tools.getToolContext();
21127
- return this._agentContext.tools.getEnabled().map((tool) => {
21827
+ const identities = this._agentContext.identities;
21828
+ let allowedSources;
21829
+ let allowedConnectorNames;
21830
+ if (identities) {
21831
+ allowedSources = /* @__PURE__ */ new Set();
21832
+ allowedConnectorNames = /* @__PURE__ */ new Set();
21833
+ for (const id of identities) {
21834
+ allowedConnectorNames.add(id.connector);
21835
+ if (id.accountId) {
21836
+ allowedSources.add(`connector:${id.connector}:${id.accountId}`);
21837
+ } else {
21838
+ allowedSources.add(`connector:${id.connector}`);
21839
+ allowedConnectorNames.add(id.connector);
21840
+ }
21841
+ }
21842
+ }
21843
+ return this._agentContext.tools.getEnabledRegistrations().filter((reg) => {
21844
+ if (!allowedSources) return true;
21845
+ if (!reg.source?.startsWith("connector:")) return true;
21846
+ if (allowedSources.has(reg.source)) return true;
21847
+ const sourceParts = reg.source.slice("connector:".length).split(":");
21848
+ const connectorName = sourceParts[0];
21849
+ if (allowedConnectorNames.has(connectorName) && allowedSources.has(`connector:${connectorName}`)) {
21850
+ return true;
21851
+ }
21852
+ return false;
21853
+ }).map((reg) => {
21854
+ const tool = reg.tool;
21128
21855
  if (tool.descriptionFactory) {
21129
21856
  const dynamicDescription = tool.descriptionFactory(toolContext);
21130
21857
  return {
@@ -21138,6 +21865,34 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
21138
21865
  return tool.definition;
21139
21866
  });
21140
21867
  }
21868
+ // ===== Model Discovery =====
21869
+ /**
21870
+ * List available models from the provider's API.
21871
+ * Useful for discovering models dynamically (e.g., Ollama local models).
21872
+ */
21873
+ async listModels() {
21874
+ return this._provider.listModels();
21875
+ }
21876
+ // ===== Snapshot / Inspection =====
21877
+ /**
21878
+ * Get a complete, serializable snapshot of the agent's context state.
21879
+ *
21880
+ * Convenience method that auto-wires tool usage stats from ToolManager.
21881
+ * Used by UI "Look Inside" panels.
21882
+ */
21883
+ async getSnapshot() {
21884
+ const stats = this._agentContext.tools.getStats();
21885
+ return this._agentContext.getSnapshot({ mostUsed: stats.mostUsed });
21886
+ }
21887
+ /**
21888
+ * Get a human-readable breakdown of the prepared context.
21889
+ *
21890
+ * Convenience method that delegates to AgentContextNextGen.
21891
+ * Used by "View Full Context" UI panels.
21892
+ */
21893
+ async getViewContext() {
21894
+ return this._agentContext.getViewContext();
21895
+ }
21141
21896
  // ===== Direct LLM Access (Bypasses AgentContext) =====
21142
21897
  /**
21143
21898
  * Get the provider for LLM calls.
@@ -21901,6 +22656,8 @@ var StreamState = class {
21901
22656
  createdAt;
21902
22657
  // Text accumulation: item_id -> text chunks
21903
22658
  textBuffers;
22659
+ // Reasoning accumulation: item_id -> reasoning chunks
22660
+ reasoningBuffers;
21904
22661
  // Tool call accumulation: tool_call_id -> buffer
21905
22662
  toolCallBuffers;
21906
22663
  // Completed tool calls
@@ -21922,6 +22679,7 @@ var StreamState = class {
21922
22679
  this.model = model;
21923
22680
  this.createdAt = createdAt || Date.now();
21924
22681
  this.textBuffers = /* @__PURE__ */ new Map();
22682
+ this.reasoningBuffers = /* @__PURE__ */ new Map();
21925
22683
  this.toolCallBuffers = /* @__PURE__ */ new Map();
21926
22684
  this.completedToolCalls = [];
21927
22685
  this.toolResults = /* @__PURE__ */ new Map();
@@ -21965,6 +22723,39 @@ var StreamState = class {
21965
22723
  }
21966
22724
  return allText.join("");
21967
22725
  }
22726
+ /**
22727
+ * Accumulate reasoning delta for a specific item
22728
+ */
22729
+ accumulateReasoningDelta(itemId, delta) {
22730
+ if (!this.reasoningBuffers.has(itemId)) {
22731
+ this.reasoningBuffers.set(itemId, []);
22732
+ }
22733
+ this.reasoningBuffers.get(itemId).push(delta);
22734
+ this.totalChunks++;
22735
+ }
22736
+ /**
22737
+ * Get complete accumulated reasoning for an item
22738
+ */
22739
+ getCompleteReasoning(itemId) {
22740
+ const chunks = this.reasoningBuffers.get(itemId);
22741
+ return chunks ? chunks.join("") : "";
22742
+ }
22743
+ /**
22744
+ * Get all accumulated reasoning (all items concatenated)
22745
+ */
22746
+ getAllReasoning() {
22747
+ const allReasoning = [];
22748
+ for (const chunks of this.reasoningBuffers.values()) {
22749
+ allReasoning.push(chunks.join(""));
22750
+ }
22751
+ return allReasoning.join("");
22752
+ }
22753
+ /**
22754
+ * Check if stream has any accumulated reasoning
22755
+ */
22756
+ hasReasoning() {
22757
+ return this.reasoningBuffers.size > 0;
22758
+ }
21968
22759
  /**
21969
22760
  * Start accumulating tool call arguments
21970
22761
  */
@@ -22133,6 +22924,7 @@ var StreamState = class {
22133
22924
  */
22134
22925
  clear() {
22135
22926
  this.textBuffers.clear();
22927
+ this.reasoningBuffers.clear();
22136
22928
  this.toolCallBuffers.clear();
22137
22929
  this.completedToolCalls = [];
22138
22930
  this.toolResults.clear();
@@ -22146,6 +22938,7 @@ var StreamState = class {
22146
22938
  model: this.model,
22147
22939
  createdAt: this.createdAt,
22148
22940
  textBuffers: new Map(this.textBuffers),
22941
+ reasoningBuffers: new Map(this.reasoningBuffers),
22149
22942
  toolCallBuffers: new Map(this.toolCallBuffers),
22150
22943
  completedToolCalls: [...this.completedToolCalls],
22151
22944
  toolResults: new Map(this.toolResults),
@@ -22575,6 +23368,20 @@ var Agent = class _Agent extends BaseAgent {
22575
23368
  _addStreamingAssistantMessage(streamState, toolCalls) {
22576
23369
  const assistantText = streamState.getAllText();
22577
23370
  const assistantContent = [];
23371
+ if (streamState.hasReasoning()) {
23372
+ const reasoning = streamState.getAllReasoning();
23373
+ if (reasoning) {
23374
+ const isAnthropic = this.connector.vendor === Vendor.Anthropic;
23375
+ assistantContent.push({
23376
+ type: "thinking" /* THINKING */,
23377
+ thinking: reasoning,
23378
+ // Streaming doesn't carry Anthropic signatures, so signature is undefined here.
23379
+ // Non-streaming responses (via convertResponse) capture signatures correctly.
23380
+ signature: void 0,
23381
+ persistInHistory: isAnthropic
23382
+ });
23383
+ }
23384
+ }
22578
23385
  if (assistantText && assistantText.trim()) {
22579
23386
  assistantContent.push({
22580
23387
  type: "output_text" /* OUTPUT_TEXT */,
@@ -22813,6 +23620,7 @@ var Agent = class _Agent extends BaseAgent {
22813
23620
  tools: this.getEnabledToolDefinitions(),
22814
23621
  tool_choice: "auto",
22815
23622
  temperature: this._config.temperature,
23623
+ thinking: this._config.thinking,
22816
23624
  vendorOptions: this._config.vendorOptions
22817
23625
  };
22818
23626
  const beforeLLM = await this.hookManager.executeHooks("before:llm", {
@@ -22878,6 +23686,7 @@ var Agent = class _Agent extends BaseAgent {
22878
23686
  tools: this.getEnabledToolDefinitions(),
22879
23687
  tool_choice: "auto",
22880
23688
  temperature: this._config.temperature,
23689
+ thinking: this._config.thinking,
22881
23690
  vendorOptions: this._config.vendorOptions
22882
23691
  };
22883
23692
  await this.hookManager.executeHooks("before:llm", {
@@ -22895,7 +23704,9 @@ var Agent = class _Agent extends BaseAgent {
22895
23704
  });
22896
23705
  try {
22897
23706
  for await (const event of this._provider.streamGenerate(generateOptions)) {
22898
- if (event.type === "response.output_text.delta" /* OUTPUT_TEXT_DELTA */) {
23707
+ if (isReasoningDelta(event)) {
23708
+ streamState.accumulateReasoningDelta(event.item_id, event.delta);
23709
+ } else if (event.type === "response.output_text.delta" /* OUTPUT_TEXT_DELTA */) {
22899
23710
  streamState.accumulateTextDelta(event.item_id, event.delta);
22900
23711
  } else if (event.type === "response.tool_call.start" /* TOOL_CALL_START */) {
22901
23712
  streamState.startToolCall(event.tool_call_id, event.tool_name);
@@ -23425,6 +24236,7 @@ function createTask(input) {
23425
24236
  suggestedTools: input.suggestedTools,
23426
24237
  validation: input.validation,
23427
24238
  expectedOutput: input.expectedOutput,
24239
+ controlFlow: input.controlFlow,
23428
24240
  attempts: 0,
23429
24241
  maxAttempts: input.maxAttempts ?? 3,
23430
24242
  createdAt: now,
@@ -23677,6 +24489,7 @@ function createRoutineDefinition(input) {
23677
24489
  instructions: input.instructions,
23678
24490
  concurrency: input.concurrency,
23679
24491
  allowDynamicTasks: input.allowDynamicTasks ?? false,
24492
+ parameters: input.parameters,
23680
24493
  tags: input.tags,
23681
24494
  author: input.author,
23682
24495
  createdAt: now,
@@ -23892,6 +24705,321 @@ function extractNumber(text, patterns = [
23892
24705
 
23893
24706
  // src/core/routineRunner.ts
23894
24707
  init_Logger();
24708
+
24709
+ // src/core/routineControlFlow.ts
24710
+ init_Logger();
24711
+ var HARD_MAX_ITERATIONS = 1e3;
24712
+ var ICM_LARGE_THRESHOLD = 5e3;
24713
+ var ROUTINE_KEYS = {
24714
+ /** Plan overview with task statuses (ICM) */
24715
+ PLAN: "__routine_plan",
24716
+ /** Dependency results location guide (ICM) */
24717
+ DEPS: "__routine_deps",
24718
+ /** Prefix for per-dependency result keys (ICM/WM) */
24719
+ DEP_RESULT_PREFIX: "__dep_result_",
24720
+ /** Current map/fold item (ICM) */
24721
+ MAP_ITEM: "__map_item",
24722
+ /** Current map/fold index, 0-based (ICM) */
24723
+ MAP_INDEX: "__map_index",
24724
+ /** Total items in map/fold (ICM) */
24725
+ MAP_TOTAL: "__map_total",
24726
+ /** Running fold accumulator (ICM) */
24727
+ FOLD_ACCUMULATOR: "__fold_accumulator",
24728
+ /** Prefix for large dep results stored in WM findings tier */
24729
+ WM_DEP_FINDINGS_PREFIX: "findings/__dep_result_"
24730
+ };
24731
+ function resolveTemplates(text, inputs, icmPlugin) {
24732
+ return text.replace(/\{\{(\w+)\.(\w+)\}\}/g, (_match, namespace, key) => {
24733
+ let value;
24734
+ if (namespace === "param") {
24735
+ value = inputs[key];
24736
+ } else if (namespace === "map") {
24737
+ const icmKey = `__map_${key}`;
24738
+ value = icmPlugin?.get(icmKey);
24739
+ } else if (namespace === "fold") {
24740
+ const icmKey = `__fold_${key}`;
24741
+ value = icmPlugin?.get(icmKey);
24742
+ } else {
24743
+ return _match;
24744
+ }
24745
+ if (value === void 0) {
24746
+ return _match;
24747
+ }
24748
+ return typeof value === "string" ? value : JSON.stringify(value);
24749
+ });
24750
+ }
24751
+ function resolveTaskTemplates(task, inputs, icmPlugin) {
24752
+ const resolvedDescription = resolveTemplates(task.description, inputs, icmPlugin);
24753
+ const resolvedExpectedOutput = task.expectedOutput ? resolveTemplates(task.expectedOutput, inputs, icmPlugin) : task.expectedOutput;
24754
+ if (resolvedDescription === task.description && resolvedExpectedOutput === task.expectedOutput) {
24755
+ return task;
24756
+ }
24757
+ return {
24758
+ ...task,
24759
+ description: resolvedDescription,
24760
+ expectedOutput: resolvedExpectedOutput
24761
+ };
24762
+ }
24763
+ function validateAndResolveInputs(parameters, inputs) {
24764
+ const resolved = { ...inputs ?? {} };
24765
+ if (!parameters || parameters.length === 0) {
24766
+ return resolved;
24767
+ }
24768
+ for (const param of parameters) {
24769
+ if (resolved[param.name] === void 0) {
24770
+ if (param.required) {
24771
+ throw new Error(`Missing required parameter: "${param.name}"`);
24772
+ }
24773
+ if (param.default !== void 0) {
24774
+ resolved[param.name] = param.default;
24775
+ }
24776
+ }
24777
+ }
24778
+ return resolved;
24779
+ }
24780
+ async function readMemoryValue(key, icmPlugin, wmPlugin) {
24781
+ if (icmPlugin) {
24782
+ const icmValue = icmPlugin.get(key);
24783
+ if (icmValue !== void 0) return icmValue;
24784
+ }
24785
+ if (wmPlugin) {
24786
+ const wmValue = await wmPlugin.retrieve(key);
24787
+ if (wmValue !== void 0) return wmValue;
24788
+ }
24789
+ return void 0;
24790
+ }
24791
+ async function storeResult(key, description, value, icmPlugin, wmPlugin) {
24792
+ const serialized = typeof value === "string" ? value : JSON.stringify(value);
24793
+ const estimatedTokens = Math.ceil(serialized.length / 4);
24794
+ if (estimatedTokens < ICM_LARGE_THRESHOLD && icmPlugin) {
24795
+ icmPlugin.set(key, description, value, "high");
24796
+ } else if (wmPlugin) {
24797
+ await wmPlugin.store(key, description, serialized, { tier: "findings" });
24798
+ } else if (icmPlugin) {
24799
+ icmPlugin.set(key, description, value, "high");
24800
+ }
24801
+ }
24802
+ function resolveSubRoutine(spec, parentTaskName) {
24803
+ if (!Array.isArray(spec)) {
24804
+ return spec;
24805
+ }
24806
+ return createRoutineDefinition({
24807
+ name: `${parentTaskName} (sub-routine)`,
24808
+ description: `Sub-routine of ${parentTaskName}`,
24809
+ tasks: spec
24810
+ });
24811
+ }
24812
+ function getPlugins(agent) {
24813
+ const icmPlugin = agent.context.getPlugin("in_context_memory");
24814
+ const wmPlugin = agent.context.memory;
24815
+ return { icmPlugin, wmPlugin };
24816
+ }
24817
+ function cleanMapKeys(icmPlugin) {
24818
+ if (!icmPlugin) return;
24819
+ icmPlugin.delete(ROUTINE_KEYS.MAP_ITEM);
24820
+ icmPlugin.delete(ROUTINE_KEYS.MAP_INDEX);
24821
+ icmPlugin.delete(ROUTINE_KEYS.MAP_TOTAL);
24822
+ }
24823
+ function cleanFoldKeys(icmPlugin) {
24824
+ if (!icmPlugin) return;
24825
+ cleanMapKeys(icmPlugin);
24826
+ icmPlugin.delete(ROUTINE_KEYS.FOLD_ACCUMULATOR);
24827
+ }
24828
+ async function readSourceArray(flow, flowType, icmPlugin, wmPlugin) {
24829
+ const sourceValue = await readMemoryValue(flow.sourceKey, icmPlugin, wmPlugin);
24830
+ if (!Array.isArray(sourceValue)) {
24831
+ return {
24832
+ completed: false,
24833
+ error: `${flowType} sourceKey "${flow.sourceKey}" is not an array (got ${typeof sourceValue})`
24834
+ };
24835
+ }
24836
+ const maxIter = Math.min(sourceValue.length, flow.maxIterations ?? sourceValue.length, HARD_MAX_ITERATIONS);
24837
+ return { array: sourceValue, maxIter };
24838
+ }
24839
+ function prepareSubRoutine(tasks, parentTaskName) {
24840
+ const subRoutine = resolveSubRoutine(tasks, parentTaskName);
24841
+ return {
24842
+ augmented: { ...subRoutine },
24843
+ baseInstructions: subRoutine.instructions ?? ""
24844
+ };
24845
+ }
24846
+ function getSubRoutineOutput(execution) {
24847
+ const tasks = execution.plan.tasks;
24848
+ for (let i = tasks.length - 1; i >= 0; i--) {
24849
+ if (tasks[i].status === "completed") {
24850
+ return tasks[i].result?.output ?? null;
24851
+ }
24852
+ }
24853
+ return null;
24854
+ }
24855
+ function setIterationKeys(icmPlugin, item, index, total, label) {
24856
+ if (!icmPlugin) return;
24857
+ icmPlugin.set(ROUTINE_KEYS.MAP_ITEM, `Current ${label} item (${index + 1}/${total})`, item, "high");
24858
+ icmPlugin.set(ROUTINE_KEYS.MAP_INDEX, `Current ${label} index (0-based)`, index, "high");
24859
+ icmPlugin.set(ROUTINE_KEYS.MAP_TOTAL, `Total items in ${label}`, total, "high");
24860
+ }
24861
+ async function withTimeout(promise, timeoutMs, label) {
24862
+ if (!timeoutMs) return promise;
24863
+ let timer;
24864
+ const timeout = new Promise((_, reject) => {
24865
+ timer = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
24866
+ });
24867
+ try {
24868
+ return await Promise.race([promise, timeout]);
24869
+ } finally {
24870
+ clearTimeout(timer);
24871
+ }
24872
+ }
24873
+ async function handleMap(agent, flow, task, inputs) {
24874
+ const { icmPlugin, wmPlugin } = getPlugins(agent);
24875
+ const log = exports.logger.child({ controlFlow: "map", task: task.name });
24876
+ const sourceResult = await readSourceArray(flow, "Map", icmPlugin, wmPlugin);
24877
+ if ("completed" in sourceResult) return sourceResult;
24878
+ const { array: array3, maxIter } = sourceResult;
24879
+ const results = [];
24880
+ const { augmented, baseInstructions } = prepareSubRoutine(flow.tasks, task.name);
24881
+ log.info({ arrayLength: array3.length, maxIterations: maxIter }, "Starting map iteration");
24882
+ try {
24883
+ for (let i = 0; i < maxIter; i++) {
24884
+ setIterationKeys(icmPlugin, array3[i], i, array3.length, "map");
24885
+ augmented.instructions = [
24886
+ `You are processing item ${i + 1} of ${array3.length} in a map operation.`,
24887
+ "The current item is available in your live context as __map_item.",
24888
+ "Current index (0-based) is in __map_index, total count in __map_total.",
24889
+ "",
24890
+ baseInstructions
24891
+ ].join("\n");
24892
+ const subExecution = await withTimeout(
24893
+ executeRoutine({ definition: augmented, agent, inputs }),
24894
+ flow.iterationTimeoutMs,
24895
+ `Map iteration ${i}`
24896
+ );
24897
+ if (subExecution.status !== "completed") {
24898
+ return {
24899
+ completed: false,
24900
+ error: `Map iteration ${i} failed: ${subExecution.error ?? "sub-routine failed"}`
24901
+ };
24902
+ }
24903
+ results.push(getSubRoutineOutput(subExecution));
24904
+ }
24905
+ } finally {
24906
+ cleanMapKeys(icmPlugin);
24907
+ }
24908
+ if (flow.resultKey) {
24909
+ await storeResult(flow.resultKey, `Map results from "${task.name}"`, results, icmPlugin, wmPlugin);
24910
+ }
24911
+ log.info({ resultCount: results.length }, "Map completed");
24912
+ return { completed: true, result: results };
24913
+ }
24914
+ async function handleFold(agent, flow, task, inputs) {
24915
+ const { icmPlugin, wmPlugin } = getPlugins(agent);
24916
+ const log = exports.logger.child({ controlFlow: "fold", task: task.name });
24917
+ const sourceResult = await readSourceArray(flow, "Fold", icmPlugin, wmPlugin);
24918
+ if ("completed" in sourceResult) return sourceResult;
24919
+ const { array: array3, maxIter } = sourceResult;
24920
+ let accumulator = flow.initialValue;
24921
+ const { augmented, baseInstructions } = prepareSubRoutine(flow.tasks, task.name);
24922
+ log.info({ arrayLength: array3.length, maxIterations: maxIter }, "Starting fold iteration");
24923
+ try {
24924
+ for (let i = 0; i < maxIter; i++) {
24925
+ setIterationKeys(icmPlugin, array3[i], i, array3.length, "fold");
24926
+ if (icmPlugin) {
24927
+ icmPlugin.set(ROUTINE_KEYS.FOLD_ACCUMULATOR, "Running accumulator \u2014 update via context_set", accumulator, "high");
24928
+ }
24929
+ augmented.instructions = [
24930
+ `You are processing item ${i + 1} of ${array3.length} in a fold/accumulate operation.`,
24931
+ "The current item is in __map_item. The running accumulator is in __fold_accumulator.",
24932
+ "After processing, use context_set to update __fold_accumulator with the new accumulated value.",
24933
+ "Your final text response will also be captured as the result.",
24934
+ "",
24935
+ baseInstructions
24936
+ ].join("\n");
24937
+ const subExecution = await withTimeout(
24938
+ executeRoutine({ definition: augmented, agent, inputs }),
24939
+ flow.iterationTimeoutMs,
24940
+ `Fold iteration ${i}`
24941
+ );
24942
+ if (subExecution.status !== "completed") {
24943
+ return {
24944
+ completed: false,
24945
+ error: `Fold iteration ${i} failed: ${subExecution.error ?? "sub-routine failed"}`
24946
+ };
24947
+ }
24948
+ const taskOutput = getSubRoutineOutput(subExecution);
24949
+ if (taskOutput !== null) {
24950
+ accumulator = taskOutput;
24951
+ } else if (icmPlugin) {
24952
+ const icmAccumulator = icmPlugin.get(ROUTINE_KEYS.FOLD_ACCUMULATOR);
24953
+ if (icmAccumulator !== void 0) {
24954
+ accumulator = icmAccumulator;
24955
+ }
24956
+ }
24957
+ }
24958
+ } finally {
24959
+ cleanFoldKeys(icmPlugin);
24960
+ }
24961
+ await storeResult(flow.resultKey, `Fold result from "${task.name}"`, accumulator, icmPlugin, wmPlugin);
24962
+ log.info("Fold completed");
24963
+ return { completed: true, result: accumulator };
24964
+ }
24965
+ async function handleUntil(agent, flow, task, inputs) {
24966
+ const { icmPlugin, wmPlugin } = getPlugins(agent);
24967
+ const log = exports.logger.child({ controlFlow: "until", task: task.name });
24968
+ const { augmented, baseInstructions } = prepareSubRoutine(flow.tasks, task.name);
24969
+ log.info({ maxIterations: flow.maxIterations }, "Starting until loop");
24970
+ const memoryAccess = {
24971
+ get: (key) => readMemoryValue(key, icmPlugin, wmPlugin)
24972
+ };
24973
+ try {
24974
+ for (let i = 0; i < flow.maxIterations; i++) {
24975
+ if (flow.iterationKey && icmPlugin) {
24976
+ icmPlugin.set(flow.iterationKey, "Current iteration index", i, "high");
24977
+ }
24978
+ augmented.instructions = [
24979
+ `You are in iteration ${i + 1} of a repeating operation (max ${flow.maxIterations}).`,
24980
+ "Complete the task. The loop will continue until its exit condition is met.",
24981
+ "",
24982
+ baseInstructions
24983
+ ].join("\n");
24984
+ const subExecution = await withTimeout(
24985
+ executeRoutine({ definition: augmented, agent, inputs }),
24986
+ flow.iterationTimeoutMs,
24987
+ `Until iteration ${i}`
24988
+ );
24989
+ if (subExecution.status !== "completed") {
24990
+ return {
24991
+ completed: false,
24992
+ error: `Until iteration ${i} failed: ${subExecution.error ?? "sub-routine failed"}`
24993
+ };
24994
+ }
24995
+ const conditionMet = await evaluateCondition(flow.condition, memoryAccess);
24996
+ if (conditionMet) {
24997
+ log.info({ iteration: i + 1 }, "Until condition met");
24998
+ return { completed: true };
24999
+ }
25000
+ }
25001
+ } finally {
25002
+ if (flow.iterationKey && icmPlugin) {
25003
+ icmPlugin.delete(flow.iterationKey);
25004
+ }
25005
+ }
25006
+ return { completed: false, error: `Until loop: maxIterations (${flow.maxIterations}) exceeded` };
25007
+ }
25008
+ async function executeControlFlow(agent, task, inputs) {
25009
+ const flow = task.controlFlow;
25010
+ switch (flow.type) {
25011
+ case "map":
25012
+ return handleMap(agent, flow, task, inputs);
25013
+ case "fold":
25014
+ return handleFold(agent, flow, task, inputs);
25015
+ case "until":
25016
+ return handleUntil(agent, flow, task, inputs);
25017
+ default:
25018
+ return { completed: false, error: `Unknown control flow type: ${flow.type}` };
25019
+ }
25020
+ }
25021
+
25022
+ // src/core/routineRunner.ts
23895
25023
  function defaultSystemPrompt(definition) {
23896
25024
  const parts = [];
23897
25025
  if (definition.instructions) {
@@ -24020,6 +25148,14 @@ async function collectValidationContext(agent, responseText) {
24020
25148
  toolCallLog
24021
25149
  };
24022
25150
  }
25151
+ function isTransientError(error) {
25152
+ if (error instanceof ProviderAuthError) return false;
25153
+ if (error instanceof ProviderContextLengthError) return false;
25154
+ if (error instanceof ProviderNotFoundError) return false;
25155
+ if (error instanceof ModelNotSupportedError) return false;
25156
+ if (error instanceof InvalidConfigError) return false;
25157
+ return true;
25158
+ }
24023
25159
  function estimateTokens(text) {
24024
25160
  return Math.ceil(text.length / 4);
24025
25161
  }
@@ -24063,32 +25199,42 @@ function buildPlanOverview(execution, definition, currentTaskId) {
24063
25199
  }
24064
25200
  return parts.join("\n");
24065
25201
  }
24066
- async function injectRoutineContext(agent, execution, definition, currentTask) {
24067
- const icmPlugin = agent.context.getPlugin("in_context_memory");
24068
- const wmPlugin = agent.context.memory;
24069
- if (!icmPlugin && !wmPlugin) {
24070
- exports.logger.warn("injectRoutineContext: No ICM or WM plugin available \u2014 skipping context injection");
24071
- return;
24072
- }
24073
- const planOverview = buildPlanOverview(execution, definition, currentTask.id);
24074
- if (icmPlugin) {
24075
- icmPlugin.set("__routine_plan", "Routine plan overview with task statuses", planOverview, "high");
24076
- }
25202
+ async function cleanupMemoryKeys(icmPlugin, wmPlugin, config) {
24077
25203
  if (icmPlugin) {
24078
25204
  for (const entry of icmPlugin.list()) {
24079
- if (entry.key.startsWith("__dep_result_") || entry.key === "__routine_deps") {
24080
- icmPlugin.delete(entry.key);
24081
- }
25205
+ const shouldDelete = config.icmPrefixes.some((p) => entry.key.startsWith(p)) || (config.icmExactKeys?.includes(entry.key) ?? false);
25206
+ if (shouldDelete) icmPlugin.delete(entry.key);
24082
25207
  }
24083
25208
  }
24084
25209
  if (wmPlugin) {
24085
25210
  const { entries: wmEntries } = await wmPlugin.query();
24086
25211
  for (const entry of wmEntries) {
24087
- if (entry.key.startsWith("__dep_result_") || entry.key.startsWith("findings/__dep_result_")) {
25212
+ if (config.wmPrefixes.some((p) => entry.key.startsWith(p))) {
24088
25213
  await wmPlugin.delete(entry.key);
24089
25214
  }
24090
25215
  }
24091
25216
  }
25217
+ }
25218
+ var DEP_CLEANUP_CONFIG = {
25219
+ icmPrefixes: [ROUTINE_KEYS.DEP_RESULT_PREFIX],
25220
+ icmExactKeys: [ROUTINE_KEYS.DEPS],
25221
+ wmPrefixes: [ROUTINE_KEYS.DEP_RESULT_PREFIX, ROUTINE_KEYS.WM_DEP_FINDINGS_PREFIX]
25222
+ };
25223
+ var FULL_CLEANUP_CONFIG = {
25224
+ icmPrefixes: ["__routine_", ROUTINE_KEYS.DEP_RESULT_PREFIX, "__map_", "__fold_"],
25225
+ wmPrefixes: [ROUTINE_KEYS.DEP_RESULT_PREFIX, ROUTINE_KEYS.WM_DEP_FINDINGS_PREFIX]
25226
+ };
25227
+ async function injectRoutineContext(agent, execution, definition, currentTask) {
25228
+ const { icmPlugin, wmPlugin } = getPlugins(agent);
25229
+ if (!icmPlugin && !wmPlugin) {
25230
+ exports.logger.warn("injectRoutineContext: No ICM or WM plugin available \u2014 skipping context injection");
25231
+ return;
25232
+ }
25233
+ const planOverview = buildPlanOverview(execution, definition, currentTask.id);
25234
+ if (icmPlugin) {
25235
+ icmPlugin.set(ROUTINE_KEYS.PLAN, "Routine plan overview with task statuses", planOverview, "high");
25236
+ }
25237
+ await cleanupMemoryKeys(icmPlugin, wmPlugin, DEP_CLEANUP_CONFIG);
24092
25238
  if (currentTask.dependsOn.length === 0) return;
24093
25239
  const inContextDeps = [];
24094
25240
  const workingMemoryDeps = [];
@@ -24097,7 +25243,7 @@ async function injectRoutineContext(agent, execution, definition, currentTask) {
24097
25243
  if (!depTask?.result?.output) continue;
24098
25244
  const output = typeof depTask.result.output === "string" ? depTask.result.output : JSON.stringify(depTask.result.output);
24099
25245
  const tokens = estimateTokens(output);
24100
- const depKey = `__dep_result_${depId}`;
25246
+ const depKey = `${ROUTINE_KEYS.DEP_RESULT_PREFIX}${depId}`;
24101
25247
  const depLabel = `Result from task "${depTask.name}"`;
24102
25248
  if (tokens < 5e3 && icmPlugin) {
24103
25249
  icmPlugin.set(depKey, depLabel, output, "high");
@@ -24119,27 +25265,12 @@ async function injectRoutineContext(agent, execution, definition, currentTask) {
24119
25265
  if (workingMemoryDeps.length > 0) {
24120
25266
  summaryParts.push(`In working memory (use memory_retrieve): ${workingMemoryDeps.join(", ")}`);
24121
25267
  }
24122
- icmPlugin.set("__routine_deps", "Dependency results location guide", summaryParts.join("\n"), "high");
25268
+ icmPlugin.set(ROUTINE_KEYS.DEPS, "Dependency results location guide", summaryParts.join("\n"), "high");
24123
25269
  }
24124
25270
  }
24125
25271
  async function cleanupRoutineContext(agent) {
24126
- const icmPlugin = agent.context.getPlugin("in_context_memory");
24127
- const wmPlugin = agent.context.memory;
24128
- if (icmPlugin) {
24129
- for (const entry of icmPlugin.list()) {
24130
- if (entry.key.startsWith("__routine_") || entry.key.startsWith("__dep_result_")) {
24131
- icmPlugin.delete(entry.key);
24132
- }
24133
- }
24134
- }
24135
- if (wmPlugin) {
24136
- const { entries: wmEntries } = await wmPlugin.query();
24137
- for (const entry of wmEntries) {
24138
- if (entry.key.startsWith("__dep_result_") || entry.key.startsWith("findings/__dep_result_")) {
24139
- await wmPlugin.delete(entry.key);
24140
- }
24141
- }
24142
- }
25272
+ const { icmPlugin, wmPlugin } = getPlugins(agent);
25273
+ await cleanupMemoryKeys(icmPlugin, wmPlugin, FULL_CLEANUP_CONFIG);
24143
25274
  }
24144
25275
  async function validateTaskCompletion(agent, task, responseText, validationPromptBuilder) {
24145
25276
  const hasExplicitValidation = task.validation?.skipReflection === false && task.validation?.completionCriteria && task.validation.completionCriteria.length > 0;
@@ -24188,11 +25319,13 @@ async function executeRoutine(options) {
24188
25319
  onTaskFailed,
24189
25320
  onTaskValidation,
24190
25321
  hooks,
24191
- prompts
25322
+ prompts,
25323
+ inputs: rawInputs
24192
25324
  } = options;
24193
25325
  if (!existingAgent && (!connector || !model)) {
24194
25326
  throw new Error("executeRoutine requires either `agent` or both `connector` and `model`");
24195
25327
  }
25328
+ const resolvedInputs = validateAndResolveInputs(definition.parameters, rawInputs);
24196
25329
  const ownsAgent = !existingAgent;
24197
25330
  const log = exports.logger.child({ routine: definition.name });
24198
25331
  const execution = createRoutineExecution(definition);
@@ -24277,7 +25410,7 @@ async function executeRoutine(options) {
24277
25410
  execution.lastUpdatedAt = Date.now();
24278
25411
  onTaskStarted?.(execution.plan.tasks[taskIndex], execution);
24279
25412
  let taskCompleted = false;
24280
- const maxTaskIterations = task.execution?.maxIterations ?? 15;
25413
+ const maxTaskIterations = task.execution?.maxIterations ?? 50;
24281
25414
  const iterationLimiter = async (ctx) => {
24282
25415
  if (ctx.iteration >= maxTaskIterations) {
24283
25416
  agent.cancel(`Task "${task.name}" exceeded max iterations (${maxTaskIterations})`);
@@ -24285,85 +25418,115 @@ async function executeRoutine(options) {
24285
25418
  return { shouldPause: false };
24286
25419
  };
24287
25420
  agent.registerHook("pause:check", iterationLimiter);
24288
- const getTask = () => execution.plan.tasks[taskIndex];
24289
- await injectRoutineContext(agent, execution, definition, getTask());
24290
- while (!taskCompleted) {
24291
- try {
24292
- const taskPrompt = buildTaskPrompt(getTask());
24293
- const response = await agent.run(taskPrompt);
24294
- const responseText = response.output_text ?? "";
24295
- const validationResult = await validateTaskCompletion(
24296
- agent,
24297
- getTask(),
24298
- responseText,
24299
- buildValidationPrompt
24300
- );
24301
- onTaskValidation?.(getTask(), validationResult, execution);
24302
- if (validationResult.isComplete) {
24303
- execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "completed");
24304
- execution.plan.tasks[taskIndex].result = {
24305
- success: true,
24306
- output: responseText,
24307
- validationScore: validationResult.completionScore,
24308
- validationExplanation: validationResult.explanation
24309
- };
24310
- taskCompleted = true;
24311
- log.info(
24312
- { taskName: getTask().name, score: validationResult.completionScore },
24313
- "Task completed"
24314
- );
24315
- execution.progress = getRoutineProgress(execution);
24316
- execution.lastUpdatedAt = Date.now();
24317
- onTaskComplete?.(execution.plan.tasks[taskIndex], execution);
24318
- } else {
24319
- log.warn(
24320
- {
24321
- taskName: getTask().name,
24322
- score: validationResult.completionScore,
24323
- attempt: getTask().attempts,
24324
- maxAttempts: getTask().maxAttempts
24325
- },
24326
- "Task validation failed"
24327
- );
24328
- if (getTask().attempts >= getTask().maxAttempts) {
25421
+ try {
25422
+ const getTask = () => execution.plan.tasks[taskIndex];
25423
+ await injectRoutineContext(agent, execution, definition, getTask());
25424
+ const { icmPlugin } = getPlugins(agent);
25425
+ if (getTask().controlFlow) {
25426
+ try {
25427
+ const cfResult = await executeControlFlow(agent, getTask(), resolvedInputs);
25428
+ if (cfResult.completed) {
25429
+ execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "completed");
25430
+ execution.plan.tasks[taskIndex].result = { success: true, output: cfResult.result };
25431
+ taskCompleted = true;
25432
+ execution.progress = getRoutineProgress(execution);
25433
+ execution.lastUpdatedAt = Date.now();
25434
+ onTaskComplete?.(execution.plan.tasks[taskIndex], execution);
25435
+ } else {
24329
25436
  execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "failed");
24330
- execution.plan.tasks[taskIndex].result = {
24331
- success: false,
24332
- error: validationResult.explanation,
24333
- validationScore: validationResult.completionScore,
24334
- validationExplanation: validationResult.explanation
24335
- };
24336
- break;
25437
+ execution.plan.tasks[taskIndex].result = { success: false, error: cfResult.error };
24337
25438
  }
24338
- execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "in_progress");
24339
- }
24340
- } catch (error) {
24341
- const errorMessage = error.message;
24342
- log.error({ taskName: getTask().name, error: errorMessage }, "Task execution error");
24343
- if (getTask().attempts >= getTask().maxAttempts) {
25439
+ } catch (error) {
25440
+ const errorMessage = error.message;
25441
+ log.error({ taskName: getTask().name, error: errorMessage }, "Control flow error");
24344
25442
  execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "failed");
24345
- execution.plan.tasks[taskIndex].result = {
24346
- success: false,
24347
- error: errorMessage
24348
- };
24349
- break;
25443
+ execution.plan.tasks[taskIndex].result = { success: false, error: errorMessage };
25444
+ }
25445
+ } else {
25446
+ while (!taskCompleted) {
25447
+ try {
25448
+ const resolvedTask = resolveTaskTemplates(getTask(), resolvedInputs, icmPlugin);
25449
+ const taskPrompt = buildTaskPrompt(resolvedTask);
25450
+ const response = await agent.run(taskPrompt);
25451
+ const responseText = response.output_text ?? "";
25452
+ const validationResult = await validateTaskCompletion(
25453
+ agent,
25454
+ getTask(),
25455
+ responseText,
25456
+ buildValidationPrompt
25457
+ );
25458
+ onTaskValidation?.(getTask(), validationResult, execution);
25459
+ if (validationResult.isComplete) {
25460
+ execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "completed");
25461
+ execution.plan.tasks[taskIndex].result = {
25462
+ success: true,
25463
+ output: responseText,
25464
+ validationScore: validationResult.completionScore,
25465
+ validationExplanation: validationResult.explanation
25466
+ };
25467
+ taskCompleted = true;
25468
+ log.info(
25469
+ { taskName: getTask().name, score: validationResult.completionScore },
25470
+ "Task completed"
25471
+ );
25472
+ execution.progress = getRoutineProgress(execution);
25473
+ execution.lastUpdatedAt = Date.now();
25474
+ onTaskComplete?.(execution.plan.tasks[taskIndex], execution);
25475
+ } else {
25476
+ log.warn(
25477
+ {
25478
+ taskName: getTask().name,
25479
+ score: validationResult.completionScore,
25480
+ attempt: getTask().attempts,
25481
+ maxAttempts: getTask().maxAttempts
25482
+ },
25483
+ "Task validation failed"
25484
+ );
25485
+ if (getTask().attempts >= getTask().maxAttempts) {
25486
+ execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "failed");
25487
+ execution.plan.tasks[taskIndex].result = {
25488
+ success: false,
25489
+ error: validationResult.explanation,
25490
+ validationScore: validationResult.completionScore,
25491
+ validationExplanation: validationResult.explanation
25492
+ };
25493
+ break;
25494
+ }
25495
+ execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "in_progress");
25496
+ }
25497
+ } catch (error) {
25498
+ const errorMessage = error.message;
25499
+ log.error({ taskName: getTask().name, error: errorMessage }, "Task execution error");
25500
+ if (!isTransientError(error) || getTask().attempts >= getTask().maxAttempts) {
25501
+ execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "failed");
25502
+ execution.plan.tasks[taskIndex].result = {
25503
+ success: false,
25504
+ error: errorMessage
25505
+ };
25506
+ break;
25507
+ }
25508
+ execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "in_progress");
25509
+ }
24350
25510
  }
24351
- execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "in_progress");
24352
25511
  }
24353
- }
24354
- if (!taskCompleted) {
24355
- execution.progress = getRoutineProgress(execution);
24356
- execution.lastUpdatedAt = Date.now();
24357
- onTaskFailed?.(execution.plan.tasks[taskIndex], execution);
24358
- if (failureMode === "fail-fast") {
24359
- execution.status = "failed";
24360
- execution.error = `Task "${getTask().name}" failed after ${getTask().attempts} attempt(s)`;
24361
- execution.completedAt = Date.now();
25512
+ if (!taskCompleted) {
25513
+ execution.progress = getRoutineProgress(execution);
24362
25514
  execution.lastUpdatedAt = Date.now();
24363
- break;
25515
+ onTaskFailed?.(execution.plan.tasks[taskIndex], execution);
25516
+ if (failureMode === "fail-fast") {
25517
+ execution.status = "failed";
25518
+ execution.error = `Task "${getTask().name}" failed after ${getTask().attempts} attempt(s)`;
25519
+ execution.completedAt = Date.now();
25520
+ execution.lastUpdatedAt = Date.now();
25521
+ break;
25522
+ }
25523
+ }
25524
+ } finally {
25525
+ try {
25526
+ agent.unregisterHook("pause:check", iterationLimiter);
25527
+ } catch {
24364
25528
  }
24365
25529
  }
24366
- agent.unregisterHook("pause:check", iterationLimiter);
24367
25530
  agent.clearConversation("task-boundary");
24368
25531
  nextTasks = getNextExecutableTasks(execution.plan);
24369
25532
  }
@@ -24391,16 +25554,22 @@ async function executeRoutine(options) {
24391
25554
  } finally {
24392
25555
  try {
24393
25556
  await cleanupRoutineContext(agent);
24394
- } catch {
25557
+ } catch (e) {
25558
+ log.debug({ error: e.message }, "Failed to clean up routine context");
24395
25559
  }
24396
25560
  for (const { name, hook } of registeredHooks) {
24397
25561
  try {
24398
25562
  agent.unregisterHook(name, hook);
24399
- } catch {
25563
+ } catch (e) {
25564
+ log.debug({ hookName: name, error: e.message }, "Failed to unregister hook");
24400
25565
  }
24401
25566
  }
24402
25567
  if (ownsAgent) {
24403
- agent.destroy();
25568
+ try {
25569
+ agent.destroy();
25570
+ } catch (e) {
25571
+ log.debug({ error: e.message }, "Failed to destroy agent");
25572
+ }
24404
25573
  }
24405
25574
  }
24406
25575
  }
@@ -31207,7 +32376,7 @@ var TextToSpeech = class _TextToSpeech {
31207
32376
  */
31208
32377
  async toFile(text, filePath, options) {
31209
32378
  const response = await this.synthesize(text, options);
31210
- await fs18__namespace.writeFile(filePath, response.audio);
32379
+ await fs17__namespace.writeFile(filePath, response.audio);
31211
32380
  }
31212
32381
  // ======================== Introspection Methods ========================
31213
32382
  /**
@@ -31555,7 +32724,7 @@ var SpeechToText = class _SpeechToText {
31555
32724
  * @param options - Optional transcription parameters
31556
32725
  */
31557
32726
  async transcribeFile(filePath, options) {
31558
- const audio = await fs18__namespace.readFile(filePath);
32727
+ const audio = await fs17__namespace.readFile(filePath);
31559
32728
  return this.transcribe(audio, options);
31560
32729
  }
31561
32730
  /**
@@ -35701,7 +36870,7 @@ var DocumentReader = class _DocumentReader {
35701
36870
  async resolveSource(source) {
35702
36871
  switch (source.type) {
35703
36872
  case "file": {
35704
- const buffer = await fs18.readFile(source.path);
36873
+ const buffer = await fs17.readFile(source.path);
35705
36874
  const filename = source.path.split("/").pop() || source.path;
35706
36875
  return { buffer, filename };
35707
36876
  }
@@ -38298,10 +39467,10 @@ var FileMediaStorage = class {
38298
39467
  }
38299
39468
  async save(data, metadata) {
38300
39469
  const dir = metadata.userId ? path2__namespace.join(this.outputDir, metadata.userId) : this.outputDir;
38301
- await fs18__namespace.mkdir(dir, { recursive: true });
39470
+ await fs17__namespace.mkdir(dir, { recursive: true });
38302
39471
  const filename = metadata.suggestedFilename ?? this.generateFilename(metadata);
38303
39472
  const filePath = path2__namespace.join(dir, filename);
38304
- await fs18__namespace.writeFile(filePath, data);
39473
+ await fs17__namespace.writeFile(filePath, data);
38305
39474
  const format = metadata.format.toLowerCase();
38306
39475
  const mimeType = MIME_TYPES2[format] ?? "application/octet-stream";
38307
39476
  return {
@@ -38312,7 +39481,7 @@ var FileMediaStorage = class {
38312
39481
  }
38313
39482
  async read(location) {
38314
39483
  try {
38315
- return await fs18__namespace.readFile(location);
39484
+ return await fs17__namespace.readFile(location);
38316
39485
  } catch (err) {
38317
39486
  if (err.code === "ENOENT") {
38318
39487
  return null;
@@ -38322,7 +39491,7 @@ var FileMediaStorage = class {
38322
39491
  }
38323
39492
  async delete(location) {
38324
39493
  try {
38325
- await fs18__namespace.unlink(location);
39494
+ await fs17__namespace.unlink(location);
38326
39495
  } catch (err) {
38327
39496
  if (err.code === "ENOENT") {
38328
39497
  return;
@@ -38332,7 +39501,7 @@ var FileMediaStorage = class {
38332
39501
  }
38333
39502
  async exists(location) {
38334
39503
  try {
38335
- await fs18__namespace.access(location);
39504
+ await fs17__namespace.access(location);
38336
39505
  return true;
38337
39506
  } catch {
38338
39507
  return false;
@@ -38341,11 +39510,11 @@ var FileMediaStorage = class {
38341
39510
  async list(options) {
38342
39511
  await this.ensureDir();
38343
39512
  let entries = [];
38344
- const files = await fs18__namespace.readdir(this.outputDir);
39513
+ const files = await fs17__namespace.readdir(this.outputDir);
38345
39514
  for (const file of files) {
38346
39515
  const filePath = path2__namespace.join(this.outputDir, file);
38347
39516
  try {
38348
- const stat6 = await fs18__namespace.stat(filePath);
39517
+ const stat6 = await fs17__namespace.stat(filePath);
38349
39518
  if (!stat6.isFile()) continue;
38350
39519
  const ext = path2__namespace.extname(file).slice(1).toLowerCase();
38351
39520
  const mimeType = MIME_TYPES2[ext] ?? "application/octet-stream";
@@ -38385,7 +39554,7 @@ var FileMediaStorage = class {
38385
39554
  }
38386
39555
  async ensureDir() {
38387
39556
  if (!this.initialized) {
38388
- await fs18__namespace.mkdir(this.outputDir, { recursive: true });
39557
+ await fs17__namespace.mkdir(this.outputDir, { recursive: true });
38389
39558
  this.initialized = true;
38390
39559
  }
38391
39560
  }
@@ -38920,6 +40089,42 @@ var StreamHelpers = class {
38920
40089
  }
38921
40090
  return chunks.join("");
38922
40091
  }
40092
+ /**
40093
+ * Get only reasoning/thinking deltas from stream
40094
+ * Filters out all other event types
40095
+ */
40096
+ static async *thinkingOnly(stream) {
40097
+ for await (const event of stream) {
40098
+ if (isReasoningDelta(event)) {
40099
+ yield event.delta;
40100
+ }
40101
+ }
40102
+ }
40103
+ /**
40104
+ * Get both text and thinking deltas from stream
40105
+ * Yields tagged objects so consumers can distinguish them
40106
+ */
40107
+ static async *textAndThinking(stream) {
40108
+ for await (const event of stream) {
40109
+ if (isOutputTextDelta(event)) {
40110
+ yield { type: "text", delta: event.delta };
40111
+ } else if (isReasoningDelta(event)) {
40112
+ yield { type: "thinking", delta: event.delta };
40113
+ }
40114
+ }
40115
+ }
40116
+ /**
40117
+ * Accumulate all thinking/reasoning content from stream into a single string
40118
+ */
40119
+ static async accumulateThinking(stream) {
40120
+ const chunks = [];
40121
+ for await (const event of stream) {
40122
+ if (isReasoningDelta(event)) {
40123
+ chunks.push(event.delta);
40124
+ }
40125
+ }
40126
+ return chunks.join("");
40127
+ }
38923
40128
  /**
38924
40129
  * Buffer stream events into batches
38925
40130
  */
@@ -38979,6 +40184,11 @@ var StreamHelpers = class {
38979
40184
  case "response.output_text.delta" /* OUTPUT_TEXT_DELTA */:
38980
40185
  state.accumulateTextDelta(event.item_id, event.delta);
38981
40186
  break;
40187
+ case "response.reasoning.delta" /* REASONING_DELTA */:
40188
+ state.accumulateReasoningDelta(event.item_id, event.delta);
40189
+ break;
40190
+ case "response.reasoning.done" /* REASONING_DONE */:
40191
+ break;
38982
40192
  case "response.tool_call.start" /* TOOL_CALL_START */:
38983
40193
  state.startToolCall(event.tool_call_id, event.tool_name);
38984
40194
  break;
@@ -39009,21 +40219,36 @@ var StreamHelpers = class {
39009
40219
  */
39010
40220
  static reconstructLLMResponse(state) {
39011
40221
  const output = [];
40222
+ const contentParts = [];
40223
+ let thinkingText;
40224
+ if (state.hasReasoning()) {
40225
+ const reasoning = state.getAllReasoning();
40226
+ if (reasoning) {
40227
+ thinkingText = reasoning;
40228
+ contentParts.push({
40229
+ type: "thinking" /* THINKING */,
40230
+ thinking: reasoning,
40231
+ persistInHistory: false
40232
+ // Vendor-agnostic default; caller can adjust
40233
+ });
40234
+ }
40235
+ }
39012
40236
  if (state.hasText()) {
39013
40237
  const textContent = state.getAllText();
39014
40238
  if (textContent) {
39015
- output.push({
39016
- type: "message",
39017
- role: "assistant" /* ASSISTANT */,
39018
- content: [
39019
- {
39020
- type: "output_text" /* OUTPUT_TEXT */,
39021
- text: textContent
39022
- }
39023
- ]
40239
+ contentParts.push({
40240
+ type: "output_text" /* OUTPUT_TEXT */,
40241
+ text: textContent
39024
40242
  });
39025
40243
  }
39026
40244
  }
40245
+ if (contentParts.length > 0) {
40246
+ output.push({
40247
+ type: "message",
40248
+ role: "assistant" /* ASSISTANT */,
40249
+ content: contentParts
40250
+ });
40251
+ }
39027
40252
  const toolCalls = state.getCompletedToolCalls();
39028
40253
  if (toolCalls.length > 0) {
39029
40254
  const toolUseContent = toolCalls.map((tc) => ({
@@ -39052,6 +40277,7 @@ var StreamHelpers = class {
39052
40277
  model: state.model,
39053
40278
  output,
39054
40279
  output_text: outputText,
40280
+ thinking: thinkingText,
39055
40281
  usage: state.usage
39056
40282
  };
39057
40283
  }
@@ -39180,6 +40406,15 @@ var SERVICE_DEFINITIONS = [
39180
40406
  baseURL: "https://api.telegram.org",
39181
40407
  docsURL: "https://core.telegram.org/bots/api"
39182
40408
  },
40409
+ {
40410
+ id: "twitter",
40411
+ name: "X (Twitter)",
40412
+ category: "communication",
40413
+ urlPattern: /api\.x\.com|api\.twitter\.com/i,
40414
+ baseURL: "https://api.x.com/2",
40415
+ docsURL: "https://developer.x.com/en/docs/x-api",
40416
+ commonScopes: ["tweet.read", "tweet.write", "users.read", "offline.access"]
40417
+ },
39183
40418
  // ============ Development & Project Management ============
39184
40419
  {
39185
40420
  id: "github",
@@ -39667,19 +40902,30 @@ var ConnectorTools = class {
39667
40902
  */
39668
40903
  static for(connectorOrName, userId, options) {
39669
40904
  const connector = this.resolveConnector(connectorOrName, options?.registry);
40905
+ const accountId = options?.accountId;
39670
40906
  const tools = [];
40907
+ const namePrefix = accountId ? `${sanitizeToolName(connector.name)}_${sanitizeToolName(accountId)}` : sanitizeToolName(connector.name);
39671
40908
  if (connector.baseURL) {
39672
- tools.push(this.createGenericAPITool(connector, { userId }));
40909
+ const accountLabel = accountId ? ` (account: ${accountId})` : "";
40910
+ tools.push(this.createGenericAPITool(connector, {
40911
+ userId,
40912
+ accountId,
40913
+ toolName: `${namePrefix}_api`,
40914
+ description: `Make an authenticated API call to ${connector.displayName}${accountLabel}.` + (connector.baseURL ? ` Base URL: ${connector.baseURL}.` : " Provide full URL in endpoint.") + ' IMPORTANT: For POST/PUT/PATCH requests, pass data in the "body" parameter as a JSON object, NOT as query string parameters in the endpoint URL. The body is sent as application/json.'
40915
+ }));
39673
40916
  }
39674
40917
  const serviceType = this.detectService(connector);
39675
40918
  if (serviceType && this.factories.has(serviceType)) {
39676
40919
  const factory = this.factories.get(serviceType);
39677
40920
  const serviceTools = factory(connector, userId);
39678
40921
  for (const tool of serviceTools) {
39679
- tool.definition.function.name = `${sanitizeToolName(connector.name)}_${tool.definition.function.name}`;
40922
+ tool.definition.function.name = `${namePrefix}_${tool.definition.function.name}`;
39680
40923
  }
39681
40924
  tools.push(...serviceTools);
39682
40925
  }
40926
+ if (accountId) {
40927
+ return tools.map((tool) => this.bindAccountId(tool, accountId));
40928
+ }
39683
40929
  return tools;
39684
40930
  }
39685
40931
  /**
@@ -39829,6 +41075,56 @@ var ConnectorTools = class {
39829
41075
  }
39830
41076
  return connectorOrName;
39831
41077
  }
41078
+ /**
41079
+ * Generate tools for a set of auth identities.
41080
+ * Each identity gets its own tool set with unique name prefixes.
41081
+ *
41082
+ * @param identities - Array of auth identities
41083
+ * @param userId - Optional user ID for multi-user OAuth
41084
+ * @param options - Optional registry for scoped connector lookup
41085
+ * @returns Map of identity key to tool array
41086
+ *
41087
+ * @example
41088
+ * ```typescript
41089
+ * const toolsByIdentity = ConnectorTools.forIdentities([
41090
+ * { connector: 'microsoft', accountId: 'work' },
41091
+ * { connector: 'microsoft', accountId: 'personal' },
41092
+ * { connector: 'github' },
41093
+ * ]);
41094
+ * // Keys: 'microsoft:work', 'microsoft:personal', 'github'
41095
+ * ```
41096
+ */
41097
+ static forIdentities(identities, userId, options) {
41098
+ const result = /* @__PURE__ */ new Map();
41099
+ for (const identity of identities) {
41100
+ const key = identity.accountId ? `${identity.connector}:${identity.accountId}` : identity.connector;
41101
+ try {
41102
+ const tools = this.for(identity.connector, userId, {
41103
+ registry: options?.registry,
41104
+ accountId: identity.accountId
41105
+ });
41106
+ if (tools.length > 0) {
41107
+ result.set(key, tools);
41108
+ }
41109
+ } catch (err) {
41110
+ exports.logger.error(`[ConnectorTools.forIdentities] Error generating tools for identity ${key}: ${err instanceof Error ? err.message : String(err)}`);
41111
+ }
41112
+ }
41113
+ return result;
41114
+ }
41115
+ /**
41116
+ * Wrap a tool to inject accountId into ToolContext at execute time.
41117
+ * This allows identity-bound tools to use the correct account without
41118
+ * modifying every service tool factory.
41119
+ */
41120
+ static bindAccountId(tool, accountId) {
41121
+ return {
41122
+ ...tool,
41123
+ execute: async (args, context) => {
41124
+ return tool.execute(args, { ...context, accountId });
41125
+ }
41126
+ };
41127
+ }
39832
41128
  static createGenericAPITool(connector, options) {
39833
41129
  const toolName = options?.toolName ?? `${sanitizeToolName(connector.name)}_api`;
39834
41130
  const userId = options?.userId;
@@ -39870,6 +41166,7 @@ var ConnectorTools = class {
39870
41166
  },
39871
41167
  execute: async (args, context) => {
39872
41168
  const effectiveUserId = context?.userId ?? userId;
41169
+ const effectiveAccountId = context?.accountId;
39873
41170
  let url2 = args.endpoint;
39874
41171
  if (args.queryParams && Object.keys(args.queryParams).length > 0) {
39875
41172
  const params = new URLSearchParams();
@@ -39902,7 +41199,8 @@ var ConnectorTools = class {
39902
41199
  },
39903
41200
  body: bodyStr
39904
41201
  },
39905
- effectiveUserId
41202
+ effectiveUserId,
41203
+ effectiveAccountId
39906
41204
  );
39907
41205
  const text = await response.text();
39908
41206
  let data;
@@ -39963,8 +41261,8 @@ var FileStorage = class {
39963
41261
  }
39964
41262
  async ensureDirectory() {
39965
41263
  try {
39966
- await fs18__namespace.mkdir(this.directory, { recursive: true });
39967
- await fs18__namespace.chmod(this.directory, 448);
41264
+ await fs17__namespace.mkdir(this.directory, { recursive: true });
41265
+ await fs17__namespace.chmod(this.directory, 448);
39968
41266
  } catch (error) {
39969
41267
  }
39970
41268
  }
@@ -39978,24 +41276,27 @@ var FileStorage = class {
39978
41276
  async storeToken(key, token) {
39979
41277
  await this.ensureDirectory();
39980
41278
  const filePath = this.getFilePath(key);
39981
- const plaintext = JSON.stringify(token);
41279
+ const tokenWithKey = { ...token, _storageKey: key };
41280
+ const plaintext = JSON.stringify(tokenWithKey);
39982
41281
  const encrypted = encrypt(plaintext, this.encryptionKey);
39983
- await fs18__namespace.writeFile(filePath, encrypted, "utf8");
39984
- await fs18__namespace.chmod(filePath, 384);
41282
+ await fs17__namespace.writeFile(filePath, encrypted, "utf8");
41283
+ await fs17__namespace.chmod(filePath, 384);
39985
41284
  }
39986
41285
  async getToken(key) {
39987
41286
  const filePath = this.getFilePath(key);
39988
41287
  try {
39989
- const encrypted = await fs18__namespace.readFile(filePath, "utf8");
41288
+ const encrypted = await fs17__namespace.readFile(filePath, "utf8");
39990
41289
  const decrypted = decrypt(encrypted, this.encryptionKey);
39991
- return JSON.parse(decrypted);
41290
+ const parsed = JSON.parse(decrypted);
41291
+ const { _storageKey, ...token } = parsed;
41292
+ return token;
39992
41293
  } catch (error) {
39993
41294
  if (error.code === "ENOENT") {
39994
41295
  return null;
39995
41296
  }
39996
41297
  console.error("Failed to read/decrypt token file:", error);
39997
41298
  try {
39998
- await fs18__namespace.unlink(filePath);
41299
+ await fs17__namespace.unlink(filePath);
39999
41300
  } catch {
40000
41301
  }
40001
41302
  return null;
@@ -40004,7 +41305,7 @@ var FileStorage = class {
40004
41305
  async deleteToken(key) {
40005
41306
  const filePath = this.getFilePath(key);
40006
41307
  try {
40007
- await fs18__namespace.unlink(filePath);
41308
+ await fs17__namespace.unlink(filePath);
40008
41309
  } catch (error) {
40009
41310
  if (error.code !== "ENOENT") {
40010
41311
  throw error;
@@ -40014,18 +41315,44 @@ var FileStorage = class {
40014
41315
  async hasToken(key) {
40015
41316
  const filePath = this.getFilePath(key);
40016
41317
  try {
40017
- await fs18__namespace.access(filePath);
41318
+ await fs17__namespace.access(filePath);
40018
41319
  return true;
40019
41320
  } catch {
40020
41321
  return false;
40021
41322
  }
40022
41323
  }
41324
+ /**
41325
+ * List all storage keys by decrypting each token file and reading _storageKey.
41326
+ * Falls back to returning hashed filenames for tokens stored before multi-account support.
41327
+ */
41328
+ async listKeys() {
41329
+ try {
41330
+ const files = await fs17__namespace.readdir(this.directory);
41331
+ const tokenFiles = files.filter((f) => f.endsWith(".token"));
41332
+ const keys = [];
41333
+ for (const file of tokenFiles) {
41334
+ try {
41335
+ const filePath = path2__namespace.join(this.directory, file);
41336
+ const encrypted = await fs17__namespace.readFile(filePath, "utf8");
41337
+ const decrypted = decrypt(encrypted, this.encryptionKey);
41338
+ const parsed = JSON.parse(decrypted);
41339
+ if (parsed._storageKey) {
41340
+ keys.push(parsed._storageKey);
41341
+ }
41342
+ } catch {
41343
+ }
41344
+ }
41345
+ return keys;
41346
+ } catch {
41347
+ return [];
41348
+ }
41349
+ }
40023
41350
  /**
40024
41351
  * List all token keys (for debugging)
40025
41352
  */
40026
41353
  async listTokens() {
40027
41354
  try {
40028
- const files = await fs18__namespace.readdir(this.directory);
41355
+ const files = await fs17__namespace.readdir(this.directory);
40029
41356
  return files.filter((f) => f.endsWith(".token")).map((f) => f.replace(".token", ""));
40030
41357
  } catch {
40031
41358
  return [];
@@ -40036,10 +41363,10 @@ var FileStorage = class {
40036
41363
  */
40037
41364
  async clearAll() {
40038
41365
  try {
40039
- const files = await fs18__namespace.readdir(this.directory);
41366
+ const files = await fs17__namespace.readdir(this.directory);
40040
41367
  const tokenFiles = files.filter((f) => f.endsWith(".token"));
40041
41368
  await Promise.all(
40042
- tokenFiles.map((f) => fs18__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
41369
+ tokenFiles.map((f) => fs17__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
40043
41370
  }))
40044
41371
  );
40045
41372
  } catch {
@@ -40049,14 +41376,14 @@ var FileStorage = class {
40049
41376
 
40050
41377
  // src/connectors/authenticatedFetch.ts
40051
41378
  init_Connector();
40052
- async function authenticatedFetch(url2, options, authProvider, userId) {
41379
+ async function authenticatedFetch(url2, options, authProvider, userId, accountId) {
40053
41380
  const connector = exports.Connector.get(authProvider);
40054
- return connector.fetch(url2.toString(), options, userId);
41381
+ return connector.fetch(url2.toString(), options, userId, accountId);
40055
41382
  }
40056
- function createAuthenticatedFetch(authProvider, userId) {
41383
+ function createAuthenticatedFetch(authProvider, userId, accountId) {
40057
41384
  const connector = exports.Connector.get(authProvider);
40058
41385
  return async (url2, options) => {
40059
- return connector.fetch(url2.toString(), options, userId);
41386
+ return connector.fetch(url2.toString(), options, userId, accountId);
40060
41387
  };
40061
41388
  }
40062
41389
 
@@ -40487,14 +41814,14 @@ var FileConnectorStorage = class {
40487
41814
  await this.ensureDirectory();
40488
41815
  const filePath = this.getFilePath(name);
40489
41816
  const json = JSON.stringify(stored, null, 2);
40490
- await fs18__namespace.writeFile(filePath, json, "utf8");
40491
- await fs18__namespace.chmod(filePath, 384);
41817
+ await fs17__namespace.writeFile(filePath, json, "utf8");
41818
+ await fs17__namespace.chmod(filePath, 384);
40492
41819
  await this.updateIndex(name, "add");
40493
41820
  }
40494
41821
  async get(name) {
40495
41822
  const filePath = this.getFilePath(name);
40496
41823
  try {
40497
- const json = await fs18__namespace.readFile(filePath, "utf8");
41824
+ const json = await fs17__namespace.readFile(filePath, "utf8");
40498
41825
  return JSON.parse(json);
40499
41826
  } catch (error) {
40500
41827
  const err = error;
@@ -40507,7 +41834,7 @@ var FileConnectorStorage = class {
40507
41834
  async delete(name) {
40508
41835
  const filePath = this.getFilePath(name);
40509
41836
  try {
40510
- await fs18__namespace.unlink(filePath);
41837
+ await fs17__namespace.unlink(filePath);
40511
41838
  await this.updateIndex(name, "remove");
40512
41839
  return true;
40513
41840
  } catch (error) {
@@ -40521,7 +41848,7 @@ var FileConnectorStorage = class {
40521
41848
  async has(name) {
40522
41849
  const filePath = this.getFilePath(name);
40523
41850
  try {
40524
- await fs18__namespace.access(filePath);
41851
+ await fs17__namespace.access(filePath);
40525
41852
  return true;
40526
41853
  } catch {
40527
41854
  return false;
@@ -40547,13 +41874,13 @@ var FileConnectorStorage = class {
40547
41874
  */
40548
41875
  async clear() {
40549
41876
  try {
40550
- const files = await fs18__namespace.readdir(this.directory);
41877
+ const files = await fs17__namespace.readdir(this.directory);
40551
41878
  const connectorFiles = files.filter(
40552
41879
  (f) => f.endsWith(".connector.json") || f === "_index.json"
40553
41880
  );
40554
41881
  await Promise.all(
40555
41882
  connectorFiles.map(
40556
- (f) => fs18__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
41883
+ (f) => fs17__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
40557
41884
  })
40558
41885
  )
40559
41886
  );
@@ -40580,8 +41907,8 @@ var FileConnectorStorage = class {
40580
41907
  async ensureDirectory() {
40581
41908
  if (this.initialized) return;
40582
41909
  try {
40583
- await fs18__namespace.mkdir(this.directory, { recursive: true });
40584
- await fs18__namespace.chmod(this.directory, 448);
41910
+ await fs17__namespace.mkdir(this.directory, { recursive: true });
41911
+ await fs17__namespace.chmod(this.directory, 448);
40585
41912
  this.initialized = true;
40586
41913
  } catch {
40587
41914
  this.initialized = true;
@@ -40592,7 +41919,7 @@ var FileConnectorStorage = class {
40592
41919
  */
40593
41920
  async loadIndex() {
40594
41921
  try {
40595
- const json = await fs18__namespace.readFile(this.indexPath, "utf8");
41922
+ const json = await fs17__namespace.readFile(this.indexPath, "utf8");
40596
41923
  return JSON.parse(json);
40597
41924
  } catch {
40598
41925
  return { connectors: {} };
@@ -40610,8 +41937,8 @@ var FileConnectorStorage = class {
40610
41937
  delete index.connectors[hash];
40611
41938
  }
40612
41939
  const json = JSON.stringify(index, null, 2);
40613
- await fs18__namespace.writeFile(this.indexPath, json, "utf8");
40614
- await fs18__namespace.chmod(this.indexPath, 384);
41940
+ await fs17__namespace.writeFile(this.indexPath, json, "utf8");
41941
+ await fs17__namespace.chmod(this.indexPath, 384);
40615
41942
  }
40616
41943
  };
40617
41944
 
@@ -40834,14 +42161,15 @@ var microsoftTemplate = {
40834
42161
  name: "OAuth (Delegated Permissions)",
40835
42162
  type: "oauth",
40836
42163
  flow: "authorization_code",
40837
- description: "User signs in with Microsoft account. Best for accessing user data (mail, calendar, files)",
40838
- requiredFields: ["clientId", "clientSecret", "redirectUri", "tenantId"],
40839
- optionalFields: ["scope"],
42164
+ description: "User signs in with Microsoft account. Best for accessing user data (mail, calendar, files). Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42165
+ requiredFields: ["clientId", "redirectUri", "tenantId"],
42166
+ optionalFields: ["clientSecret", "scope"],
40840
42167
  defaults: {
40841
42168
  type: "oauth",
40842
42169
  flow: "authorization_code",
40843
42170
  authorizationUrl: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize",
40844
- tokenUrl: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token"
42171
+ tokenUrl: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token",
42172
+ usePKCE: true
40845
42173
  },
40846
42174
  scopes: [
40847
42175
  "User.Read",
@@ -40927,14 +42255,15 @@ var googleTemplate = {
40927
42255
  name: "OAuth (User Consent)",
40928
42256
  type: "oauth",
40929
42257
  flow: "authorization_code",
40930
- description: "User logs in with Google account. Best for accessing user data (Drive, Gmail, Calendar)",
40931
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
40932
- optionalFields: ["scope"],
42258
+ description: "User logs in with Google account. Best for accessing user data (Drive, Gmail, Calendar). Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42259
+ requiredFields: ["clientId", "redirectUri"],
42260
+ optionalFields: ["clientSecret", "scope"],
40933
42261
  defaults: {
40934
42262
  type: "oauth",
40935
42263
  flow: "authorization_code",
40936
42264
  authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
40937
- tokenUrl: "https://oauth2.googleapis.com/token"
42265
+ tokenUrl: "https://oauth2.googleapis.com/token",
42266
+ usePKCE: true
40938
42267
  },
40939
42268
  scopes: [
40940
42269
  "https://www.googleapis.com/auth/drive",
@@ -41013,14 +42342,15 @@ var slackTemplate = {
41013
42342
  name: "OAuth (User Token)",
41014
42343
  type: "oauth",
41015
42344
  flow: "authorization_code",
41016
- description: "Distributed app - users authorize via Slack OAuth",
41017
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41018
- optionalFields: ["scope", "userScope"],
42345
+ description: "Distributed app - users authorize via Slack OAuth. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42346
+ requiredFields: ["clientId", "redirectUri"],
42347
+ optionalFields: ["clientSecret", "scope", "userScope"],
41019
42348
  defaults: {
41020
42349
  type: "oauth",
41021
42350
  flow: "authorization_code",
41022
42351
  authorizationUrl: "https://slack.com/oauth/v2/authorize",
41023
- tokenUrl: "https://slack.com/api/oauth.v2.access"
42352
+ tokenUrl: "https://slack.com/api/oauth.v2.access",
42353
+ usePKCE: true
41024
42354
  },
41025
42355
  scopes: ["chat:write", "channels:read", "users:read", "im:write", "groups:read", "files:read", "files:write", "reactions:read", "reactions:write", "team:read"],
41026
42356
  scopeDescriptions: {
@@ -41066,14 +42396,15 @@ var discordTemplate = {
41066
42396
  name: "OAuth (User Token)",
41067
42397
  type: "oauth",
41068
42398
  flow: "authorization_code",
41069
- description: "OAuth2 for user authorization - users grant permissions to your app",
41070
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41071
- optionalFields: ["scope"],
42399
+ description: "OAuth2 for user authorization - users grant permissions to your app. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42400
+ requiredFields: ["clientId", "redirectUri"],
42401
+ optionalFields: ["clientSecret", "scope"],
41072
42402
  defaults: {
41073
42403
  type: "oauth",
41074
42404
  flow: "authorization_code",
41075
42405
  authorizationUrl: "https://discord.com/api/oauth2/authorize",
41076
- tokenUrl: "https://discord.com/api/oauth2/token"
42406
+ tokenUrl: "https://discord.com/api/oauth2/token",
42407
+ usePKCE: true
41077
42408
  },
41078
42409
  scopes: ["identify", "email", "guilds", "guilds.members.read", "messages.read", "bot", "connections"],
41079
42410
  scopeDescriptions: {
@@ -41115,6 +42446,92 @@ var telegramTemplate = {
41115
42446
  ]
41116
42447
  };
41117
42448
 
42449
+ // src/connectors/vendors/templates/twitter.ts
42450
+ var twitterTemplate = {
42451
+ id: "twitter",
42452
+ name: "X (Twitter)",
42453
+ serviceType: "twitter",
42454
+ baseURL: "https://api.x.com/2",
42455
+ docsURL: "https://developer.x.com/en/docs/x-api",
42456
+ credentialsSetupURL: "https://developer.x.com/en/portal/dashboard",
42457
+ category: "communication",
42458
+ notes: "X (formerly Twitter) API v2. OAuth 2.0 with PKCE for user-context actions, Bearer Token for app-only access.",
42459
+ authTemplates: [
42460
+ {
42461
+ id: "oauth-user",
42462
+ name: "OAuth 2.0 (User Context)",
42463
+ type: "oauth",
42464
+ flow: "authorization_code",
42465
+ description: "User authorizes via X login - required for posting tweets, managing likes/follows, and accessing private data. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42466
+ requiredFields: ["clientId", "redirectUri"],
42467
+ optionalFields: ["clientSecret", "scope"],
42468
+ defaults: {
42469
+ type: "oauth",
42470
+ flow: "authorization_code",
42471
+ authorizationUrl: "https://x.com/i/oauth2/authorize",
42472
+ tokenUrl: "https://api.x.com/2/oauth2/token",
42473
+ usePKCE: true
42474
+ },
42475
+ scopes: [
42476
+ "tweet.read",
42477
+ "tweet.write",
42478
+ "tweet.moderate.write",
42479
+ "users.read",
42480
+ "follows.read",
42481
+ "follows.write",
42482
+ "like.read",
42483
+ "like.write",
42484
+ "bookmark.read",
42485
+ "bookmark.write",
42486
+ "list.read",
42487
+ "list.write",
42488
+ "block.read",
42489
+ "block.write",
42490
+ "mute.read",
42491
+ "mute.write",
42492
+ "space.read",
42493
+ "dm.read",
42494
+ "dm.write",
42495
+ "offline.access"
42496
+ ],
42497
+ scopeDescriptions: {
42498
+ "tweet.read": "Read tweets and timelines",
42499
+ "tweet.write": "Post and delete tweets",
42500
+ "tweet.moderate.write": "Hide and unhide replies",
42501
+ "users.read": "Read user profile information",
42502
+ "follows.read": "Read following/followers lists",
42503
+ "follows.write": "Follow and unfollow users",
42504
+ "like.read": "Read liked tweets",
42505
+ "like.write": "Like and unlike tweets",
42506
+ "bookmark.read": "Read bookmarked tweets",
42507
+ "bookmark.write": "Bookmark and remove bookmarks",
42508
+ "list.read": "Read lists",
42509
+ "list.write": "Create, edit, and delete lists",
42510
+ "block.read": "Read blocked users",
42511
+ "block.write": "Block and unblock users",
42512
+ "mute.read": "Read muted users",
42513
+ "mute.write": "Mute and unmute users",
42514
+ "space.read": "Read Spaces information",
42515
+ "dm.read": "Read direct messages",
42516
+ "dm.write": "Send direct messages",
42517
+ "offline.access": "Stay connected (refresh token)"
42518
+ }
42519
+ },
42520
+ {
42521
+ id: "bearer-token",
42522
+ name: "Bearer Token (App-Only)",
42523
+ type: "api_key",
42524
+ description: "App-only access using Bearer Token from developer portal. Can read public tweets, users, and spaces but cannot post or access private data.",
42525
+ requiredFields: ["apiKey"],
42526
+ defaults: {
42527
+ type: "api_key",
42528
+ headerName: "Authorization",
42529
+ headerPrefix: "Bearer"
42530
+ }
42531
+ }
42532
+ ]
42533
+ };
42534
+
41118
42535
  // src/connectors/vendors/templates/github.ts
41119
42536
  var githubTemplate = {
41120
42537
  id: "github",
@@ -41142,14 +42559,15 @@ var githubTemplate = {
41142
42559
  name: "OAuth App (User Authorization)",
41143
42560
  type: "oauth",
41144
42561
  flow: "authorization_code",
41145
- description: "User logs in via GitHub and grants permissions to your app",
41146
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41147
- optionalFields: ["scope"],
42562
+ description: "User logs in via GitHub and grants permissions to your app. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42563
+ requiredFields: ["clientId", "redirectUri"],
42564
+ optionalFields: ["clientSecret", "scope"],
41148
42565
  defaults: {
41149
42566
  type: "oauth",
41150
42567
  flow: "authorization_code",
41151
42568
  authorizationUrl: "https://github.com/login/oauth/authorize",
41152
- tokenUrl: "https://github.com/login/oauth/access_token"
42569
+ tokenUrl: "https://github.com/login/oauth/access_token",
42570
+ usePKCE: true
41153
42571
  },
41154
42572
  scopes: ["repo", "read:user", "user:email", "read:org", "workflow", "gist", "notifications", "delete_repo", "admin:org"],
41155
42573
  scopeDescriptions: {
@@ -41208,14 +42626,15 @@ var gitlabTemplate = {
41208
42626
  name: "OAuth (User Authorization)",
41209
42627
  type: "oauth",
41210
42628
  flow: "authorization_code",
41211
- description: "OAuth2 application for user authorization",
41212
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41213
- optionalFields: ["scope"],
42629
+ description: "OAuth2 application for user authorization. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42630
+ requiredFields: ["clientId", "redirectUri"],
42631
+ optionalFields: ["clientSecret", "scope"],
41214
42632
  defaults: {
41215
42633
  type: "oauth",
41216
42634
  flow: "authorization_code",
41217
42635
  authorizationUrl: "https://gitlab.com/oauth/authorize",
41218
- tokenUrl: "https://gitlab.com/oauth/token"
42636
+ tokenUrl: "https://gitlab.com/oauth/token",
42637
+ usePKCE: true
41219
42638
  },
41220
42639
  scopes: ["api", "read_user", "read_repository", "write_repository"],
41221
42640
  scopeDescriptions: {
@@ -41256,14 +42675,15 @@ var jiraTemplate = {
41256
42675
  name: "OAuth 2.0 (3LO)",
41257
42676
  type: "oauth",
41258
42677
  flow: "authorization_code",
41259
- description: "Three-legged OAuth for user authorization. Create app at developer.atlassian.com",
41260
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41261
- optionalFields: ["scope"],
42678
+ description: "Three-legged OAuth for user authorization. Create app at developer.atlassian.com. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42679
+ requiredFields: ["clientId", "redirectUri"],
42680
+ optionalFields: ["clientSecret", "scope"],
41262
42681
  defaults: {
41263
42682
  type: "oauth",
41264
42683
  flow: "authorization_code",
41265
42684
  authorizationUrl: "https://auth.atlassian.com/authorize",
41266
- tokenUrl: "https://auth.atlassian.com/oauth/token"
42685
+ tokenUrl: "https://auth.atlassian.com/oauth/token",
42686
+ usePKCE: true
41267
42687
  },
41268
42688
  scopes: ["read:jira-work", "write:jira-work", "read:jira-user", "manage:jira-project", "manage:jira-configuration"],
41269
42689
  scopeDescriptions: {
@@ -41303,14 +42723,15 @@ var confluenceTemplate = {
41303
42723
  name: "OAuth 2.0 (3LO)",
41304
42724
  type: "oauth",
41305
42725
  flow: "authorization_code",
41306
- description: "Three-legged OAuth for user authorization",
41307
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41308
- optionalFields: ["scope"],
42726
+ description: "Three-legged OAuth for user authorization. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42727
+ requiredFields: ["clientId", "redirectUri"],
42728
+ optionalFields: ["clientSecret", "scope"],
41309
42729
  defaults: {
41310
42730
  type: "oauth",
41311
42731
  flow: "authorization_code",
41312
42732
  authorizationUrl: "https://auth.atlassian.com/authorize",
41313
- tokenUrl: "https://auth.atlassian.com/oauth/token"
42733
+ tokenUrl: "https://auth.atlassian.com/oauth/token",
42734
+ usePKCE: true
41314
42735
  },
41315
42736
  scopes: ["read:confluence-content.all", "write:confluence-content", "read:confluence-space.summary", "write:confluence-space", "read:confluence-user"],
41316
42737
  scopeDescriptions: {
@@ -41349,14 +42770,15 @@ var bitbucketTemplate = {
41349
42770
  name: "OAuth Consumer",
41350
42771
  type: "oauth",
41351
42772
  flow: "authorization_code",
41352
- description: "OAuth consumer for user authorization. Create at Workspace Settings > OAuth consumers",
41353
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41354
- optionalFields: ["scope"],
42773
+ description: "OAuth consumer for user authorization. Create at Workspace Settings > OAuth consumers. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42774
+ requiredFields: ["clientId", "redirectUri"],
42775
+ optionalFields: ["clientSecret", "scope"],
41355
42776
  defaults: {
41356
42777
  type: "oauth",
41357
42778
  flow: "authorization_code",
41358
42779
  authorizationUrl: "https://bitbucket.org/site/oauth2/authorize",
41359
- tokenUrl: "https://bitbucket.org/site/oauth2/access_token"
42780
+ tokenUrl: "https://bitbucket.org/site/oauth2/access_token",
42781
+ usePKCE: true
41360
42782
  },
41361
42783
  scopes: ["repository", "repository:write", "pullrequest", "pullrequest:write", "account", "pipeline", "wiki"],
41362
42784
  scopeDescriptions: {
@@ -41398,14 +42820,15 @@ var trelloTemplate = {
41398
42820
  name: "OAuth 1.0a",
41399
42821
  type: "oauth",
41400
42822
  flow: "authorization_code",
41401
- description: "OAuth 1.0a for user authorization (legacy)",
41402
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41403
- optionalFields: ["scope"],
42823
+ description: "OAuth 1.0a for user authorization (legacy). Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42824
+ requiredFields: ["clientId", "redirectUri"],
42825
+ optionalFields: ["clientSecret", "scope"],
41404
42826
  defaults: {
41405
42827
  type: "oauth",
41406
42828
  flow: "authorization_code",
41407
42829
  authorizationUrl: "https://trello.com/1/authorize",
41408
- tokenUrl: "https://trello.com/1/OAuthGetAccessToken"
42830
+ tokenUrl: "https://trello.com/1/OAuthGetAccessToken",
42831
+ usePKCE: true
41409
42832
  },
41410
42833
  scopes: ["read", "write", "account"],
41411
42834
  scopeDescriptions: {
@@ -41445,14 +42868,15 @@ var linearTemplate = {
41445
42868
  name: "OAuth (User Authorization)",
41446
42869
  type: "oauth",
41447
42870
  flow: "authorization_code",
41448
- description: "OAuth application for user authorization. Create at Settings > API > OAuth applications",
41449
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41450
- optionalFields: ["scope"],
42871
+ description: "OAuth application for user authorization. Create at Settings > API > OAuth applications. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42872
+ requiredFields: ["clientId", "redirectUri"],
42873
+ optionalFields: ["clientSecret", "scope"],
41451
42874
  defaults: {
41452
42875
  type: "oauth",
41453
42876
  flow: "authorization_code",
41454
42877
  authorizationUrl: "https://linear.app/oauth/authorize",
41455
- tokenUrl: "https://api.linear.app/oauth/token"
42878
+ tokenUrl: "https://api.linear.app/oauth/token",
42879
+ usePKCE: true
41456
42880
  },
41457
42881
  scopes: ["read", "write", "issues:create", "comments:create"]
41458
42882
  }
@@ -41486,14 +42910,15 @@ var asanaTemplate = {
41486
42910
  name: "OAuth (User Authorization)",
41487
42911
  type: "oauth",
41488
42912
  flow: "authorization_code",
41489
- description: "OAuth application for user authorization. Create at developer console",
41490
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41491
- optionalFields: ["scope"],
42913
+ description: "OAuth application for user authorization. Create at developer console. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42914
+ requiredFields: ["clientId", "redirectUri"],
42915
+ optionalFields: ["clientSecret", "scope"],
41492
42916
  defaults: {
41493
42917
  type: "oauth",
41494
42918
  flow: "authorization_code",
41495
42919
  authorizationUrl: "https://app.asana.com/-/oauth_authorize",
41496
- tokenUrl: "https://app.asana.com/-/oauth_token"
42920
+ tokenUrl: "https://app.asana.com/-/oauth_token",
42921
+ usePKCE: true
41497
42922
  },
41498
42923
  scopes: ["default"]
41499
42924
  }
@@ -41527,14 +42952,15 @@ var notionTemplate = {
41527
42952
  name: "Public Integration (OAuth)",
41528
42953
  type: "oauth",
41529
42954
  flow: "authorization_code",
41530
- description: "Public integration for multi-workspace access",
41531
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41532
- optionalFields: ["scope"],
42955
+ description: "Public integration for multi-workspace access. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42956
+ requiredFields: ["clientId", "redirectUri"],
42957
+ optionalFields: ["clientSecret", "scope"],
41533
42958
  defaults: {
41534
42959
  type: "oauth",
41535
42960
  flow: "authorization_code",
41536
42961
  authorizationUrl: "https://api.notion.com/v1/oauth/authorize",
41537
- tokenUrl: "https://api.notion.com/v1/oauth/token"
42962
+ tokenUrl: "https://api.notion.com/v1/oauth/token",
42963
+ usePKCE: true
41538
42964
  }
41539
42965
  }
41540
42966
  ]
@@ -41567,9 +42993,9 @@ var airtableTemplate = {
41567
42993
  name: "OAuth (User Authorization)",
41568
42994
  type: "oauth",
41569
42995
  flow: "authorization_code",
41570
- description: "OAuth integration for multi-user access. Register at airtable.com/create/oauth",
41571
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41572
- optionalFields: ["scope"],
42996
+ description: "OAuth integration for multi-user access. Register at airtable.com/create/oauth. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
42997
+ requiredFields: ["clientId", "redirectUri"],
42998
+ optionalFields: ["clientSecret", "scope"],
41573
42999
  defaults: {
41574
43000
  type: "oauth",
41575
43001
  flow: "authorization_code",
@@ -41598,14 +43024,15 @@ var salesforceTemplate = {
41598
43024
  name: "OAuth (User Authorization)",
41599
43025
  type: "oauth",
41600
43026
  flow: "authorization_code",
41601
- description: "User logs in via Salesforce. Create Connected App in Setup",
41602
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41603
- optionalFields: ["scope"],
43027
+ description: "User logs in via Salesforce. Create Connected App in Setup. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43028
+ requiredFields: ["clientId", "redirectUri"],
43029
+ optionalFields: ["clientSecret", "scope"],
41604
43030
  defaults: {
41605
43031
  type: "oauth",
41606
43032
  flow: "authorization_code",
41607
43033
  authorizationUrl: "https://login.salesforce.com/services/oauth2/authorize",
41608
- tokenUrl: "https://login.salesforce.com/services/oauth2/token"
43034
+ tokenUrl: "https://login.salesforce.com/services/oauth2/token",
43035
+ usePKCE: true
41609
43036
  },
41610
43037
  scopes: ["api", "refresh_token", "offline_access", "chatter_api", "wave_api", "full"],
41611
43038
  scopeDescriptions: {
@@ -41661,14 +43088,15 @@ var hubspotTemplate = {
41661
43088
  name: "OAuth (User Authorization)",
41662
43089
  type: "oauth",
41663
43090
  flow: "authorization_code",
41664
- description: "Public app OAuth for multi-portal access. Create app at developers.hubspot.com",
41665
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41666
- optionalFields: ["scope"],
43091
+ description: "Public app OAuth for multi-portal access. Create app at developers.hubspot.com. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43092
+ requiredFields: ["clientId", "redirectUri"],
43093
+ optionalFields: ["clientSecret", "scope"],
41667
43094
  defaults: {
41668
43095
  type: "oauth",
41669
43096
  flow: "authorization_code",
41670
43097
  authorizationUrl: "https://app.hubspot.com/oauth/authorize",
41671
- tokenUrl: "https://api.hubapi.com/oauth/v1/token"
43098
+ tokenUrl: "https://api.hubapi.com/oauth/v1/token",
43099
+ usePKCE: true
41672
43100
  },
41673
43101
  scopes: [
41674
43102
  "crm.objects.contacts.read",
@@ -41690,6 +43118,22 @@ var hubspotTemplate = {
41690
43118
  "tickets": "Read and write support tickets",
41691
43119
  "e-commerce": "Access e-commerce data (products, line items)"
41692
43120
  }
43121
+ },
43122
+ {
43123
+ id: "oauth-mcp",
43124
+ name: "MCP Auth App (OAuth 2.1)",
43125
+ type: "oauth",
43126
+ flow: "authorization_code",
43127
+ description: "HubSpot MCP Auth app using OAuth 2.1 with PKCE. Scopes are auto-granted based on user permissions at install time. Create app at developers.hubspot.com under MCP Auth Apps.",
43128
+ requiredFields: ["clientId", "redirectUri"],
43129
+ optionalFields: ["clientSecret"],
43130
+ defaults: {
43131
+ type: "oauth",
43132
+ flow: "authorization_code",
43133
+ authorizationUrl: "https://mcp.hubspot.com/oauth/authorize/user",
43134
+ tokenUrl: "https://mcp.hubspot.com/oauth/v1/token",
43135
+ usePKCE: true
43136
+ }
41693
43137
  }
41694
43138
  ]
41695
43139
  };
@@ -41721,14 +43165,15 @@ var pipedriveTemplate = {
41721
43165
  name: "OAuth (App Authorization)",
41722
43166
  type: "oauth",
41723
43167
  flow: "authorization_code",
41724
- description: "OAuth app for marketplace distribution. Create at developers.pipedrive.com",
41725
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41726
- optionalFields: ["scope"],
43168
+ description: "OAuth app for marketplace distribution. Create at developers.pipedrive.com. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43169
+ requiredFields: ["clientId", "redirectUri"],
43170
+ optionalFields: ["clientSecret", "scope"],
41727
43171
  defaults: {
41728
43172
  type: "oauth",
41729
43173
  flow: "authorization_code",
41730
43174
  authorizationUrl: "https://oauth.pipedrive.com/oauth/authorize",
41731
- tokenUrl: "https://oauth.pipedrive.com/oauth/token"
43175
+ tokenUrl: "https://oauth.pipedrive.com/oauth/token",
43176
+ usePKCE: true
41732
43177
  }
41733
43178
  }
41734
43179
  ]
@@ -41761,14 +43206,15 @@ var stripeTemplate = {
41761
43206
  name: "OAuth (Stripe Connect)",
41762
43207
  type: "oauth",
41763
43208
  flow: "authorization_code",
41764
- description: "Stripe Connect for marketplace platforms. Requires Connect setup in dashboard",
41765
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41766
- optionalFields: ["scope"],
43209
+ description: "Stripe Connect for marketplace platforms. Requires Connect setup in dashboard. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43210
+ requiredFields: ["clientId", "redirectUri"],
43211
+ optionalFields: ["clientSecret", "scope"],
41767
43212
  defaults: {
41768
43213
  type: "oauth",
41769
43214
  flow: "authorization_code",
41770
43215
  authorizationUrl: "https://connect.stripe.com/oauth/authorize",
41771
- tokenUrl: "https://connect.stripe.com/oauth/token"
43216
+ tokenUrl: "https://connect.stripe.com/oauth/token",
43217
+ usePKCE: true
41772
43218
  },
41773
43219
  scopes: ["read_write"]
41774
43220
  }
@@ -41818,14 +43264,15 @@ var quickbooksTemplate = {
41818
43264
  name: "OAuth (User Authorization)",
41819
43265
  type: "oauth",
41820
43266
  flow: "authorization_code",
41821
- description: "Standard OAuth 2.0 flow for accessing QuickBooks on behalf of a user. Create an app at developer.intuit.com",
41822
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41823
- optionalFields: ["scope"],
43267
+ description: "Standard OAuth 2.0 flow for accessing QuickBooks on behalf of a user. Create an app at developer.intuit.com. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43268
+ requiredFields: ["clientId", "redirectUri"],
43269
+ optionalFields: ["clientSecret", "scope"],
41824
43270
  defaults: {
41825
43271
  type: "oauth",
41826
43272
  flow: "authorization_code",
41827
43273
  authorizationUrl: "https://appcenter.intuit.com/connect/oauth2",
41828
- tokenUrl: "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer"
43274
+ tokenUrl: "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer",
43275
+ usePKCE: true
41829
43276
  },
41830
43277
  scopes: ["com.intuit.quickbooks.accounting", "com.intuit.quickbooks.payment"]
41831
43278
  }
@@ -41860,14 +43307,15 @@ var rampTemplate = {
41860
43307
  name: "OAuth (User Authorization)",
41861
43308
  type: "oauth",
41862
43309
  flow: "authorization_code",
41863
- description: "OAuth 2.0 authorization code flow for accessing Ramp on behalf of a user",
41864
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41865
- optionalFields: ["scope"],
43310
+ description: "OAuth 2.0 authorization code flow for accessing Ramp on behalf of a user. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43311
+ requiredFields: ["clientId", "redirectUri"],
43312
+ optionalFields: ["clientSecret", "scope"],
41866
43313
  defaults: {
41867
43314
  type: "oauth",
41868
43315
  flow: "authorization_code",
41869
43316
  authorizationUrl: "https://app.ramp.com/v1/authorize",
41870
- tokenUrl: "https://api.ramp.com/developer/v1/token"
43317
+ tokenUrl: "https://api.ramp.com/developer/v1/token",
43318
+ usePKCE: true
41871
43319
  },
41872
43320
  scopes: [
41873
43321
  "transactions:read",
@@ -41924,9 +43372,9 @@ var dropboxTemplate = {
41924
43372
  name: "OAuth (User Authorization)",
41925
43373
  type: "oauth",
41926
43374
  flow: "authorization_code",
41927
- description: "OAuth app for user authorization. Create app at dropbox.com/developers/apps",
41928
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41929
- optionalFields: ["scope"],
43375
+ description: "OAuth app for user authorization. Create app at dropbox.com/developers/apps. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43376
+ requiredFields: ["clientId", "redirectUri"],
43377
+ optionalFields: ["clientSecret", "scope"],
41930
43378
  defaults: {
41931
43379
  type: "oauth",
41932
43380
  flow: "authorization_code",
@@ -41963,14 +43411,15 @@ var boxTemplate = {
41963
43411
  name: "OAuth (User Authorization)",
41964
43412
  type: "oauth",
41965
43413
  flow: "authorization_code",
41966
- description: "OAuth 2.0 for user authorization. Create app at developer.box.com/console",
41967
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
41968
- optionalFields: ["scope"],
43414
+ description: "OAuth 2.0 for user authorization. Create app at developer.box.com/console. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43415
+ requiredFields: ["clientId", "redirectUri"],
43416
+ optionalFields: ["clientSecret", "scope"],
41969
43417
  defaults: {
41970
43418
  type: "oauth",
41971
43419
  flow: "authorization_code",
41972
43420
  authorizationUrl: "https://account.box.com/api/oauth2/authorize",
41973
- tokenUrl: "https://api.box.com/oauth2/token"
43421
+ tokenUrl: "https://api.box.com/oauth2/token",
43422
+ usePKCE: true
41974
43423
  },
41975
43424
  scopes: ["root_readwrite", "manage_users", "manage_groups", "manage_enterprise"],
41976
43425
  scopeDescriptions: {
@@ -42048,13 +43497,15 @@ var mailchimpTemplate = {
42048
43497
  name: "OAuth (User Authorization)",
42049
43498
  type: "oauth",
42050
43499
  flow: "authorization_code",
42051
- description: "OAuth for multi-account access. Register app at mailchimp.com/developer",
42052
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
43500
+ description: "OAuth for multi-account access. Register app at mailchimp.com/developer. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43501
+ requiredFields: ["clientId", "redirectUri"],
43502
+ optionalFields: ["clientSecret"],
42053
43503
  defaults: {
42054
43504
  type: "oauth",
42055
43505
  flow: "authorization_code",
42056
43506
  authorizationUrl: "https://login.mailchimp.com/oauth2/authorize",
42057
- tokenUrl: "https://login.mailchimp.com/oauth2/token"
43507
+ tokenUrl: "https://login.mailchimp.com/oauth2/token",
43508
+ usePKCE: true
42058
43509
  }
42059
43510
  }
42060
43511
  ]
@@ -42146,14 +43597,15 @@ var pagerdutyTemplate = {
42146
43597
  name: "OAuth (App Authorization)",
42147
43598
  type: "oauth",
42148
43599
  flow: "authorization_code",
42149
- description: "OAuth app for multi-account access. Register at developer.pagerduty.com",
42150
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
42151
- optionalFields: ["scope"],
43600
+ description: "OAuth app for multi-account access. Register at developer.pagerduty.com. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43601
+ requiredFields: ["clientId", "redirectUri"],
43602
+ optionalFields: ["clientSecret", "scope"],
42152
43603
  defaults: {
42153
43604
  type: "oauth",
42154
43605
  flow: "authorization_code",
42155
43606
  authorizationUrl: "https://app.pagerduty.com/oauth/authorize",
42156
- tokenUrl: "https://app.pagerduty.com/oauth/token"
43607
+ tokenUrl: "https://app.pagerduty.com/oauth/token",
43608
+ usePKCE: true
42157
43609
  },
42158
43610
  scopes: ["read", "write"],
42159
43611
  scopeDescriptions: {
@@ -42189,14 +43641,15 @@ var sentryTemplate = {
42189
43641
  name: "OAuth (Integration)",
42190
43642
  type: "oauth",
42191
43643
  flow: "authorization_code",
42192
- description: "OAuth integration. Create at Organization Settings > Integrations",
42193
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
42194
- optionalFields: ["scope"],
43644
+ description: "OAuth integration. Create at Organization Settings > Integrations. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43645
+ requiredFields: ["clientId", "redirectUri"],
43646
+ optionalFields: ["clientSecret", "scope"],
42195
43647
  defaults: {
42196
43648
  type: "oauth",
42197
43649
  flow: "authorization_code",
42198
43650
  authorizationUrl: "https://sentry.io/oauth/authorize/",
42199
- tokenUrl: "https://sentry.io/oauth/token/"
43651
+ tokenUrl: "https://sentry.io/oauth/token/",
43652
+ usePKCE: true
42200
43653
  },
42201
43654
  scopes: ["project:read", "project:write", "event:read", "org:read", "member:read"],
42202
43655
  scopeDescriptions: {
@@ -42393,14 +43846,15 @@ var zendeskTemplate = {
42393
43846
  name: "OAuth (User Authorization)",
42394
43847
  type: "oauth",
42395
43848
  flow: "authorization_code",
42396
- description: "OAuth client for user authorization. Create at Admin > Channels > API > OAuth Clients",
42397
- requiredFields: ["clientId", "clientSecret", "redirectUri", "subdomain"],
42398
- optionalFields: ["scope"],
43849
+ description: "OAuth client for user authorization. Create at Admin > Channels > API > OAuth Clients. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43850
+ requiredFields: ["clientId", "redirectUri", "subdomain"],
43851
+ optionalFields: ["clientSecret", "scope"],
42399
43852
  defaults: {
42400
43853
  type: "oauth",
42401
43854
  flow: "authorization_code",
42402
43855
  authorizationUrl: "https://{subdomain}.zendesk.com/oauth/authorizations/new",
42403
- tokenUrl: "https://{subdomain}.zendesk.com/oauth/tokens"
43856
+ tokenUrl: "https://{subdomain}.zendesk.com/oauth/tokens",
43857
+ usePKCE: true
42404
43858
  },
42405
43859
  scopes: ["read", "write", "tickets:read", "tickets:write"],
42406
43860
  scopeDescriptions: {
@@ -42438,13 +43892,15 @@ var intercomTemplate = {
42438
43892
  name: "OAuth (App Installation)",
42439
43893
  type: "oauth",
42440
43894
  flow: "authorization_code",
42441
- description: "OAuth for Intercom app marketplace distribution",
42442
- requiredFields: ["clientId", "clientSecret", "redirectUri"],
43895
+ description: "OAuth for Intercom app marketplace distribution. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43896
+ requiredFields: ["clientId", "redirectUri"],
43897
+ optionalFields: ["clientSecret"],
42443
43898
  defaults: {
42444
43899
  type: "oauth",
42445
43900
  flow: "authorization_code",
42446
43901
  authorizationUrl: "https://app.intercom.com/oauth",
42447
- tokenUrl: "https://api.intercom.io/auth/eagle/token"
43902
+ tokenUrl: "https://api.intercom.io/auth/eagle/token",
43903
+ usePKCE: true
42448
43904
  }
42449
43905
  }
42450
43906
  ]
@@ -42477,14 +43933,15 @@ var shopifyTemplate = {
42477
43933
  name: "OAuth (Public/Custom App)",
42478
43934
  type: "oauth",
42479
43935
  flow: "authorization_code",
42480
- description: "OAuth for public apps or per-store custom apps. Create at partners.shopify.com",
42481
- requiredFields: ["clientId", "clientSecret", "redirectUri", "subdomain"],
42482
- optionalFields: ["scope"],
43936
+ description: "OAuth for public apps or per-store custom apps. Create at partners.shopify.com. Provide clientSecret for web apps; omit for native/desktop apps (secured via PKCE).",
43937
+ requiredFields: ["clientId", "redirectUri", "subdomain"],
43938
+ optionalFields: ["clientSecret", "scope"],
42483
43939
  defaults: {
42484
43940
  type: "oauth",
42485
43941
  flow: "authorization_code",
42486
43942
  authorizationUrl: "https://{subdomain}.myshopify.com/admin/oauth/authorize",
42487
- tokenUrl: "https://{subdomain}.myshopify.com/admin/oauth/access_token"
43943
+ tokenUrl: "https://{subdomain}.myshopify.com/admin/oauth/access_token",
43944
+ usePKCE: true
42488
43945
  },
42489
43946
  scopes: ["read_products", "write_products", "read_orders", "write_orders", "read_customers", "write_customers", "read_inventory", "write_inventory", "read_fulfillments", "write_fulfillments"],
42490
43947
  scopeDescriptions: {
@@ -42512,6 +43969,7 @@ var allVendorTemplates = [
42512
43969
  slackTemplate,
42513
43970
  discordTemplate,
42514
43971
  telegramTemplate,
43972
+ twitterTemplate,
42515
43973
  // Development
42516
43974
  githubTemplate,
42517
43975
  gitlabTemplate,
@@ -42571,6 +44029,7 @@ var VENDOR_ICON_MAP = {
42571
44029
  discord: "discord",
42572
44030
  slack: "slack",
42573
44031
  telegram: "telegram",
44032
+ twitter: "x",
42574
44033
  "microsoft-teams": "microsoftteams",
42575
44034
  // CRM
42576
44035
  salesforce: "salesforce",
@@ -42635,6 +44094,7 @@ var FALLBACK_PLACEHOLDERS = {
42635
44094
  // Communication (trademark removed)
42636
44095
  slack: { color: "#4A154B", letter: "S" },
42637
44096
  "microsoft-teams": { color: "#6264A7", letter: "T" },
44097
+ twitter: { color: "#000000", letter: "X" },
42638
44098
  // CRM (trademark removed)
42639
44099
  salesforce: { color: "#00A1E0", letter: "S" },
42640
44100
  pipedrive: { color: "#1A1F26", letter: "P" },
@@ -43572,7 +45032,7 @@ EXAMPLES:
43572
45032
  };
43573
45033
  }
43574
45034
  try {
43575
- const stats = await fs18.stat(resolvedPath);
45035
+ const stats = await fs17.stat(resolvedPath);
43576
45036
  if (!stats.isFile()) {
43577
45037
  return {
43578
45038
  success: false,
@@ -43614,7 +45074,7 @@ EXAMPLES:
43614
45074
  } catch {
43615
45075
  }
43616
45076
  }
43617
- const content = await fs18.readFile(resolvedPath, "utf-8");
45077
+ const content = await fs17.readFile(resolvedPath, "utf-8");
43618
45078
  const allLines = content.split("\n");
43619
45079
  const totalLines = allLines.length;
43620
45080
  const startIndex = Math.max(0, offset - 1);
@@ -43723,9 +45183,9 @@ EXAMPLES:
43723
45183
  try {
43724
45184
  const parentDir = path2.dirname(resolvedPath);
43725
45185
  if (!fs19.existsSync(parentDir)) {
43726
- await fs18.mkdir(parentDir, { recursive: true });
45186
+ await fs17.mkdir(parentDir, { recursive: true });
43727
45187
  }
43728
- await fs18.writeFile(resolvedPath, content, "utf-8");
45188
+ await fs17.writeFile(resolvedPath, content, "utf-8");
43729
45189
  return {
43730
45190
  success: true,
43731
45191
  path: file_path,
@@ -43836,7 +45296,7 @@ EXAMPLES:
43836
45296
  };
43837
45297
  }
43838
45298
  try {
43839
- const content = await fs18.readFile(resolvedPath, "utf-8");
45299
+ const content = await fs17.readFile(resolvedPath, "utf-8");
43840
45300
  let occurrences = 0;
43841
45301
  let searchIndex = 0;
43842
45302
  while (true) {
@@ -43875,7 +45335,7 @@ EXAMPLES:
43875
45335
  } else {
43876
45336
  newContent = content.replace(old_string, new_string);
43877
45337
  }
43878
- await fs18.writeFile(resolvedPath, newContent, "utf-8");
45338
+ await fs17.writeFile(resolvedPath, newContent, "utf-8");
43879
45339
  const diffPreview = generateDiffPreview(old_string, new_string);
43880
45340
  return {
43881
45341
  success: true,
@@ -43931,7 +45391,7 @@ async function findFiles(dir, pattern, baseDir, config, results = [], depth = 0)
43931
45391
  return results;
43932
45392
  }
43933
45393
  try {
43934
- const entries = await fs18.readdir(dir, { withFileTypes: true });
45394
+ const entries = await fs17.readdir(dir, { withFileTypes: true });
43935
45395
  for (const entry of entries) {
43936
45396
  if (results.length >= config.maxResults) break;
43937
45397
  const fullPath = path2.join(dir, entry.name);
@@ -43945,7 +45405,7 @@ async function findFiles(dir, pattern, baseDir, config, results = [], depth = 0)
43945
45405
  } else if (entry.isFile()) {
43946
45406
  if (matchGlobPattern(pattern, relativePath)) {
43947
45407
  try {
43948
- const stats = await fs18.stat(fullPath);
45408
+ const stats = await fs17.stat(fullPath);
43949
45409
  results.push({
43950
45410
  path: relativePath,
43951
45411
  mtime: stats.mtimeMs
@@ -44082,7 +45542,7 @@ async function findFilesToSearch(dir, baseDir, config, globPattern, fileType, fi
44082
45542
  return files;
44083
45543
  }
44084
45544
  try {
44085
- const entries = await fs18.readdir(dir, { withFileTypes: true });
45545
+ const entries = await fs17.readdir(dir, { withFileTypes: true });
44086
45546
  for (const entry of entries) {
44087
45547
  const fullPath = path2.join(dir, entry.name);
44088
45548
  if (entry.isDirectory()) {
@@ -44115,7 +45575,7 @@ async function findFilesToSearch(dir, baseDir, config, globPattern, fileType, fi
44115
45575
  async function searchFile(filePath, regex, contextBefore, contextAfter) {
44116
45576
  const matches = [];
44117
45577
  try {
44118
- const content = await fs18.readFile(filePath, "utf-8");
45578
+ const content = await fs17.readFile(filePath, "utf-8");
44119
45579
  const lines = content.split("\n");
44120
45580
  for (let i = 0; i < lines.length; i++) {
44121
45581
  const line = lines[i] ?? "";
@@ -44272,7 +45732,7 @@ WHEN TO USE:
44272
45732
  };
44273
45733
  }
44274
45734
  try {
44275
- const stats = await fs18.stat(resolvedPath);
45735
+ const stats = await fs17.stat(resolvedPath);
44276
45736
  let filesToSearch;
44277
45737
  if (stats.isFile()) {
44278
45738
  filesToSearch = [resolvedPath];
@@ -44360,7 +45820,7 @@ async function listDir(dir, baseDir, config, recursive, filter, maxDepth = 3, cu
44360
45820
  return entries;
44361
45821
  }
44362
45822
  try {
44363
- const dirEntries = await fs18.readdir(dir, { withFileTypes: true });
45823
+ const dirEntries = await fs17.readdir(dir, { withFileTypes: true });
44364
45824
  for (const entry of dirEntries) {
44365
45825
  if (entries.length >= config.maxResults) break;
44366
45826
  const fullPath = path2.join(dir, entry.name);
@@ -44378,7 +45838,7 @@ async function listDir(dir, baseDir, config, recursive, filter, maxDepth = 3, cu
44378
45838
  }
44379
45839
  if (filter === "directories" && !isDir) continue;
44380
45840
  try {
44381
- const stats = await fs18.stat(fullPath);
45841
+ const stats = await fs17.stat(fullPath);
44382
45842
  const dirEntry = {
44383
45843
  name: entry.name,
44384
45844
  path: relativePath,
@@ -44481,7 +45941,7 @@ EXAMPLES:
44481
45941
  };
44482
45942
  }
44483
45943
  try {
44484
- const stats = await fs18.stat(resolvedPath);
45944
+ const stats = await fs17.stat(resolvedPath);
44485
45945
  if (!stats.isDirectory()) {
44486
45946
  return {
44487
45947
  success: false,
@@ -45804,21 +47264,53 @@ registerWebTools();
45804
47264
  init_Connector();
45805
47265
  var DEFAULT_TIMEOUT = 1e4;
45806
47266
  var DEFAULT_MAX_TIMEOUT = 3e4;
45807
- function formatConnectorEntry(c) {
47267
+ function formatConnectorEntry(c, accountId) {
45808
47268
  const parts = [];
45809
47269
  const serviceOrVendor = c.serviceType ?? c.vendor ?? void 0;
45810
47270
  if (serviceOrVendor) parts.push(`Service: ${serviceOrVendor}`);
47271
+ if (accountId) parts.push(`Account: "${accountId}"`);
45811
47272
  if (c.config.description) parts.push(c.config.description);
45812
47273
  if (c.baseURL) parts.push(`URL: ${c.baseURL}`);
47274
+ const label = accountId ? `"${c.name}" account "${accountId}"` : `"${c.name}"`;
45813
47275
  const details = parts.map((p) => ` ${p}`).join("\n");
45814
- return ` \u2022 "${c.name}" (${c.displayName})
47276
+ return ` \u2022 ${label} (${c.displayName})
45815
47277
  ${details}`;
45816
47278
  }
45817
- function generateDescription(context, maxTimeout) {
47279
+ function buildIdentityList(context) {
47280
+ const identities = context?.identities;
45818
47281
  const registry = context?.connectorRegistry ?? exports.Connector.asRegistry();
47282
+ if (identities?.length) {
47283
+ const entries = [];
47284
+ for (const id of identities) {
47285
+ try {
47286
+ const connector = registry.get(id.connector);
47287
+ entries.push(formatConnectorEntry(connector, id.accountId));
47288
+ } catch {
47289
+ entries.push(` \u2022 "${id.connector}"${id.accountId ? ` account "${id.accountId}"` : ""} \u2014 not available`);
47290
+ }
47291
+ }
47292
+ return entries.length > 0 ? entries.join("\n\n") : " No connectors registered.";
47293
+ }
45819
47294
  const connectors = registry.listAll();
45820
- const connectorList = connectors.length > 0 ? connectors.map(formatConnectorEntry).join("\n\n") : " No connectors registered.";
47295
+ return connectors.length > 0 ? connectors.map((c) => formatConnectorEntry(c)).join("\n\n") : " No connectors registered.";
47296
+ }
47297
+ function hasAccountIds(context) {
47298
+ return !!context?.identities?.some((id) => id.accountId);
47299
+ }
47300
+ function generateDescription(context, maxTimeout) {
47301
+ const connectorList = buildIdentityList(context);
47302
+ const showAccountId = hasAccountIds(context);
45821
47303
  const timeoutSec = Math.round(maxTimeout / 1e3);
47304
+ const accountIdParam = showAccountId ? `
47305
+ \u2022 accountId (optional): Account alias for multi-account connectors.
47306
+ Required when a connector has multiple accounts (see list below).
47307
+ Example: authenticatedFetch('/v1.0/me', {}, 'microsoft', 'work')` : "";
47308
+ const accountIdExamples = showAccountId ? `
47309
+ // Multi-account: specify accountId for connectors with multiple accounts
47310
+ const resp = await authenticatedFetch('/v1.0/me', { method: 'GET' }, 'microsoft', 'work');
47311
+ const profile = await resp.json();
47312
+ output = profile;
47313
+ ` : "";
45822
47314
  return `Execute JavaScript code in a secure sandbox with authenticated API access to external services.
45823
47315
 
45824
47316
  Use this tool when you need to:
@@ -45829,7 +47321,7 @@ Use this tool when you need to:
45829
47321
 
45830
47322
  SANDBOX API:
45831
47323
 
45832
- 1. authenticatedFetch(url, options, connectorName)
47324
+ 1. authenticatedFetch(url, options, connectorName${showAccountId ? ", accountId?" : ""})
45833
47325
  Makes authenticated HTTP requests using the connector's credentials.
45834
47326
  The current user's identity (userId) is automatically included \u2014 no need to pass it.
45835
47327
  Auth headers are added automatically \u2014 DO NOT set Authorization header manually.
@@ -45840,7 +47332,7 @@ SANDBOX API:
45840
47332
  - Relative: "/user/repos" (resolved against connector's base URL)
45841
47333
  \u2022 options: Standard fetch options { method, headers, body }
45842
47334
  - For POST/PUT: set body to JSON.stringify(data) and headers to { 'Content-Type': 'application/json' }
45843
- \u2022 connectorName: Name of a registered connector (see list below)
47335
+ \u2022 connectorName: Name of a registered connector (see list below)${accountIdParam}
45844
47336
 
45845
47337
  Returns: Promise<Response>
45846
47338
  \u2022 response.ok \u2014 true if status 200-299
@@ -45876,7 +47368,7 @@ const resp = await authenticatedFetch('/chat.postMessage', {
45876
47368
  body: JSON.stringify({ channel: '#general', text: 'Hello!' })
45877
47369
  }, 'slack');
45878
47370
  output = await resp.json();
45879
-
47371
+ ${accountIdExamples}
45880
47372
  // Data processing (no API needed)
45881
47373
  const items = input.data;
45882
47374
  output = items.filter(i => i.score > 0.8).sort((a, b) => b.score - a.score);
@@ -45964,9 +47456,10 @@ async function executeInVM(code, input, timeout, logs, userId, registry) {
45964
47456
  },
45965
47457
  // Authenticated fetch — userId auto-injected from ToolContext.
45966
47458
  // Only connectors visible in the scoped registry are accessible.
45967
- authenticatedFetch: (url2, options, connectorName) => {
47459
+ // Optional 4th param accountId for multi-account OAuth identities.
47460
+ authenticatedFetch: (url2, options, connectorName, accountId) => {
45968
47461
  registry.get(connectorName);
45969
- return authenticatedFetch(url2, options, connectorName, userId);
47462
+ return authenticatedFetch(url2, options, connectorName, userId, accountId);
45970
47463
  },
45971
47464
  // Standard fetch (no auth)
45972
47465
  fetch: globalThis.fetch,
@@ -46654,7 +48147,8 @@ async function githubFetch(connector, endpoint, options) {
46654
48147
  headers,
46655
48148
  body: options?.body ? JSON.stringify(options.body) : void 0
46656
48149
  },
46657
- options?.userId
48150
+ options?.userId,
48151
+ options?.accountId
46658
48152
  );
46659
48153
  const text = await response.text();
46660
48154
  let data;
@@ -46736,6 +48230,7 @@ EXAMPLES:
46736
48230
  },
46737
48231
  execute: async (args, context) => {
46738
48232
  const effectiveUserId = context?.userId ?? userId;
48233
+ const effectiveAccountId = context?.accountId;
46739
48234
  const resolved = resolveRepository(args.repository, connector);
46740
48235
  if (!resolved.success) {
46741
48236
  return { success: false, error: resolved.error };
@@ -46747,7 +48242,7 @@ EXAMPLES:
46747
48242
  const repoInfo = await githubFetch(
46748
48243
  connector,
46749
48244
  `/repos/${owner}/${repo}`,
46750
- { userId: effectiveUserId }
48245
+ { userId: effectiveUserId, accountId: effectiveAccountId }
46751
48246
  );
46752
48247
  ref = repoInfo.default_branch;
46753
48248
  }
@@ -46847,6 +48342,7 @@ EXAMPLES:
46847
48342
  },
46848
48343
  execute: async (args, context) => {
46849
48344
  const effectiveUserId = context?.userId ?? userId;
48345
+ const effectiveAccountId = context?.accountId;
46850
48346
  const resolved = resolveRepository(args.repository, connector);
46851
48347
  if (!resolved.success) {
46852
48348
  return { success: false, error: resolved.error };
@@ -46864,6 +48360,7 @@ EXAMPLES:
46864
48360
  `/search/code`,
46865
48361
  {
46866
48362
  userId: effectiveUserId,
48363
+ accountId: effectiveAccountId,
46867
48364
  // Request text-match fragments
46868
48365
  accept: "application/vnd.github.text-match+json",
46869
48366
  queryParams: { q, per_page: perPage }
@@ -46952,6 +48449,7 @@ NOTE: Files larger than 1MB are fetched via the Git Blob API. Very large files (
46952
48449
  },
46953
48450
  execute: async (args, context) => {
46954
48451
  const effectiveUserId = context?.userId ?? userId;
48452
+ const effectiveAccountId = context?.accountId;
46955
48453
  const resolved = resolveRepository(args.repository, connector);
46956
48454
  if (!resolved.success) {
46957
48455
  return { success: false, error: resolved.error };
@@ -46965,7 +48463,7 @@ NOTE: Files larger than 1MB are fetched via the Git Blob API. Very large files (
46965
48463
  const contentResp = await githubFetch(
46966
48464
  connector,
46967
48465
  `/repos/${owner}/${repo}/contents/${args.path}${refParam}`,
46968
- { userId: effectiveUserId }
48466
+ { userId: effectiveUserId, accountId: effectiveAccountId }
46969
48467
  );
46970
48468
  if (contentResp.type !== "file") {
46971
48469
  return {
@@ -46982,7 +48480,7 @@ NOTE: Files larger than 1MB are fetched via the Git Blob API. Very large files (
46982
48480
  const blob = await githubFetch(
46983
48481
  connector,
46984
48482
  contentResp.git_url,
46985
- { userId: effectiveUserId }
48483
+ { userId: effectiveUserId, accountId: effectiveAccountId }
46986
48484
  );
46987
48485
  fileContent = Buffer.from(blob.content, "base64").toString("utf-8");
46988
48486
  fileSize = blob.size;
@@ -47071,6 +48569,7 @@ EXAMPLES:
47071
48569
  },
47072
48570
  execute: async (args, context) => {
47073
48571
  const effectiveUserId = context?.userId ?? userId;
48572
+ const effectiveAccountId = context?.accountId;
47074
48573
  const resolved = resolveRepository(args.repository, connector);
47075
48574
  if (!resolved.success) {
47076
48575
  return { success: false, error: resolved.error };
@@ -47080,7 +48579,7 @@ EXAMPLES:
47080
48579
  const pr = await githubFetch(
47081
48580
  connector,
47082
48581
  `/repos/${owner}/${repo}/pulls/${args.pull_number}`,
47083
- { userId: effectiveUserId }
48582
+ { userId: effectiveUserId, accountId: effectiveAccountId }
47084
48583
  );
47085
48584
  return {
47086
48585
  success: true,
@@ -47158,6 +48657,7 @@ NOTE: Very large diffs may be truncated by GitHub. Patch content may be absent f
47158
48657
  },
47159
48658
  execute: async (args, context) => {
47160
48659
  const effectiveUserId = context?.userId ?? userId;
48660
+ const effectiveAccountId = context?.accountId;
47161
48661
  const resolved = resolveRepository(args.repository, connector);
47162
48662
  if (!resolved.success) {
47163
48663
  return { success: false, error: resolved.error };
@@ -47169,6 +48669,7 @@ NOTE: Very large diffs may be truncated by GitHub. Patch content may be absent f
47169
48669
  `/repos/${owner}/${repo}/pulls/${args.pull_number}/files`,
47170
48670
  {
47171
48671
  userId: effectiveUserId,
48672
+ accountId: effectiveAccountId,
47172
48673
  queryParams: { per_page: 100 }
47173
48674
  }
47174
48675
  );
@@ -47241,6 +48742,7 @@ EXAMPLES:
47241
48742
  },
47242
48743
  execute: async (args, context) => {
47243
48744
  const effectiveUserId = context?.userId ?? userId;
48745
+ const effectiveAccountId = context?.accountId;
47244
48746
  const resolved = resolveRepository(args.repository, connector);
47245
48747
  if (!resolved.success) {
47246
48748
  return { success: false, error: resolved.error };
@@ -47248,7 +48750,7 @@ EXAMPLES:
47248
48750
  const { owner, repo } = resolved.repo;
47249
48751
  try {
47250
48752
  const basePath = `/repos/${owner}/${repo}`;
47251
- const queryOpts = { userId: effectiveUserId, queryParams: { per_page: 100 } };
48753
+ const queryOpts = { userId: effectiveUserId, accountId: effectiveAccountId, queryParams: { per_page: 100 } };
47252
48754
  const [reviewComments, reviews, issueComments] = await Promise.all([
47253
48755
  githubFetch(
47254
48756
  connector,
@@ -47377,6 +48879,7 @@ EXAMPLES:
47377
48879
  },
47378
48880
  execute: async (args, context) => {
47379
48881
  const effectiveUserId = context?.userId ?? userId;
48882
+ const effectiveAccountId = context?.accountId;
47380
48883
  const resolved = resolveRepository(args.repository, connector);
47381
48884
  if (!resolved.success) {
47382
48885
  return { success: false, error: resolved.error };
@@ -47389,6 +48892,7 @@ EXAMPLES:
47389
48892
  {
47390
48893
  method: "POST",
47391
48894
  userId: effectiveUserId,
48895
+ accountId: effectiveAccountId,
47392
48896
  body: {
47393
48897
  title: args.title,
47394
48898
  body: args.body,
@@ -47480,7 +48984,8 @@ async function microsoftFetch(connector, endpoint, options) {
47480
48984
  headers,
47481
48985
  body: options?.body ? JSON.stringify(options.body) : void 0
47482
48986
  },
47483
- options?.userId
48987
+ options?.userId,
48988
+ options?.accountId
47484
48989
  );
47485
48990
  const text = await response.text();
47486
48991
  if (!response.ok) {
@@ -47533,7 +49038,7 @@ function isTeamsMeetingUrl(input) {
47533
49038
  return false;
47534
49039
  }
47535
49040
  }
47536
- async function resolveMeetingId(connector, input, prefix, effectiveUserId) {
49041
+ async function resolveMeetingId(connector, input, prefix, effectiveUserId, effectiveAccountId) {
47537
49042
  if (!input || input.trim().length === 0) {
47538
49043
  throw new Error("Meeting ID cannot be empty");
47539
49044
  }
@@ -47546,6 +49051,7 @@ async function resolveMeetingId(connector, input, prefix, effectiveUserId) {
47546
49051
  `${prefix}/onlineMeetings`,
47547
49052
  {
47548
49053
  userId: effectiveUserId,
49054
+ accountId: effectiveAccountId,
47549
49055
  queryParams: { "$filter": `JoinWebUrl eq '${trimmed}'` }
47550
49056
  }
47551
49057
  );
@@ -47624,13 +49130,14 @@ EXAMPLES:
47624
49130
  },
47625
49131
  execute: async (args, context) => {
47626
49132
  const effectiveUserId = context?.userId ?? userId;
49133
+ const effectiveAccountId = context?.accountId;
47627
49134
  try {
47628
49135
  const prefix = getUserPathPrefix(connector, args.targetUser);
47629
49136
  if (args.replyToMessageId) {
47630
49137
  const replyDraft = await microsoftFetch(
47631
49138
  connector,
47632
49139
  `${prefix}/messages/${args.replyToMessageId}/createReply`,
47633
- { method: "POST", userId: effectiveUserId, body: {} }
49140
+ { method: "POST", userId: effectiveUserId, accountId: effectiveAccountId, body: {} }
47634
49141
  );
47635
49142
  const updated = await microsoftFetch(
47636
49143
  connector,
@@ -47638,6 +49145,7 @@ EXAMPLES:
47638
49145
  {
47639
49146
  method: "PATCH",
47640
49147
  userId: effectiveUserId,
49148
+ accountId: effectiveAccountId,
47641
49149
  body: {
47642
49150
  subject: args.subject,
47643
49151
  body: { contentType: "HTML", content: args.body },
@@ -47658,6 +49166,7 @@ EXAMPLES:
47658
49166
  {
47659
49167
  method: "POST",
47660
49168
  userId: effectiveUserId,
49169
+ accountId: effectiveAccountId,
47661
49170
  body: {
47662
49171
  isDraft: true,
47663
49172
  subject: args.subject,
@@ -47746,6 +49255,7 @@ EXAMPLES:
47746
49255
  },
47747
49256
  execute: async (args, context) => {
47748
49257
  const effectiveUserId = context?.userId ?? userId;
49258
+ const effectiveAccountId = context?.accountId;
47749
49259
  try {
47750
49260
  const prefix = getUserPathPrefix(connector, args.targetUser);
47751
49261
  if (args.replyToMessageId) {
@@ -47755,6 +49265,7 @@ EXAMPLES:
47755
49265
  {
47756
49266
  method: "POST",
47757
49267
  userId: effectiveUserId,
49268
+ accountId: effectiveAccountId,
47758
49269
  body: {
47759
49270
  message: {
47760
49271
  toRecipients: formatRecipients(args.to),
@@ -47771,6 +49282,7 @@ EXAMPLES:
47771
49282
  {
47772
49283
  method: "POST",
47773
49284
  userId: effectiveUserId,
49285
+ accountId: effectiveAccountId,
47774
49286
  body: {
47775
49287
  message: {
47776
49288
  subject: args.subject,
@@ -47870,6 +49382,7 @@ EXAMPLES:
47870
49382
  },
47871
49383
  execute: async (args, context) => {
47872
49384
  const effectiveUserId = context?.userId ?? userId;
49385
+ const effectiveAccountId = context?.accountId;
47873
49386
  try {
47874
49387
  const prefix = getUserPathPrefix(connector, args.targetUser);
47875
49388
  const tz = args.timeZone ?? "UTC";
@@ -47892,7 +49405,7 @@ EXAMPLES:
47892
49405
  const event = await microsoftFetch(
47893
49406
  connector,
47894
49407
  `${prefix}/events`,
47895
- { method: "POST", userId: effectiveUserId, body: eventBody }
49408
+ { method: "POST", userId: effectiveUserId, accountId: effectiveAccountId, body: eventBody }
47896
49409
  );
47897
49410
  return {
47898
49411
  success: true,
@@ -47996,6 +49509,7 @@ EXAMPLES:
47996
49509
  },
47997
49510
  execute: async (args, context) => {
47998
49511
  const effectiveUserId = context?.userId ?? userId;
49512
+ const effectiveAccountId = context?.accountId;
47999
49513
  try {
48000
49514
  const prefix = getUserPathPrefix(connector, args.targetUser);
48001
49515
  const tz = args.timeZone ?? "UTC";
@@ -48019,7 +49533,7 @@ EXAMPLES:
48019
49533
  const event = await microsoftFetch(
48020
49534
  connector,
48021
49535
  `${prefix}/events/${args.eventId}`,
48022
- { method: "PATCH", userId: effectiveUserId, body: patchBody }
49536
+ { method: "PATCH", userId: effectiveUserId, accountId: effectiveAccountId, body: patchBody }
48023
49537
  );
48024
49538
  return {
48025
49539
  success: true,
@@ -48092,14 +49606,15 @@ EXAMPLES:
48092
49606
  },
48093
49607
  execute: async (args, context) => {
48094
49608
  const effectiveUserId = context?.userId ?? userId;
49609
+ const effectiveAccountId = context?.accountId;
48095
49610
  try {
48096
49611
  const prefix = getUserPathPrefix(connector, args.targetUser);
48097
- const resolved = await resolveMeetingId(connector, args.meetingId, prefix, effectiveUserId);
49612
+ const resolved = await resolveMeetingId(connector, args.meetingId, prefix, effectiveUserId, effectiveAccountId);
48098
49613
  const meetingId = resolved.meetingId;
48099
49614
  const transcriptList = await microsoftFetch(
48100
49615
  connector,
48101
49616
  `${prefix}/onlineMeetings/${meetingId}/transcripts`,
48102
- { userId: effectiveUserId }
49617
+ { userId: effectiveUserId, accountId: effectiveAccountId }
48103
49618
  );
48104
49619
  if (!transcriptList.value || transcriptList.value.length === 0) {
48105
49620
  return {
@@ -48112,7 +49627,8 @@ EXAMPLES:
48112
49627
  const response = await connector.fetch(
48113
49628
  contentUrl + "?$format=text/vtt",
48114
49629
  { method: "GET", headers: { "Accept": "text/vtt" } },
48115
- effectiveUserId
49630
+ effectiveUserId,
49631
+ effectiveAccountId
48116
49632
  );
48117
49633
  if (!response.ok) {
48118
49634
  const errorText = await response.text();
@@ -48204,6 +49720,7 @@ EXAMPLES:
48204
49720
  },
48205
49721
  execute: async (args, context) => {
48206
49722
  const effectiveUserId = context?.userId ?? userId;
49723
+ const effectiveAccountId = context?.accountId;
48207
49724
  try {
48208
49725
  const prefix = getUserPathPrefix(connector, args.targetUser);
48209
49726
  const tz = args.timeZone ?? "UTC";
@@ -48213,6 +49730,7 @@ EXAMPLES:
48213
49730
  {
48214
49731
  method: "POST",
48215
49732
  userId: effectiveUserId,
49733
+ accountId: effectiveAccountId,
48216
49734
  body: {
48217
49735
  attendees: formatAttendees(args.attendees),
48218
49736
  timeConstraint: {
@@ -49141,27 +50659,42 @@ var customToolDelete = createCustomToolDelete();
49141
50659
 
49142
50660
  // src/tools/custom-tools/sandboxDescription.ts
49143
50661
  init_Connector();
49144
- function formatConnectorEntry2(c) {
50662
+ function formatConnectorEntry2(c, accountId) {
49145
50663
  const parts = [];
49146
50664
  const serviceOrVendor = c.serviceType ?? c.vendor ?? void 0;
49147
50665
  if (serviceOrVendor) parts.push(`Service: ${serviceOrVendor}`);
50666
+ if (accountId) parts.push(`Account: "${accountId}"`);
49148
50667
  if (c.config.description) parts.push(c.config.description);
49149
50668
  if (c.baseURL) parts.push(`URL: ${c.baseURL}`);
50669
+ const label = accountId ? `"${c.name}" account "${accountId}"` : `"${c.name}"`;
49150
50670
  const details = parts.map((p) => ` ${p}`).join("\n");
49151
- return ` \u2022 "${c.name}" (${c.displayName})
50671
+ return ` \u2022 ${label} (${c.displayName})
49152
50672
  ${details}`;
49153
50673
  }
49154
50674
  function buildConnectorList(context) {
50675
+ const identities = context?.identities;
49155
50676
  const registry = context?.connectorRegistry ?? exports.Connector.asRegistry();
50677
+ if (identities?.length) {
50678
+ const entries = [];
50679
+ for (const id of identities) {
50680
+ try {
50681
+ const connector = registry.get(id.connector);
50682
+ entries.push(formatConnectorEntry2(connector, id.accountId));
50683
+ } catch {
50684
+ entries.push(` \u2022 "${id.connector}"${id.accountId ? ` account "${id.accountId}"` : ""} \u2014 not available`);
50685
+ }
50686
+ }
50687
+ return entries.length > 0 ? entries.join("\n\n") : " No connectors registered.";
50688
+ }
49156
50689
  const connectors = registry.listAll();
49157
50690
  if (connectors.length === 0) {
49158
50691
  return " No connectors registered.";
49159
50692
  }
49160
- return connectors.map(formatConnectorEntry2).join("\n\n");
50693
+ return connectors.map((c) => formatConnectorEntry2(c)).join("\n\n");
49161
50694
  }
49162
50695
  var SANDBOX_API_REFERENCE = `SANDBOX API (available inside custom tool code):
49163
50696
 
49164
- 1. authenticatedFetch(url, options, connectorName)
50697
+ 1. authenticatedFetch(url, options, connectorName, accountId?)
49165
50698
  Makes authenticated HTTP requests using the connector's credentials.
49166
50699
  Auth headers are added automatically \u2014 DO NOT set Authorization header manually.
49167
50700
 
@@ -49172,6 +50705,7 @@ var SANDBOX_API_REFERENCE = `SANDBOX API (available inside custom tool code):
49172
50705
  \u2022 options: Standard fetch options { method, headers, body }
49173
50706
  - For POST/PUT: set body to JSON.stringify(data) and headers to { 'Content-Type': 'application/json' }
49174
50707
  \u2022 connectorName: Name of a registered connector (see REGISTERED CONNECTORS below)
50708
+ \u2022 accountId (optional): Account alias for multi-account connectors (e.g., 'work', 'personal')
49175
50709
 
49176
50710
  Returns: Promise<Response>
49177
50711
  \u2022 response.ok \u2014 true if status 200-299
@@ -50343,6 +51877,7 @@ exports.ProviderError = ProviderError;
50343
51877
  exports.ProviderErrorMapper = ProviderErrorMapper;
50344
51878
  exports.ProviderNotFoundError = ProviderNotFoundError;
50345
51879
  exports.ProviderRateLimitError = ProviderRateLimitError;
51880
+ exports.ROUTINE_KEYS = ROUTINE_KEYS;
50346
51881
  exports.RapidAPIProvider = RapidAPIProvider;
50347
51882
  exports.RateLimitError = RateLimitError;
50348
51883
  exports.SERVICE_DEFINITIONS = SERVICE_DEFINITIONS;
@@ -50500,6 +52035,7 @@ exports.findConnectorByServiceTypes = findConnectorByServiceTypes;
50500
52035
  exports.forPlan = forPlan;
50501
52036
  exports.forTasks = forTasks;
50502
52037
  exports.formatAttendees = formatAttendees;
52038
+ exports.formatPluginDisplayName = formatPluginDisplayName;
50503
52039
  exports.formatRecipients = formatRecipients;
50504
52040
  exports.generateEncryptionKey = generateEncryptionKey;
50505
52041
  exports.generateSimplePlan = generateSimplePlan;
@@ -50568,6 +52104,8 @@ exports.isErrorEvent = isErrorEvent;
50568
52104
  exports.isExcludedExtension = isExcludedExtension;
50569
52105
  exports.isKnownService = isKnownService;
50570
52106
  exports.isOutputTextDelta = isOutputTextDelta;
52107
+ exports.isReasoningDelta = isReasoningDelta;
52108
+ exports.isReasoningDone = isReasoningDone;
50571
52109
  exports.isResponseComplete = isResponseComplete;
50572
52110
  exports.isSimpleScope = isSimpleScope;
50573
52111
  exports.isStreamEvent = isStreamEvent;
@@ -50604,6 +52142,7 @@ exports.resolveMaxContextTokens = resolveMaxContextTokens;
50604
52142
  exports.resolveMeetingId = resolveMeetingId;
50605
52143
  exports.resolveModelCapabilities = resolveModelCapabilities;
50606
52144
  exports.resolveRepository = resolveRepository;
52145
+ exports.resolveTemplates = resolveTemplates;
50607
52146
  exports.retryWithBackoff = retryWithBackoff;
50608
52147
  exports.sanitizeToolName = sanitizeToolName;
50609
52148
  exports.scopeEquals = scopeEquals;