@agent-team-foundation/first-tree-hub 0.12.5 → 0.12.6

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.
@@ -576,12 +576,22 @@ const serverConfigSchema = defineConfig({
576
576
  }),
577
577
  githubTokenRepos: field(z.string().optional(), { env: "FIRST_TREE_HUB_CONTEXT_TREE_GITHUB_TOKEN_REPOS" })
578
578
  }),
579
- oauth: optional({ github: optional({
580
- clientId: field(z.string(), { env: "FIRST_TREE_HUB_GITHUB_OAUTH_CLIENT_ID" }),
581
- clientSecret: field(z.string(), {
582
- env: "FIRST_TREE_HUB_GITHUB_OAUTH_CLIENT_SECRET",
579
+ oauth: optional({ githubApp: optional({
580
+ appId: field(z.string().min(1), { env: "FIRST_TREE_HUB_GITHUB_APP_ID" }),
581
+ clientId: field(z.string().min(1), { env: "FIRST_TREE_HUB_GITHUB_APP_CLIENT_ID" }),
582
+ clientSecret: field(z.string().min(1), {
583
+ env: "FIRST_TREE_HUB_GITHUB_APP_CLIENT_SECRET",
583
584
  secret: true
584
- })
585
+ }),
586
+ privateKeyPem: field(z.string().min(1), {
587
+ env: "FIRST_TREE_HUB_GITHUB_APP_PRIVATE_KEY",
588
+ secret: true
589
+ }),
590
+ webhookSecret: field(z.string().min(1), {
591
+ env: "FIRST_TREE_HUB_GITHUB_APP_WEBHOOK_SECRET",
592
+ secret: true
593
+ }),
594
+ slug: field(z.string().min(1).optional(), { env: "FIRST_TREE_HUB_GITHUB_APP_SLUG" })
585
595
  }) }),
586
596
  cors: optional({ origin: field(z.string(), { env: "FIRST_TREE_HUB_CORS_ORIGIN" }) }),
587
597
  trustProxy: field(z.boolean().default(false), { env: "FIRST_TREE_HUB_TRUST_PROXY" }),
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import "../observability-BAScT_5S-BcW9HgkG.mjs";
3
- import { $ as formatStaleReason, A as checkDocker, B as isServiceSupported, C as createApiNameResolver, D as checkBackgroundService, E as checkAgentConfigs, F as checkWebSocket, H as restartClientService, I as printResults, J as stopPostgres, L as reconcileAgentConfigs, M as checkServerConfig, N as checkServerHealth, O as checkClientConfig, P as checkServerReachable, Q as findStaleAliases, R as getClientServiceStatus, S as runHomeMigration, T as runMigrations, U as startClientService, W as stopClientService, X as handleClientOrgMismatch, Y as ClientRuntime, _ as formatCheckReport, a as declineUpdate, at as fail, b as onboardCreate, c as detectInstallMode, ct as ClientUserMismatchError, d as startServer, dt as SessionRegistry, et as removeLocalAgent, f as reconcileLocalRuntimeProviders, ft as cleanWorkspaces, g as promptMissingFields, h as promptAddAgent, ht as configureClientLoggerForService, i as createExecuteUpdate, it as resolveSenderName, j as checkNodeVersion, k as checkDatabase, l as fetchLatestVersion, lt as FirstTreeHubSDK, m as isInteractive, mt as applyClientLoggerConfig, o as promptUpdate, ot as success, p as uploadClientCapabilities, pt as probeCapabilities, r as registerSaaSConnectCommand, rt as resolveReplyToFromEnv, s as PACKAGE_NAME, st as ClientOrgMismatchError, tt as createOwner, u as installGlobalLatest, ut as SdkError, v as loadOnboardState, w as migrateLocalAgentDirs, x as saveOnboardState, y as onboardCheck, z as installClientService } from "../saas-connect-DYjvx5yr.mjs";
3
+ import { $ as formatStaleReason, A as checkDocker, B as isServiceSupported, C as createApiNameResolver, D as checkBackgroundService, E as checkAgentConfigs, F as checkWebSocket, H as restartClientService, I as printResults, J as stopPostgres, L as reconcileAgentConfigs, M as checkServerConfig, N as checkServerHealth, O as checkClientConfig, P as checkServerReachable, Q as findStaleAliases, R as getClientServiceStatus, S as runHomeMigration, T as runMigrations, U as startClientService, W as stopClientService, X as handleClientOrgMismatch, Y as ClientRuntime, _ as formatCheckReport, a as declineUpdate, at as fail, b as onboardCreate, c as detectInstallMode, ct as ClientUserMismatchError, d as startServer, dt as SessionRegistry, et as removeLocalAgent, f as reconcileLocalRuntimeProviders, ft as cleanWorkspaces, g as promptMissingFields, h as promptAddAgent, ht as configureClientLoggerForService, i as createExecuteUpdate, it as resolveSenderName, j as checkNodeVersion, k as checkDatabase, l as fetchLatestVersion, lt as FirstTreeHubSDK, m as isInteractive, mt as applyClientLoggerConfig, o as promptUpdate, ot as success, p as uploadClientCapabilities, pt as probeCapabilities, r as registerSaaSConnectCommand, rt as resolveReplyToFromEnv, s as PACKAGE_NAME, st as ClientOrgMismatchError, tt as createOwner, u as installGlobalLatest, ut as SdkError, v as loadOnboardState, w as migrateLocalAgentDirs, x as saveOnboardState, y as onboardCheck, z as installClientService } from "../saas-connect-RCN8zL5e.mjs";
4
4
  import "../logger-core-BTmvdflj-DjW8FM4T.mjs";
5
- import { C as resetConfigMeta, E as setConfigValue, S as resetConfig, T as serverConfigSchema, _ as getConfigValue, a as ensureFreshAdminToken, c as resolveServerUrl, d as DEFAULT_CONFIG_DIR, f as DEFAULT_DATA_DIR, h as clientConfigSchema, i as ensureFreshAccessToken, l as saveAgentConfig, m as agentConfigSchema, o as loadCredentials, p as DEFAULT_HOME_DIR, u as saveCredentials, v as initConfig, w as resolveConfigReadonly, x as readConfigFile, y as loadAgents } from "../bootstrap-C_K2CKXC.mjs";
5
+ import { C as resetConfigMeta, E as setConfigValue, S as resetConfig, T as serverConfigSchema, _ as getConfigValue, a as ensureFreshAdminToken, c as resolveServerUrl, d as DEFAULT_CONFIG_DIR, f as DEFAULT_DATA_DIR, h as clientConfigSchema, i as ensureFreshAccessToken, l as saveAgentConfig, m as agentConfigSchema, o as loadCredentials, p as DEFAULT_HOME_DIR, u as saveCredentials, v as initConfig, w as resolveConfigReadonly, x as readConfigFile, y as loadAgents } from "../bootstrap-BCZC1ki6.mjs";
6
6
  import { a as print, n as CLI_USER_AGENT, o as setJsonMode, r as COMMAND_VERSION, t as cliFetch } from "../cli-fetch--tiwKm5S.mjs";
7
- import "../dist-BwPlBZWi.mjs";
8
- import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-CKGzIamp.mjs";
7
+ import "../dist-xP6NpdMp.mjs";
8
+ import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-CsfadBKa.mjs";
9
9
  import "../errors-CF5evtJt-B0NTIVPt.mjs";
10
10
  import "../src-DNBS5Yjj.mjs";
11
- import "../client-DL5vHhvQ-CnYGq2x-.mjs";
11
+ import "../client-B89AKi3Q-DAyGdQSq.mjs";
12
12
  import "../invitation-Bg0TRiyx-BsZH4GCS.mjs";
13
13
  import { join } from "node:path";
14
14
  import { existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
@@ -1672,13 +1672,13 @@ function isSecretField(schema, dotPath) {
1672
1672
  //#region src/commands/onboard.ts
1673
1673
  async function promptMissing(args) {
1674
1674
  if (!args.server) try {
1675
- const { resolveServerUrl } = await import("../bootstrap-C_K2CKXC.mjs").then((n) => n.r);
1675
+ const { resolveServerUrl } = await import("../bootstrap-BCZC1ki6.mjs").then((n) => n.r);
1676
1676
  resolveServerUrl();
1677
1677
  } catch {
1678
1678
  args.server = await input({ message: "Hub server URL:" });
1679
1679
  saveOnboardState(args);
1680
1680
  }
1681
- const { loadCredentials } = await import("../bootstrap-C_K2CKXC.mjs").then((n) => n.r);
1681
+ const { loadCredentials } = await import("../bootstrap-BCZC1ki6.mjs").then((n) => n.r);
1682
1682
  if (!loadCredentials()) throw new Error("No saved credentials. Run `first-tree-hub client connect <server-url>` before onboarding.");
1683
1683
  if (!args.id) {
1684
1684
  args.id = await input({ message: "Agent ID:" });
@@ -1,10 +1,10 @@
1
1
  import { O as withSpan, f as messageAttrs, s as createLogger } from "./observability-BAScT_5S-BcW9HgkG.mjs";
2
- import { X as questionAnswerMessageContentSchema, Z as questionMessageContentSchema, _ as clientCapabilitiesSchema, a as AGENT_VISIBILITY, i as AGENT_STATUSES, j as extractMentions, nt as scanMentionTokens } from "./dist-BwPlBZWi.mjs";
2
+ import { $ as questionMessageContentSchema, M as extractMentions, O as defaultParticipantMode, Q as questionAnswerMessageContentSchema, _ as clientCapabilitiesSchema, a as AGENT_VISIBILITY, h as agentTypeSchema, i as AGENT_STATUSES, it as scanMentionTokens } from "./dist-xP6NpdMp.mjs";
3
3
  import { a as ConflictError, i as ClientUserMismatchError, l as organizations, n as BadRequestError, o as ForbiddenError, s as NotFoundError, u as users } from "./errors-CF5evtJt-B0NTIVPt.mjs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { and, desc, eq, inArray, isNotNull, lt, ne, or, sql } from "drizzle-orm";
6
6
  import { bigserial, boolean, index, integer, jsonb, pgTable, primaryKey, text, timestamp, unique } from "drizzle-orm/pg-core";
7
- //#region ../server/dist/client-DL5vHhvQ.mjs
7
+ //#region ../server/dist/client-B89AKi3Q.mjs
8
8
  /**
9
9
  * Client connections. A client is a single SDK process (AgentRuntime) that may
10
10
  * host multiple agents. From the unified-user-token milestone on, a client is
@@ -185,6 +185,140 @@ function invalidateChatAudience(chatId) {
185
185
  cache.delete(chatId);
186
186
  }
187
187
  /**
188
+ * Single source of truth for writing `chat_participants`.
189
+ *
190
+ * **This is the ONLY place in the codebase that may `INSERT` into the
191
+ * `chat_participants` table.** Do not call `tx.insert(chatParticipants)`
192
+ * or `db.insert(chatParticipants)` from anywhere else. The original bug
193
+ * (docs/chat-participant-mode-fix-design.md §1.1) was caused by ten
194
+ * scattered insert sites each re-deriving the `mode` rule, several of
195
+ * which violated `group + non-human ⇒ mention_only`. Re-introducing a
196
+ * second writer reopens that hole — please don't.
197
+ *
198
+ * Test fixtures under `src/__tests__/` that deliberately seed
199
+ * pathological rows (e.g. cross-org pollution tests) may bypass this
200
+ * rule; they are setting up "what bad data looks like" rather than
201
+ * exercising the production write path.
202
+ *
203
+ * All callers that need to add a participant — `createChat`, `addParticipant`,
204
+ * `ensureParticipant`, `joinChat`, `createMeChat`, `addMeChatParticipants`,
205
+ * `findOrCreateDirectChat`, `findOrCreateChatForChannel`, `joinAsParticipant`,
206
+ * … — go through `addChatParticipants`. The function performs ONE round-trip
207
+ * to read `chats.type` + every involved `agents.type`, runs each row through
208
+ * `defaultParticipantMode`, and inserts the result. `agents.type` is parsed
209
+ * through the shared `agentTypeSchema` so schema drift surfaces loudly
210
+ * instead of silently coercing to a default.
211
+ *
212
+ * `changeChatType` complements it on the type-flip path: when a `direct`
213
+ * chat is being upgraded to `group` by the very next participant insert, the
214
+ * existing non-human rows must be re-graded to `mention_only`. Callers that
215
+ * trigger an upgrade are expected to invoke `changeChatType` BEFORE
216
+ * `addChatParticipants`, inside the same transaction, so the new row picks
217
+ * up the post-upgrade `chats.type` and existing rows get re-graded together.
218
+ */
219
+ /**
220
+ * Insert participant rows whose `mode` is derived from `(chats.type, agents.type)`.
221
+ *
222
+ * Reads:
223
+ * - `chats.type` for the target chat (NotFoundError on missing)
224
+ * - `agents.type` for every requested participant (BadRequestError on missing)
225
+ *
226
+ * Mode derivation:
227
+ * - for each row, `peerAgentTypes` is the type of every OTHER participant
228
+ * being inserted in the same call PLUS every EXISTING participant of
229
+ * the chat. This matters only for `direct` chats; the helper ignores
230
+ * it for `group` / `thread`.
231
+ *
232
+ * Writes one INSERT (multi-row) per call.
233
+ *
234
+ * No watcher / audience-cache side effects — the caller owns those, since
235
+ * different entrypoints have different surrounding work (state-carry, watcher
236
+ * recompute, audience invalidation). Keeping this module side-effect-free
237
+ * makes it testable from any tx context.
238
+ */
239
+ async function addChatParticipants(tx, chatId, participants, options = {}) {
240
+ if (participants.length === 0) return;
241
+ const [chat] = await tx.select({ type: chats.type }).from(chats).where(eq(chats.id, chatId)).limit(1);
242
+ if (!chat) throw new NotFoundError(`Chat "${chatId}" not found`);
243
+ if (chat.type !== "direct" && chat.type !== "group" && chat.type !== "thread") throw new Error(`Unexpected chat type "${chat.type}" for chat "${chatId}"`);
244
+ const chatType = chat.type;
245
+ const agentIds = participants.map((p) => p.agentId);
246
+ const agentRows = await tx.select({
247
+ uuid: agents.uuid,
248
+ type: agents.type
249
+ }).from(agents).where(inArray(agents.uuid, agentIds));
250
+ const agentTypeById = /* @__PURE__ */ new Map();
251
+ for (const row of agentRows) agentTypeById.set(row.uuid, row.type);
252
+ const missing = agentIds.filter((id) => !agentTypeById.has(id));
253
+ if (missing.length > 0) throw new BadRequestError(`Agents not found: ${missing.join(", ")}`);
254
+ if (options.assertHuman) {
255
+ const nonHuman = agentRows.filter((a) => a.type !== "human");
256
+ if (nonHuman.length > 0) throw new BadRequestError(`assertHuman violated: agents must be of type 'human' but got ${nonHuman.map((a) => `${a.uuid}=${a.type}`).join(", ")}`);
257
+ }
258
+ let existingAgentTypes = [];
259
+ if (chatType === "direct") existingAgentTypes = await loadExistingAgentTypes(tx, chatId, new Set(agentIds));
260
+ const rows = participants.map((spec) => {
261
+ const rawAgentType = agentTypeById.get(spec.agentId);
262
+ if (rawAgentType === void 0) throw new Error("Unexpected: agent type lookup unset after presence check");
263
+ const agentType = agentTypeSchema.parse(rawAgentType);
264
+ const peerTypesForRow = chatType === "direct" ? [...existingAgentTypes, ...participants.filter((p) => p.agentId !== spec.agentId).map((p) => agentTypeById.get(p.agentId)).filter((t) => t !== void 0)].map((t) => agentTypeSchema.parse(t)) : [];
265
+ return {
266
+ chatId,
267
+ agentId: spec.agentId,
268
+ role: spec.role ?? "member",
269
+ mode: defaultParticipantMode(chatType, agentType, peerTypesForRow),
270
+ lastReadAt: spec.carriedReadState?.lastReadAt ?? null,
271
+ unreadMentionCount: spec.carriedReadState?.unreadMentionCount ?? 0
272
+ };
273
+ });
274
+ const insert = tx.insert(chatParticipants).values(rows);
275
+ if (options.onConflictDoNothing) await insert.onConflictDoNothing({ target: [chatParticipants.chatId, chatParticipants.agentId] });
276
+ else await insert;
277
+ }
278
+ async function loadExistingAgentTypes(tx, chatId, excludeAgentIds) {
279
+ return (await tx.select({
280
+ type: agents.type,
281
+ agentId: chatParticipants.agentId
282
+ }).from(chatParticipants).innerJoin(agents, eq(chatParticipants.agentId, agents.uuid)).where(eq(chatParticipants.chatId, chatId))).filter((r) => !excludeAgentIds.has(r.agentId)).map((r) => r.type);
283
+ }
284
+ /**
285
+ * Upgrade `chats.type` from `direct` → `group` AND re-grade every existing
286
+ * non-human participant to `mention_only`. Idempotent: if `chat.type` is
287
+ * already `group` (or any non-`direct` value), no-op.
288
+ *
289
+ * Callers that are about to insert a 3rd participant on a `direct` chat
290
+ * invoke this BEFORE `addChatParticipants` so the new row picks up the
291
+ * post-upgrade `chats.type` and the existing rows are re-graded in the
292
+ * same transaction.
293
+ *
294
+ * Note: this is the replacement for `services/chat.ts`'s
295
+ * `maybeUpgradeDirectToGroup` (the one in `services/watcher.ts` is
296
+ * removed). Keep the rename: `changeChatType` is more precise about the
297
+ * primary mutation; `maybe…ToGroup` overstated the conditional gate.
298
+ */
299
+ async function changeChatType(tx, chatId, newType) {
300
+ const [chat] = await tx.select({ type: chats.type }).from(chats).where(eq(chats.id, chatId)).limit(1);
301
+ if (!chat) throw new NotFoundError(`Chat "${chatId}" not found`);
302
+ if (chat.type === newType) return;
303
+ if (newType === "group" && chat.type !== "direct") throw new BadRequestError(`Cannot change chat type from "${chat.type}" to "${newType}"`);
304
+ await tx.update(chats).set({
305
+ type: newType,
306
+ updatedAt: /* @__PURE__ */ new Date()
307
+ }).where(eq(chats.id, chatId));
308
+ const ids = (await tx.select({ agentId: chatParticipants.agentId }).from(chatParticipants).innerJoin(agents, eq(chatParticipants.agentId, agents.uuid)).where(and(eq(chatParticipants.chatId, chatId), ne(agents.type, "human")))).map((r) => r.agentId);
309
+ if (ids.length === 0) return;
310
+ await tx.update(chatParticipants).set({ mode: "mention_only" }).where(and(eq(chatParticipants.chatId, chatId), inArray(chatParticipants.agentId, ids)));
311
+ }
312
+ /**
313
+ * Heuristic for whether an insert about to happen would push the chat past
314
+ * the direct → group threshold. Pure helper so callers can decide whether
315
+ * to call `changeChatType` before `addChatParticipants` without re-deriving
316
+ * the rule locally.
317
+ */
318
+ function wouldUpgradeToGroup(currentParticipantCount, newParticipantCount) {
319
+ return currentParticipantCount + newParticipantCount >= 3;
320
+ }
321
+ /**
188
322
  * Chat-first workspace — watcher subscription helpers.
189
323
  *
190
324
  * Watchers (rows in `chat_subscriptions`) are non-speaking observers. A
@@ -276,24 +410,6 @@ async function recomputeWatchersForMember(db, memberId) {
276
410
  for (const { chatId } of rows) await recomputeChatWatchers(db, chatId);
277
411
  }
278
412
  /**
279
- * Mirror of `services/chat.ts` `maybeUpgradeDirectToGroup`. Inlined here so
280
- * `joinAsParticipant` keeps the upgrade rule + the state carry in one
281
- * transaction without depending on chat.ts (avoids a circular import).
282
- */
283
- async function maybeUpgradeDirectToGroup$1(tx, chatId, existingParticipantIds) {
284
- if (existingParticipantIds.length + 1 < 3) return;
285
- const [chat] = await tx.select({ type: chats.type }).from(chats).where(eq(chats.id, chatId)).limit(1);
286
- if (!chat || chat.type !== "direct") return;
287
- await tx.update(chats).set({
288
- type: "group",
289
- updatedAt: /* @__PURE__ */ new Date()
290
- }).where(eq(chats.id, chatId));
291
- if (existingParticipantIds.length === 0) return;
292
- const ids = (await tx.select({ uuid: agents.uuid }).from(agents).where(and(inArray(agents.uuid, existingParticipantIds), ne(agents.type, "human")))).map((r) => r.uuid);
293
- if (ids.length === 0) return;
294
- await tx.update(chatParticipants).set({ mode: "mention_only" }).where(and(eq(chatParticipants.chatId, chatId), inArray(chatParticipants.agentId, ids)));
295
- }
296
- /**
297
413
  * Watcher → speaking participant. State-carry transaction.
298
414
  *
299
415
  * 1. DELETE the watcher row (returning read state).
@@ -318,15 +434,15 @@ async function joinAsParticipant(db, chatId, humanAgentId) {
318
434
  inserted: false,
319
435
  carried: carriedRow ?? null
320
436
  };
321
- await maybeUpgradeDirectToGroup$1(tx, chatId, (await tx.select({ agentId: chatParticipants.agentId }).from(chatParticipants).where(eq(chatParticipants.chatId, chatId))).map((p) => p.agentId));
322
- await tx.insert(chatParticipants).values({
323
- chatId,
437
+ if (wouldUpgradeToGroup((await tx.select({ agentId: chatParticipants.agentId }).from(chatParticipants).where(eq(chatParticipants.chatId, chatId))).length, 1)) await changeChatType(tx, chatId, "group");
438
+ await addChatParticipants(tx, chatId, [{
324
439
  agentId: humanAgentId,
325
440
  role: "member",
326
- mode: "full",
327
- lastReadAt: carriedRow?.lastReadAt ?? null,
328
- unreadMentionCount: carriedRow?.unreadMentionCount ?? 0
329
- });
441
+ carriedReadState: carriedRow ? {
442
+ lastReadAt: carriedRow.lastReadAt,
443
+ unreadMentionCount: carriedRow.unreadMentionCount
444
+ } : void 0
445
+ }], { assertHuman: true });
330
446
  return {
331
447
  chatId,
332
448
  inserted: true,
@@ -405,28 +521,6 @@ function ensureCanJoin(membership) {
405
521
  if (membership === "participant") throw new ConflictError("Already a participant in this chat");
406
522
  if (membership === null) throw new ForbiddenError("Not a watcher of this chat — open the chat from your workspace before joining");
407
523
  }
408
- /**
409
- * When a direct chat grows past 2 participants, upgrade it to `group` and
410
- * flip every existing non-human agent participant to `mention_only` — see
411
- * proposals/hub-agent-messaging-reply-and-mentions §3.3. The caller is
412
- * expected to insert the new participant AFTER this runs, so the "existing"
413
- * set excludes them.
414
- *
415
- * Idempotent: if the chat is already a group, no-op.
416
- */
417
- async function maybeUpgradeDirectToGroup(db, chatId, existingParticipantIds, newParticipantCount) {
418
- if (existingParticipantIds.length + newParticipantCount < 3) return;
419
- const [chat] = await db.select({ type: chats.type }).from(chats).where(eq(chats.id, chatId)).limit(1);
420
- if (!chat || chat.type !== "direct") return;
421
- await db.update(chats).set({
422
- type: "group",
423
- updatedAt: /* @__PURE__ */ new Date()
424
- }).where(eq(chats.id, chatId));
425
- if (existingParticipantIds.length === 0) return;
426
- const ids = (await db.select({ uuid: agents.uuid }).from(agents).where(and(inArray(agents.uuid, existingParticipantIds), ne(agents.type, "human")))).map((a) => a.uuid);
427
- if (ids.length === 0) return;
428
- await db.update(chatParticipants).set({ mode: "mention_only" }).where(and(eq(chatParticipants.chatId, chatId), inArray(chatParticipants.agentId, ids)));
429
- }
430
524
  async function createChat(db, creatorId, data) {
431
525
  const chatId = randomUUID();
432
526
  const allParticipantIds = new Set([creatorId, ...data.participantIds]);
@@ -444,7 +538,6 @@ async function createChat(db, creatorId, data) {
444
538
  const orgId = creator.organizationId;
445
539
  const crossOrg = existingAgents.filter((a) => a.organizationId !== orgId);
446
540
  if (crossOrg.length > 0) throw new BadRequestError(`Cross-organization chat not allowed: ${crossOrg.map((a) => a.id).join(", ")}`);
447
- const isDirectAgentOnly = data.type === "direct" && existingAgents.every((a) => a.type !== "human");
448
541
  return db.transaction(async (tx) => {
449
542
  const [chat] = await tx.insert(chats).values({
450
543
  id: chatId,
@@ -453,13 +546,10 @@ async function createChat(db, creatorId, data) {
453
546
  topic: data.topic ?? null,
454
547
  metadata: data.metadata ?? {}
455
548
  }).returning();
456
- const participantRows = [...allParticipantIds].map((agentId) => ({
457
- chatId,
549
+ await addChatParticipants(tx, chatId, [...allParticipantIds].map((agentId) => ({
458
550
  agentId,
459
- role: agentId === creatorId ? "owner" : "member",
460
- ...isDirectAgentOnly ? { mode: "mention_only" } : {}
461
- }));
462
- await tx.insert(chatParticipants).values(participantRows);
551
+ role: agentId === creatorId ? "owner" : "member"
552
+ })));
463
553
  await recomputeChatWatchers(tx, chatId);
464
554
  const participants = await tx.select().from(chatParticipants).where(eq(chatParticipants.chatId, chatId));
465
555
  if (!chat) throw new Error("Unexpected: INSERT RETURNING produced no row");
@@ -532,13 +622,9 @@ async function ensureParticipant(db, chatId, agentId) {
532
622
  const [existing] = await db.select({ agentId: chatParticipants.agentId }).from(chatParticipants).where(and(eq(chatParticipants.chatId, chatId), eq(chatParticipants.agentId, agentId))).limit(1);
533
623
  if (existing) return;
534
624
  await db.transaction(async (tx) => {
535
- await maybeUpgradeDirectToGroup(tx, chatId, (await tx.select({ agentId: chatParticipants.agentId }).from(chatParticipants).where(eq(chatParticipants.chatId, chatId))).map((p) => p.agentId), 1);
625
+ if (wouldUpgradeToGroup((await tx.select({ agentId: chatParticipants.agentId }).from(chatParticipants).where(eq(chatParticipants.chatId, chatId))).length, 1)) await changeChatType(tx, chatId, "group");
536
626
  await tx.delete(chatSubscriptions).where(and(eq(chatSubscriptions.chatId, chatId), eq(chatSubscriptions.agentId, agentId)));
537
- await tx.insert(chatParticipants).values({
538
- chatId,
539
- agentId,
540
- mode: "full"
541
- }).onConflictDoNothing({ target: [chatParticipants.chatId, chatParticipants.agentId] });
627
+ await addChatParticipants(tx, chatId, [{ agentId }], { onConflictDoNothing: true });
542
628
  await recomputeChatWatchers(tx, chatId);
543
629
  });
544
630
  invalidateChatAudience(chatId);
@@ -555,13 +641,9 @@ async function addParticipant(db, chatId, requesterId, data) {
555
641
  const [existing] = await db.select({ chatId: chatParticipants.chatId }).from(chatParticipants).where(and(eq(chatParticipants.chatId, chatId), eq(chatParticipants.agentId, data.agentId))).limit(1);
556
642
  if (existing) throw new ConflictError(`Agent "${data.agentId}" is already a participant`);
557
643
  await db.transaction(async (tx) => {
558
- await maybeUpgradeDirectToGroup(tx, chatId, (await tx.select({ agentId: chatParticipants.agentId }).from(chatParticipants).where(eq(chatParticipants.chatId, chatId))).map((p) => p.agentId), 1);
644
+ if (wouldUpgradeToGroup((await tx.select({ agentId: chatParticipants.agentId }).from(chatParticipants).where(eq(chatParticipants.chatId, chatId))).length, 1)) await changeChatType(tx, chatId, "group");
559
645
  await tx.delete(chatSubscriptions).where(and(eq(chatSubscriptions.chatId, chatId), eq(chatSubscriptions.agentId, data.agentId)));
560
- await tx.insert(chatParticipants).values({
561
- chatId,
562
- agentId: data.agentId,
563
- mode: data.mode ?? "full"
564
- });
646
+ await addChatParticipants(tx, chatId, [{ agentId: data.agentId }]);
565
647
  await recomputeChatWatchers(tx, chatId);
566
648
  });
567
649
  invalidateChatAudience(chatId);
@@ -669,15 +751,15 @@ async function joinChat(db, chatId, memberId, humanAgentId) {
669
751
  lastReadAt: chatSubscriptions.lastReadAt,
670
752
  unreadMentionCount: chatSubscriptions.unreadMentionCount
671
753
  });
672
- await maybeUpgradeDirectToGroup(tx, chatId, participantAgentIds, 1);
673
- await tx.insert(chatParticipants).values({
674
- chatId,
754
+ if (wouldUpgradeToGroup(participantAgentIds.length, 1)) await changeChatType(tx, chatId, "group");
755
+ await addChatParticipants(tx, chatId, [{
675
756
  agentId: humanAgentId,
676
757
  role: "member",
677
- mode: "full",
678
- lastReadAt: carriedRow?.lastReadAt ?? null,
679
- unreadMentionCount: carriedRow?.unreadMentionCount ?? 0
680
- });
758
+ carriedReadState: carriedRow ? {
759
+ lastReadAt: carriedRow.lastReadAt,
760
+ unreadMentionCount: carriedRow.unreadMentionCount
761
+ } : void 0
762
+ }], { assertHuman: true });
681
763
  await recomputeChatWatchers(tx, chatId);
682
764
  });
683
765
  invalidateChatAudience(chatId);
@@ -719,7 +801,6 @@ async function findOrCreateDirectChat(db, agentAId, agentBId) {
719
801
  const directChats = await db.select().from(chats).where(and(inArray(chats.id, commonChatIds), eq(chats.type, "direct"), eq(chats.organizationId, orgId))).orderBy(chats.createdAt, chats.id).limit(1);
720
802
  if (directChats.length > 0 && directChats[0]) return directChats[0];
721
803
  }
722
- const mode = agentA.type !== "human" && agentB.type !== "human" ? "mention_only" : "full";
723
804
  const chatId = randomUUID();
724
805
  return db.transaction(async (tx) => {
725
806
  const [chat] = await tx.insert(chats).values({
@@ -727,16 +808,12 @@ async function findOrCreateDirectChat(db, agentAId, agentBId) {
727
808
  organizationId: orgId,
728
809
  type: "direct"
729
810
  }).returning();
730
- await tx.insert(chatParticipants).values([{
731
- chatId,
811
+ await addChatParticipants(tx, chatId, [{
732
812
  agentId: agentAId,
733
- role: "member",
734
- mode
813
+ role: "member"
735
814
  }, {
736
- chatId,
737
815
  agentId: agentBId,
738
- role: "member",
739
- mode
816
+ role: "member"
740
817
  }]);
741
818
  await recomputeChatWatchers(tx, chatId);
742
819
  if (!chat) throw new Error("Unexpected: INSERT RETURNING produced no row");
@@ -2051,4 +2128,4 @@ async function cleanupStaleClients(db, staleSeconds = 60) {
2051
2128
  return result.length;
2052
2129
  }
2053
2130
  //#endregion
2054
- export { pendingQuestions as $, heartbeatClient as A, listAgentsWithRuntime as B, findOrCreateDirectChat as C, getClient as D, getChatDetail as E, joinChat as F, listClientsForOrgAdmin as G, listChats as H, leaveAsParticipant as I, markStaleAgents as J, listMessages as K, leaveChat as L, inboxEntries as M, invalidateChatAudience as N, getOnlineCount as O, joinAsParticipant as P, notifyRecipients as Q, listActiveAgentsPinnedToClient as R, ensureParticipant as S, getCachedAudience as T, listChatsForMember as U, listChatParticipantsWithNames as V, listClients as W, members as X, markSupersededByChat as Y, messages as Z, createNotifier as _, updateClientCapabilities as _t, agents as a, removeParticipant as at, editMessage as b, bindAgent as c, retireClient as ct, chats as d, serverInstances as dt, recomputeChatWatchers as et, claimClient as f, setOffline as ft, createChat as g, unbindAgent as gt, clients as h, touchAgent as ht, agentVisibilityCondition as i, registerClient as it, heartbeatInstance as j, getPresence as k, chatParticipants as l, sendMessage as lt, cleanupStalePresence as m, submitAnswer as mt, agentChatSessions as n, recomputeWatchersForMember as nt, assertClientOwner as o, resetActivity as ot, cleanupStaleClients as p, setRuntimeState as pt, listMyPinnedAgents as q, agentPresence as r, registerChatMessageDispatcher as rt, assertParticipant as s, resolveChatMembership as st, addParticipant as t, recomputeWatchersForAgent as tt, chatSubscriptions as u, sendToAgent as ut, deriveAuthState as v, upsertSessionState as vt, getActivityOverview as w, ensureCanJoin as x, disconnectClient as y, listAgentsManagedByUser as z };
2131
+ export { messages as $, getOnlineCount as A, listActiveAgentsPinnedToClient as B, ensureCanJoin as C, getCachedAudience as D, getActivityOverview as E, invalidateChatAudience as F, listChatsForMember as G, listAgentsWithRuntime as H, joinAsParticipant as I, listMessages as J, listClients as K, joinChat as L, heartbeatClient as M, heartbeatInstance as N, getChatDetail as O, inboxEntries as P, members as Q, leaveAsParticipant as R, editMessage as S, findOrCreateDirectChat as T, listChatParticipantsWithNames as U, listAgentsManagedByUser as V, listChats as W, markStaleAgents as X, listMyPinnedAgents as Y, markSupersededByChat as Z, clients as _, touchAgent as _t, agentVisibilityCondition as a, registerChatMessageDispatcher as at, deriveAuthState as b, upsertSessionState as bt, assertParticipant as c, resetActivity as ct, chatParticipants as d, sendMessage as dt, notifyRecipients as et, chatSubscriptions as f, sendToAgent as ft, cleanupStalePresence as g, submitAnswer as gt, cleanupStaleClients as h, setRuntimeState as ht, agentPresence as i, recomputeWatchersForMember as it, getPresence as j, getClient as k, bindAgent as l, resolveChatMembership as lt, claimClient as m, setOffline as mt, addParticipant as n, recomputeChatWatchers as nt, agents as o, registerClient as ot, chats as p, serverInstances as pt, listClientsForOrgAdmin as q, agentChatSessions as r, recomputeWatchersForAgent as rt, assertClientOwner as s, removeParticipant as st, addChatParticipants as t, pendingQuestions as tt, changeChatType as u, retireClient as ut, createChat as v, unbindAgent as vt, ensureParticipant as w, disconnectClient as x, createNotifier as y, updateClientCapabilities as yt, leaveChat as z };
@@ -1,7 +1,7 @@
1
1
  import "./observability-BAScT_5S-BcW9HgkG.mjs";
2
2
  import "./logger-core-BTmvdflj-DjW8FM4T.mjs";
3
- import "./dist-BwPlBZWi.mjs";
3
+ import "./dist-xP6NpdMp.mjs";
4
4
  import "./errors-CF5evtJt-B0NTIVPt.mjs";
5
5
  import "./src-DNBS5Yjj.mjs";
6
- import { q as listMyPinnedAgents } from "./client-DL5vHhvQ-CnYGq2x-.mjs";
6
+ import { Y as listMyPinnedAgents } from "./client-B89AKi3Q-DAyGdQSq.mjs";
7
7
  export { listMyPinnedAgents };
@@ -45,6 +45,39 @@ function scanMentionTokens(content) {
45
45
  return tokens;
46
46
  }
47
47
  /**
48
+ * Derive the `chat_participants.mode` that a freshly inserted row MUST get,
49
+ * given the chat's `type` and the joining agent's `type`. This is the single
50
+ * authoritative rule for the invariant
51
+ *
52
+ * `(chat.type === 'group' && agent.type !== 'human') ⇒ mode === 'mention_only'`
53
+ *
54
+ * plus the legacy "agent-only direct chat" anti-echo rule. The helper is
55
+ * pure and synchronous; all DB lookups are the caller's responsibility (see
56
+ * `services/participant-mode.ts::addChatParticipants` for the canonical
57
+ * server entrypoint that wires it).
58
+ *
59
+ * Rule (encoded once, here):
60
+ *
61
+ * - `agent.type === 'human'` → 'full'
62
+ * - `chat.type === 'group'` (and agent is non-human) → 'mention_only'
63
+ * - `chat.type === 'direct'` + agent non-human:
64
+ * - if every other participant on this chat is also non-human →
65
+ * 'mention_only' (prevents the A↔B reply loop noted in migration
66
+ * 0029)
67
+ * - otherwise → 'full' (the peer is a human / external user, so the
68
+ * agent should listen to every message in this 1:1 line)
69
+ *
70
+ * `peerAgentTypes` is read only in the `direct` branch; callers may pass
71
+ * an empty array (or omit it) for `group` chats — it's ignored. Watcher /
72
+ * subscription-side `chat_subscriptions` rows are unaffected; the helper
73
+ * only governs the "speaking" mode column.
74
+ */
75
+ function defaultParticipantMode(chatType, agentType, peerAgentTypes = []) {
76
+ if (agentType === "human") return "full";
77
+ if (chatType === "group" || chatType === "thread") return "mention_only";
78
+ return peerAgentTypes.every((t) => t !== "human") ? "mention_only" : "full";
79
+ }
80
+ /**
48
81
  * Single source of truth for "is this string safe to redirect to after a
49
82
  * successful OAuth callback".
50
83
  *
@@ -626,10 +659,17 @@ z.object({
626
659
  firstMessagePreview: z.string().nullable()
627
660
  });
628
661
  const updateChatSchema = z.object({ topic: z.string().trim().max(500).nullable() });
629
- const addParticipantSchema = z.object({
630
- agentId: z.string().min(1),
631
- mode: z.enum(["full", "mention_only"]).default("full")
632
- });
662
+ /**
663
+ * Public API body for `POST /api/v1/agent/chats/:chatId/participants`.
664
+ * Phase 1 removed the `mode` field: participant mode is derived server-side
665
+ * from `(chats.type, agents.type)` via `services/participant-mode.ts` and
666
+ * cannot be overridden by the caller. The handler still inspects the raw
667
+ * body and rejects with `400 MODE_FIELD_DEPRECATED` if `mode` is present,
668
+ * so an out-of-tree caller that still sends it gets a clear error and a
669
+ * telemetry counter increments — see `chat-participant-mode-fix-design.md`
670
+ * §3.2 / §6.
671
+ */
672
+ const addParticipantSchema = z.object({ agentId: z.string().min(1) });
633
673
  z.object({ agentId: z.string().min(1) });
634
674
  const clientStatusSchema = z.enum(["connected", "disconnected"]);
635
675
  /**
@@ -813,6 +853,62 @@ const contextTreeSnapshotSchema = z.object({
813
853
  edges: z.array(contextTreeEdgeSchema),
814
854
  changes: z.array(contextTreeChangeSchema)
815
855
  });
856
+ const githubAccountTypeSchema = z.enum(["User", "Organization"]);
857
+ const githubPermissionLevelSchema = z.enum([
858
+ "read",
859
+ "write",
860
+ "admin"
861
+ ]);
862
+ /**
863
+ * `installation.permissions` blob from GitHub. Key is the permission name
864
+ * (`contents`, `pull_requests`, `issues`, `members`, …) — we keep this as a
865
+ * free-form `z.record` because GitHub adds new permission keys over time
866
+ * and we don't want a Hub-side `app_id` upgrade just to surface a new key
867
+ * in the integrations panel.
868
+ */
869
+ const githubAppInstallationPermissionsSchema = z.record(z.string(), githubPermissionLevelSchema);
870
+ /**
871
+ * Subscribed event-name list, e.g. `["issues", "pull_request", "push"]`.
872
+ * Free-form for the same forward-compat reason as `permissions`.
873
+ */
874
+ const githubAppInstallationEventsSchema = z.array(z.string());
875
+ z.object({
876
+ login: z.string().optional(),
877
+ accessToken: z.string().optional(),
878
+ accessTokenExpiresAt: z.string().datetime({ offset: true }).optional(),
879
+ refreshToken: z.string().optional(),
880
+ refreshTokenExpiresAt: z.string().datetime({ offset: true }).optional()
881
+ });
882
+ /**
883
+ * GET-side projection returned by the Hub admin API for the Integrations
884
+ * panel. Secrets are never echoed — only the metadata needed to render
885
+ * "you're connected as @octocat (Organization), 7 repos selected".
886
+ *
887
+ * `selectedRepoCount` is derived from a separate join (App webhook
888
+ * `installation_repositories` events update a children table not modeled
889
+ * here yet); included now so the panel's API shape is stable from the
890
+ * first ship.
891
+ */
892
+ /**
893
+ * Body for `POST /orgs/:orgId/github-app-installation/claim` — manual
894
+ * recovery for an installation row that ended up unbound (codex P1-5 + H1).
895
+ * The orphan-reclaim sweep at sign-in auto-claims the single-orphan case;
896
+ * this endpoint backs the Settings "Claim install" buttons when there's
897
+ * more than one (or the account is an org, where auto-claim is too risky).
898
+ */
899
+ const githubAppInstallationClaimBodySchema = z.object({ installationId: z.number().int().positive() });
900
+ z.object({
901
+ installationId: z.number().int().positive(),
902
+ accountType: githubAccountTypeSchema,
903
+ accountLogin: z.string(),
904
+ accountGithubId: z.number().int().positive(),
905
+ permissions: githubAppInstallationPermissionsSchema,
906
+ events: githubAppInstallationEventsSchema,
907
+ suspended: z.boolean(),
908
+ manageUrl: z.string().url(),
909
+ createdAt: z.string().datetime({ offset: true }),
910
+ updatedAt: z.string().datetime({ offset: true })
911
+ });
816
912
  const supportedImageMimeSchema = z.enum([
817
913
  "image/png",
818
914
  "image/jpeg",
@@ -1199,18 +1295,36 @@ const notificationQuerySchema = z.object({
1199
1295
  const githubStartQuerySchema = z.object({ next: z.string().max(256).optional() });
1200
1296
  const githubCallbackQuerySchema = z.object({
1201
1297
  code: z.string().min(1),
1202
- state: z.string().min(1)
1298
+ state: z.string().min(1),
1299
+ installation_id: z.string().regex(/^\d+$/).optional(),
1300
+ setup_action: z.enum([
1301
+ "install",
1302
+ "update",
1303
+ "request"
1304
+ ]).optional()
1203
1305
  });
1204
1306
  /**
1205
1307
  * Dev-only callback to bypass the GitHub round-trip — sign in as a stub
1206
1308
  * Github user. Gated by NODE_ENV !== 'production'; production always 404s.
1309
+ *
1310
+ * The App-flow extension fields (`installationId`, `installationAccountType`,
1311
+ * `installationAccountLogin`, `installationAccountGithubId`) let the dev
1312
+ * flow simulate a GitHub App install in the same redirect — when present
1313
+ * they stub a `github_app_installations` row before the OAuth flow
1314
+ * completes, so the rest of the dev session can exercise the App-bound
1315
+ * code paths (Settings → Integrations panel, webhook routing) without a
1316
+ * real install. Missing → legacy OAuth-only dev flow.
1207
1317
  */
1208
1318
  const githubDevCallbackQuerySchema = z.object({
1209
1319
  githubId: z.string().min(1),
1210
1320
  login: z.string().min(1),
1211
1321
  email: z.string().email().optional(),
1212
1322
  displayName: z.string().optional(),
1213
- next: z.string().max(256).optional()
1323
+ next: z.string().max(256).optional(),
1324
+ installationId: z.string().regex(/^\d+$/).optional(),
1325
+ installationAccountType: z.enum(["User", "Organization"]).optional(),
1326
+ installationAccountLogin: z.string().min(1).optional(),
1327
+ installationAccountGithubId: z.string().regex(/^\d+$/).optional()
1214
1328
  });
1215
1329
  /**
1216
1330
  * Per-organization settings — schemas, namespaces, and the registry that
@@ -1606,4 +1720,4 @@ z.object({
1606
1720
  capabilities: serverCapabilitiesSchema.optional()
1607
1721
  }).passthrough();
1608
1722
  //#endregion
1609
- export { refreshTokenSchema as $, dryRunAgentRuntimeConfigSchema as A, isRedactedEnvValue as B, createAgentSchema as C, createOrgFromMeSchema as D, createMemberSchema as E, imageInlineContentSchema as F, messageSourceSchema as G, joinByInvitationSchema as H, inboxAckFrameSchema as I, paginationQuerySchema as J, notificationQuerySchema as K, inboxDeliverFrameSchema as L, githubCallbackQuerySchema as M, githubDevCallbackQuerySchema as N, defaultRuntimeConfigPayload as O, githubStartQuerySchema as P, rebindAgentSchema as Q, inboxPollQuerySchema as R, createAdapterMappingSchema as S, createMeChatSchema as T, listMeChatsQuerySchema as U, isReservedAgentName as V, loginSchema as W, questionAnswerMessageContentSchema as X, patchOnboardingSchema as Y, questionMessageContentSchema as Z, clientCapabilitiesSchema as _, updateClientCapabilitiesSchema as _t, AGENT_VISIBILITY as a, sendToAgentSchema as at, contextTreeSnapshotSchema as b, wsAuthFrameSchema as bt, ORG_SETTINGS_NAMESPACES as c, sessionEventSchema as ct, addParticipantSchema as d, stripCode as dt, runtimeStateMessageSchema as et, agentBindRequestSchema as f, submitQuestionAnswerSchema as ft, chatMetadataSchema as g, updateChatSchema as gt, agentTypeSchema as h, updateAgentSchema as ht, AGENT_STATUSES as i, sendMessageSchema as it, extractMentions as j, delegateFeishuUserSchema as k, WS_AUTH_FRAME_TIMEOUT_MS as l, sessionReconcileRequestSchema as lt, agentRuntimeConfigPayloadSchema as m, updateAgentRuntimeConfigSchema as mt, AGENT_NAME_REGEX as n, scanMentionTokens as nt, DEFAULT_RUNTIME_PROVIDER as o, sessionCompletionMessageSchema as ot, agentPinnedMessageSchema as p, updateAdapterConfigSchema as pt, onboardingEventSchema as q, AGENT_SELECTOR_HEADER as r, selfServiceFeishuBotSchema as rt, MENTION_REGEX as s, sessionEventMessageSchema as st, AGENT_BIND_REJECT_REASONS as t, safeRedirectPath as tt, addMeChatParticipantsSchema as u, sessionStateMessageSchema as ut, clientRegisterSchema as v, updateMemberSchema as vt, createChatSchema as w, createAdapterConfigSchema as x, connectTokenExchangeSchema as y, updateOrganizationSchema as yt, isOrgSettingNamespace as z };
1723
+ export { questionMessageContentSchema as $, delegateFeishuUserSchema as A, inboxPollQuerySchema as B, createAgentSchema as C, createOrgFromMeSchema as D, createMemberSchema as E, githubDevCallbackQuerySchema as F, listMeChatsQuerySchema as G, isRedactedEnvValue as H, githubStartQuerySchema as I, notificationQuerySchema as J, loginSchema as K, imageInlineContentSchema as L, extractMentions as M, githubAppInstallationClaimBodySchema as N, defaultParticipantMode as O, githubCallbackQuerySchema as P, questionAnswerMessageContentSchema as Q, inboxAckFrameSchema as R, createAdapterMappingSchema as S, wsAuthFrameSchema as St, createMeChatSchema as T, isReservedAgentName as U, isOrgSettingNamespace as V, joinByInvitationSchema as W, paginationQuerySchema as X, onboardingEventSchema as Y, patchOnboardingSchema as Z, clientCapabilitiesSchema as _, updateAgentSchema as _t, AGENT_VISIBILITY as a, selfServiceFeishuBotSchema as at, contextTreeSnapshotSchema as b, updateMemberSchema as bt, ORG_SETTINGS_NAMESPACES as c, sessionCompletionMessageSchema as ct, addParticipantSchema as d, sessionReconcileRequestSchema as dt, rebindAgentSchema as et, agentBindRequestSchema as f, sessionStateMessageSchema as ft, chatMetadataSchema as g, updateAgentRuntimeConfigSchema as gt, agentTypeSchema as h, updateAdapterConfigSchema as ht, AGENT_STATUSES as i, scanMentionTokens as it, dryRunAgentRuntimeConfigSchema as j, defaultRuntimeConfigPayload as k, WS_AUTH_FRAME_TIMEOUT_MS as l, sessionEventMessageSchema as lt, agentRuntimeConfigPayloadSchema as m, submitQuestionAnswerSchema as mt, AGENT_NAME_REGEX as n, runtimeStateMessageSchema as nt, DEFAULT_RUNTIME_PROVIDER as o, sendMessageSchema as ot, agentPinnedMessageSchema as p, stripCode as pt, messageSourceSchema as q, AGENT_SELECTOR_HEADER as r, safeRedirectPath as rt, MENTION_REGEX as s, sendToAgentSchema as st, AGENT_BIND_REJECT_REASONS as t, refreshTokenSchema as tt, addMeChatParticipantsSchema as u, sessionEventSchema as ut, clientRegisterSchema as v, updateChatSchema as vt, createChatSchema as w, createAdapterConfigSchema as x, updateOrganizationSchema as xt, connectTokenExchangeSchema as y, updateClientCapabilitiesSchema as yt, inboxDeliverFrameSchema as z };