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

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-CY2NxeKx.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-CTkhS6p5.mjs";
8
+ import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-DJm0EaZP.mjs";
9
9
  import "../errors-CF5evtJt-B0NTIVPt.mjs";
10
10
  import "../src-DNBS5Yjj.mjs";
11
- import "../client-DL5vHhvQ-CnYGq2x-.mjs";
11
+ import "../client-h5l7mi0m-OEX7MOBg.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,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-CTkhS6p5.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-h5l7mi0m-OEX7MOBg.mjs";
7
7
  export { listMyPinnedAgents };
@@ -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-CTkhS6p5.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-h5l7mi0m.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");
@@ -1079,7 +1156,8 @@ async function listAgentsWithRuntime(db, scope) {
1079
1156
  activeSessions: agentPresence.activeSessions,
1080
1157
  totalSessions: agentPresence.totalSessions,
1081
1158
  runtimeUpdatedAt: agentPresence.runtimeUpdatedAt,
1082
- type: agents.type
1159
+ type: agents.type,
1160
+ managerId: agents.managerId
1083
1161
  }).from(agentPresence).innerJoin(agents, eq(agentPresence.agentId, agents.uuid)).where(and(isNotNull(agentPresence.runtimeState), agentVisibilityCondition(scope.organizationId, scope.memberId)));
1084
1162
  }
1085
1163
  /**
@@ -2051,4 +2129,4 @@ async function cleanupStaleClients(db, staleSeconds = 60) {
2051
2129
  return result.length;
2052
2130
  }
2053
2131
  //#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 };
2132
+ 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 };
@@ -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
  *
@@ -379,10 +412,15 @@ const runtimeStateSchema = z.enum([
379
412
  z.enum([
380
413
  "active",
381
414
  "suspended",
382
- "evicted"
415
+ "evicted",
416
+ "errored"
383
417
  ]);
384
418
  /** Wire-level states a client may report. `evicted` from a stale client is rejected. */
385
- const clientSessionStateSchema = z.enum(["active", "suspended"]);
419
+ const clientSessionStateSchema = z.enum([
420
+ "active",
421
+ "suspended",
422
+ "errored"
423
+ ]);
386
424
  const sessionStateMessageSchema = z.object({
387
425
  chatId: z.string().min(1),
388
426
  state: clientSessionStateSchema
@@ -626,10 +664,17 @@ z.object({
626
664
  firstMessagePreview: z.string().nullable()
627
665
  });
628
666
  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
- });
667
+ /**
668
+ * Public API body for `POST /api/v1/agent/chats/:chatId/participants`.
669
+ * Phase 1 removed the `mode` field: participant mode is derived server-side
670
+ * from `(chats.type, agents.type)` via `services/participant-mode.ts` and
671
+ * cannot be overridden by the caller. The handler still inspects the raw
672
+ * body and rejects with `400 MODE_FIELD_DEPRECATED` if `mode` is present,
673
+ * so an out-of-tree caller that still sends it gets a clear error and a
674
+ * telemetry counter increments — see `chat-participant-mode-fix-design.md`
675
+ * §3.2 / §6.
676
+ */
677
+ const addParticipantSchema = z.object({ agentId: z.string().min(1) });
633
678
  z.object({ agentId: z.string().min(1) });
634
679
  const clientStatusSchema = z.enum(["connected", "disconnected"]);
635
680
  /**
@@ -813,6 +858,62 @@ const contextTreeSnapshotSchema = z.object({
813
858
  edges: z.array(contextTreeEdgeSchema),
814
859
  changes: z.array(contextTreeChangeSchema)
815
860
  });
861
+ const githubAccountTypeSchema = z.enum(["User", "Organization"]);
862
+ const githubPermissionLevelSchema = z.enum([
863
+ "read",
864
+ "write",
865
+ "admin"
866
+ ]);
867
+ /**
868
+ * `installation.permissions` blob from GitHub. Key is the permission name
869
+ * (`contents`, `pull_requests`, `issues`, `members`, …) — we keep this as a
870
+ * free-form `z.record` because GitHub adds new permission keys over time
871
+ * and we don't want a Hub-side `app_id` upgrade just to surface a new key
872
+ * in the integrations panel.
873
+ */
874
+ const githubAppInstallationPermissionsSchema = z.record(z.string(), githubPermissionLevelSchema);
875
+ /**
876
+ * Subscribed event-name list, e.g. `["issues", "pull_request", "push"]`.
877
+ * Free-form for the same forward-compat reason as `permissions`.
878
+ */
879
+ const githubAppInstallationEventsSchema = z.array(z.string());
880
+ z.object({
881
+ login: z.string().optional(),
882
+ accessToken: z.string().optional(),
883
+ accessTokenExpiresAt: z.string().datetime({ offset: true }).optional(),
884
+ refreshToken: z.string().optional(),
885
+ refreshTokenExpiresAt: z.string().datetime({ offset: true }).optional()
886
+ });
887
+ /**
888
+ * GET-side projection returned by the Hub admin API for the Integrations
889
+ * panel. Secrets are never echoed — only the metadata needed to render
890
+ * "you're connected as @octocat (Organization), 7 repos selected".
891
+ *
892
+ * `selectedRepoCount` is derived from a separate join (App webhook
893
+ * `installation_repositories` events update a children table not modeled
894
+ * here yet); included now so the panel's API shape is stable from the
895
+ * first ship.
896
+ */
897
+ /**
898
+ * Body for `POST /orgs/:orgId/github-app-installation/claim` — manual
899
+ * recovery for an installation row that ended up unbound (codex P1-5 + H1).
900
+ * The orphan-reclaim sweep at sign-in auto-claims the single-orphan case;
901
+ * this endpoint backs the Settings "Claim install" buttons when there's
902
+ * more than one (or the account is an org, where auto-claim is too risky).
903
+ */
904
+ const githubAppInstallationClaimBodySchema = z.object({ installationId: z.number().int().positive() });
905
+ z.object({
906
+ installationId: z.number().int().positive(),
907
+ accountType: githubAccountTypeSchema,
908
+ accountLogin: z.string(),
909
+ accountGithubId: z.number().int().positive(),
910
+ permissions: githubAppInstallationPermissionsSchema,
911
+ events: githubAppInstallationEventsSchema,
912
+ suspended: z.boolean(),
913
+ manageUrl: z.string().url(),
914
+ createdAt: z.string().datetime({ offset: true }),
915
+ updatedAt: z.string().datetime({ offset: true })
916
+ });
816
917
  const supportedImageMimeSchema = z.enum([
817
918
  "image/png",
818
919
  "image/jpeg",
@@ -1199,18 +1300,36 @@ const notificationQuerySchema = z.object({
1199
1300
  const githubStartQuerySchema = z.object({ next: z.string().max(256).optional() });
1200
1301
  const githubCallbackQuerySchema = z.object({
1201
1302
  code: z.string().min(1),
1202
- state: z.string().min(1)
1303
+ state: z.string().min(1),
1304
+ installation_id: z.string().regex(/^\d+$/).optional(),
1305
+ setup_action: z.enum([
1306
+ "install",
1307
+ "update",
1308
+ "request"
1309
+ ]).optional()
1203
1310
  });
1204
1311
  /**
1205
1312
  * Dev-only callback to bypass the GitHub round-trip — sign in as a stub
1206
1313
  * Github user. Gated by NODE_ENV !== 'production'; production always 404s.
1314
+ *
1315
+ * The App-flow extension fields (`installationId`, `installationAccountType`,
1316
+ * `installationAccountLogin`, `installationAccountGithubId`) let the dev
1317
+ * flow simulate a GitHub App install in the same redirect — when present
1318
+ * they stub a `github_app_installations` row before the OAuth flow
1319
+ * completes, so the rest of the dev session can exercise the App-bound
1320
+ * code paths (Settings → Integrations panel, webhook routing) without a
1321
+ * real install. Missing → legacy OAuth-only dev flow.
1207
1322
  */
1208
1323
  const githubDevCallbackQuerySchema = z.object({
1209
1324
  githubId: z.string().min(1),
1210
1325
  login: z.string().min(1),
1211
1326
  email: z.string().email().optional(),
1212
1327
  displayName: z.string().optional(),
1213
- next: z.string().max(256).optional()
1328
+ next: z.string().max(256).optional(),
1329
+ installationId: z.string().regex(/^\d+$/).optional(),
1330
+ installationAccountType: z.enum(["User", "Organization"]).optional(),
1331
+ installationAccountLogin: z.string().min(1).optional(),
1332
+ installationAccountGithubId: z.string().regex(/^\d+$/).optional()
1214
1333
  });
1215
1334
  /**
1216
1335
  * Per-organization settings — schemas, namespaces, and the registry that
@@ -1606,4 +1725,4 @@ z.object({
1606
1725
  capabilities: serverCapabilitiesSchema.optional()
1607
1726
  }).passthrough();
1608
1727
  //#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 };
1728
+ 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 };