@agent-team-foundation/first-tree-hub 0.13.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -552,6 +552,7 @@ const serverConfigSchema = defineConfig({
552
552
  host: field(z.string().default("127.0.0.1"), { env: "FIRST_TREE_HUB_HOST" }),
553
553
  publicUrl: field(z.string().optional(), { env: "FIRST_TREE_HUB_PUBLIC_URL" })
554
554
  },
555
+ workspace: { root: field(z.string().default(join(DEFAULT_DATA_DIR, "workspaces")), { env: "FIRST_TREE_HUB_WORKSPACES_ROOT" }) },
555
556
  secrets: {
556
557
  jwtSecret: field(z.string(), {
557
558
  env: "FIRST_TREE_HUB_JWT_SECRET",
@@ -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-Bb5LR4y6.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-BBRxjmBS.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, v as initConfig, w as resolveConfigReadonly, x as readConfigFile, y as loadAgents } from "../bootstrap-Cya2OoHz.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, v as initConfig, w as resolveConfigReadonly, x as readConfigFile, y as loadAgents } from "../bootstrap-C15ZBOCC.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-C8yStx2L.mjs";
8
- import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-D_vnqC6a.mjs";
7
+ import "../dist-1XGLJMOq.mjs";
8
+ import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-BGx71p5s.mjs";
9
9
  import "../errors-LPcARA4K-Dbrptiyz.mjs";
10
10
  import "../src-DNBS5Yjj.mjs";
11
- import "../client-BH4CmUL0-CybE3kuP.mjs";
11
+ import "../client-CzXmweS9-DhUiuQvL.mjs";
12
12
  import "../invitation-DZO4NX3P-BPxTeHf-.mjs";
13
13
  import { join } from "node:path";
14
14
  import { existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
@@ -1495,13 +1495,13 @@ function decodeJwtExpSeconds(token) {
1495
1495
  //#region src/commands/onboard.ts
1496
1496
  async function promptMissing(args) {
1497
1497
  if (!args.server) try {
1498
- const { resolveServerUrl } = await import("../bootstrap-Cya2OoHz.mjs").then((n) => n.r);
1498
+ const { resolveServerUrl } = await import("../bootstrap-C15ZBOCC.mjs").then((n) => n.r);
1499
1499
  resolveServerUrl();
1500
1500
  } catch {
1501
1501
  args.server = await input({ message: "Hub server URL:" });
1502
1502
  saveOnboardState(args);
1503
1503
  }
1504
- const { loadCredentials } = await import("../bootstrap-Cya2OoHz.mjs").then((n) => n.r);
1504
+ const { loadCredentials } = await import("../bootstrap-C15ZBOCC.mjs").then((n) => n.r);
1505
1505
  if (!loadCredentials()) throw new Error("No saved credentials. Run `first-tree-hub connect <token>` before onboarding.");
1506
1506
  if (!args.id) {
1507
1507
  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-C8yStx2L.mjs";
3
+ import "./dist-1XGLJMOq.mjs";
4
4
  import "./errors-LPcARA4K-Dbrptiyz.mjs";
5
5
  import "./src-DNBS5Yjj.mjs";
6
- import { J as listMyPinnedAgents } from "./client-BH4CmUL0-CybE3kuP.mjs";
6
+ import { J as listMyPinnedAgents } from "./client-CzXmweS9-DhUiuQvL.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 { I as extractMentions, M as defaultParticipantMode, at as questionMessageContentSchema, i as AGENT_STATUSES, it as questionAnswerMessageContentSchema, o as AGENT_VISIBILITY, s as CHAT_ENGAGEMENT_STATUSES, ut as scanMentionTokens, x as clientCapabilitiesSchema, y as agentTypeSchema } from "./dist-C8yStx2L.mjs";
2
+ import { L as extractMentions, N as defaultParticipantMode, S as clientCapabilitiesSchema, b as agentTypeSchema, ct as questionAnswerMessageContentSchema, i as AGENT_STATUSES, lt as questionMessageContentSchema, mt as scanMentionTokens, o as AGENT_VISIBILITY, s as CHAT_ENGAGEMENT_STATUSES } from "./dist-1XGLJMOq.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-LPcARA4K-Dbrptiyz.mjs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { and, desc, eq, inArray, isNotNull, lt, ne, or, sql } from "drizzle-orm";
6
- import { bigserial, boolean, index, integer, jsonb, pgTable, primaryKey, text, timestamp, unique } from "drizzle-orm/pg-core";
7
- //#region ../server/dist/client-BH4CmUL0.mjs
6
+ import { bigserial, boolean, customType, index, integer, jsonb, pgTable, primaryKey, text, timestamp, unique } from "drizzle-orm/pg-core";
7
+ //#region ../server/dist/client-CzXmweS9.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
@@ -33,6 +33,13 @@ const clients = pgTable("clients", {
33
33
  lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
34
34
  metadata: jsonb("metadata").$type()
35
35
  }, (table) => [index("idx_clients_user").on(table.userId), index("idx_clients_org").on(table.organizationId)]);
36
+ /**
37
+ * `bytea` column type — Drizzle ships pg primitives but not bytea out of the
38
+ * box. Reads come back as Node `Buffer` (postgres-js); writes accept any
39
+ * `Uint8Array`. Used for the small inline avatar image blob; no streaming
40
+ * needed at this size (≤ ~50 KB after client-side resize).
41
+ */
42
+ const bytea = customType({ dataType: () => "bytea" });
36
43
  /** Agent registration. Each agent owns a unique inboxId for message delivery. */
37
44
  const agents = pgTable("agents", {
38
45
  uuid: text("uuid").primaryKey(),
@@ -49,6 +56,10 @@ const agents = pgTable("agents", {
49
56
  managerId: text("manager_id").notNull(),
50
57
  clientId: text("client_id").references(() => clients.id, { onDelete: "restrict" }),
51
58
  runtimeProvider: text("runtime_provider").notNull().default("claude-code"),
59
+ avatarColorToken: text("avatar_color_token"),
60
+ avatarImageData: bytea("avatar_image_data"),
61
+ avatarImageMime: text("avatar_image_mime"),
62
+ avatarImageUpdatedAt: timestamp("avatar_image_updated_at", { withTimezone: true }),
52
63
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
53
64
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
54
65
  }, (table) => [
@@ -1764,18 +1775,31 @@ async function sendMessageInner(db, chatId, senderId, data, options) {
1764
1775
  tx.select({ type: chats.type }).from(chats).where(eq(chats.id, chatId)).limit(1),
1765
1776
  tx.select({
1766
1777
  inboxId: agents.inboxId,
1767
- organizationId: agents.organizationId
1778
+ organizationId: agents.organizationId,
1779
+ type: agents.type
1768
1780
  }).from(agents).where(eq(agents.uuid, senderId)).limit(1)
1769
1781
  ]);
1770
1782
  const chatType = chatRow?.type ?? null;
1771
1783
  if (!senderRow) throw new NotFoundError(`Sender agent "${senderId}" not found`);
1784
+ let effectiveContent = data.content;
1785
+ if (senderRow.type !== "human" && typeof effectiveContent === "string") {
1786
+ const unwrapped = maybeUnwrapDoubleEncoded(effectiveContent);
1787
+ if (unwrapped !== null) {
1788
+ log.warn({
1789
+ metric: "double_encoded_content_unwrapped_total",
1790
+ chatId,
1791
+ senderId
1792
+ }, "agent sent JSON-encoded string content — unwrapping to restore markdown rendering");
1793
+ effectiveContent = unwrapped;
1794
+ }
1795
+ }
1772
1796
  if (data.replyToInbox !== void 0 && data.replyToInbox !== null) {
1773
1797
  if (senderRow.inboxId !== data.replyToInbox) throw new BadRequestError("replyToInbox must reference the sender's own inbox");
1774
1798
  }
1775
1799
  const incomingMeta = data.metadata ?? {};
1776
1800
  const explicitMentionsRaw = incomingMeta.mentions;
1777
1801
  const explicitMentions = Array.isArray(explicitMentionsRaw) ? explicitMentionsRaw.filter((m) => typeof m === "string") : [];
1778
- const contentText = typeof data.content === "string" ? data.content : "";
1802
+ const contentText = typeof effectiveContent === "string" ? effectiveContent : "";
1779
1803
  const resolved = contentText ? extractMentions(contentText, participants) : [];
1780
1804
  const mergedMentions = [...new Set([...explicitMentions, ...resolved])];
1781
1805
  const metadataToStore = mergedMentions.length > 0 ? {
@@ -1786,7 +1810,7 @@ async function sendMessageInner(db, chatId, senderId, data, options) {
1786
1810
  if (options.enforceGroupMention && chatType === "group") {
1787
1811
  if (mergedMentions.filter((id) => id !== senderId).length === 0) throw new BadRequestError("Sending to a group chat requires an explicit @mention. Use `first-tree-hub chat send <name>` to message a single agent, or @<name> in the content to address one or more group members.");
1788
1812
  }
1789
- let outboundContent = data.content;
1813
+ let outboundContent = effectiveContent;
1790
1814
  if (options.normalizeMentionsInContent && typeof outboundContent === "string") {
1791
1815
  const present = new Set(scanMentionTokens(outboundContent));
1792
1816
  const missingNames = [];
@@ -1898,6 +1922,35 @@ async function sendMessageInner(db, chatId, senderId, data, options) {
1898
1922
  recipients: txResult.recipients
1899
1923
  };
1900
1924
  }
1925
+ /**
1926
+ * Detect agent-sent content that was JSON.stringify-ed once before reaching
1927
+ * the CLI / API. The bad shape is an outer `"..."` wrapper + interior `\n` /
1928
+ * `\"` escape sequences, which the UI renders as a quoted literal instead of
1929
+ * markdown (issue #389). Returns the unwrapped inner string on a confident
1930
+ * match, or `null` to leave the content alone.
1931
+ *
1932
+ * Match conditions (all required) — kept strict so legitimate human content
1933
+ * that happens to look like a quoted phrase is never touched. The caller is
1934
+ * additionally responsible for restricting this to non-human senders.
1935
+ *
1936
+ * - first and last char are `"`
1937
+ * - body contains at least one typical JSON escape sequence
1938
+ * (`\n`, `\r`, `\t`, `\"`, or `\\`)
1939
+ * - `JSON.parse` succeeds
1940
+ * - the parse result is a `string` (excludes `{...}`, `[...]`, numbers)
1941
+ */
1942
+ function maybeUnwrapDoubleEncoded(content) {
1943
+ if (content.length < 4) return null;
1944
+ if (content.charCodeAt(0) !== 34) return null;
1945
+ if (content.charCodeAt(content.length - 1) !== 34) return null;
1946
+ if (!/\\[nrt"\\]/.test(content)) return null;
1947
+ try {
1948
+ const parsed = JSON.parse(content);
1949
+ return typeof parsed === "string" ? parsed : null;
1950
+ } catch {
1951
+ return null;
1952
+ }
1953
+ }
1901
1954
  function stripMentionPrefix(content) {
1902
1955
  return content.replace(/^(@\S+\s*)+/, "").trim();
1903
1956
  }
@@ -520,6 +520,16 @@ const AGENT_VISIBILITY = {
520
520
  ORGANIZATION: "organization"
521
521
  };
522
522
  const agentVisibilitySchema = z.enum(["private", "organization"]);
523
+ const avatarColorTokenSchema = z.enum([
524
+ "hue-0",
525
+ "hue-1",
526
+ "hue-2",
527
+ "hue-3",
528
+ "hue-4",
529
+ "hue-5",
530
+ "hue-6",
531
+ "hue-7"
532
+ ]);
523
533
  const AGENT_STATUSES = {
524
534
  ACTIVE: "active",
525
535
  SUSPENDED: "suspended",
@@ -571,7 +581,8 @@ const updateAgentSchema = z.object({
571
581
  visibility: agentVisibilitySchema.optional(),
572
582
  metadata: z.record(z.string(), z.unknown()).optional(),
573
583
  managerId: z.string().nullable().optional(),
574
- clientId: z.string().min(1).max(100).nullable().optional()
584
+ clientId: z.string().min(1).max(100).nullable().optional(),
585
+ avatarColorToken: avatarColorTokenSchema.nullable().optional()
575
586
  });
576
587
  /**
577
588
  * Service-level rebind input. Admin / owner re-binds an agent to a new
@@ -600,6 +611,8 @@ z.object({
600
611
  managerId: z.string().nullable(),
601
612
  clientId: z.string().nullable(),
602
613
  runtimeProvider: runtimeProviderSchema,
614
+ avatarColorToken: z.string().nullable(),
615
+ avatarImageUrl: z.string().nullable(),
603
616
  presenceStatus: presenceStatusSchema.optional(),
604
617
  createdAt: z.string(),
605
618
  updatedAt: z.string()
@@ -637,12 +650,30 @@ z.object({
637
650
  expiresIn: z.number(),
638
651
  command: z.string()
639
652
  });
640
- const githubEntityTypeSchema = z.enum([
653
+ /**
654
+ * `chats.metadata` is `jsonb` at the DB layer. Without a typed contract every
655
+ * writer was free to invent their own keys, so a GitHub webhook writing
656
+ * `{ source: "github", entityKey: "..." }` and a Feishu adapter writing
657
+ * `{ source: "feishu", externalChannelId: "..." }` could collide on shared
658
+ * keys (`source`, `title`). The discriminated union below pins both writers
659
+ * to a single shape and lets TypeScript narrow on `metadata.source`.
660
+ *
661
+ * Add new variants here; do not extend with free-form `Record<string, unknown>`.
662
+ */
663
+ /**
664
+ * Source of truth for which github entities get their own tag in the
665
+ * conversation list. Adding a new entry here extends `ChatSource`
666
+ * (`github_${entityType}`) and the SQL CASE/IN-list in
667
+ * `server/services/me-chat.ts` without touching them — both derive from
668
+ * this constant.
669
+ */
670
+ const GITHUB_ENTITY_TYPES = [
641
671
  "issue",
642
672
  "pull_request",
643
673
  "discussion",
644
674
  "commit"
645
- ]);
675
+ ];
676
+ const githubEntityTypeSchema = z.enum(GITHUB_ENTITY_TYPES);
646
677
  const githubChatMetadataSchema = z.object({
647
678
  source: z.literal("github"),
648
679
  entityType: githubEntityTypeSchema,
@@ -661,6 +692,14 @@ const chatMetadataSchema = z.discriminatedUnion("source", [githubChatMetadataSch
661
692
  * sneak through `{ source: "github" }` without the required fields.
662
693
  */
663
694
  const optionalChatMetadataSchema = z.union([z.object({}).strict(), chatMetadataSchema]);
695
+ const chatSourceSchema = z.enum([
696
+ "manual",
697
+ "github_issue",
698
+ "github_pull_request",
699
+ "github_discussion",
700
+ "github_commit",
701
+ "feishu"
702
+ ]);
664
703
  const chatTypeSchema = z.enum(["direct", "group"]);
665
704
  /**
666
705
  * Per-(chat, user) engagement state. Stored on `chat_user_state` so each
@@ -897,6 +936,11 @@ const contextTreeSummarySchema = z.object({
897
936
  removedCount: z.number().int().nonnegative(),
898
937
  changedNodeCount: z.number().int().nonnegative()
899
938
  });
939
+ const contextTreeUsageSummarySchema = z.object({
940
+ windowDays: z.number().int().positive(),
941
+ agentCount: z.number().int().nonnegative(),
942
+ usageCount: z.number().int().nonnegative()
943
+ });
900
944
  const contextTreeSnapshotSchema = z.object({
901
945
  repo: z.string().nullable(),
902
946
  branch: z.string().nullable(),
@@ -905,6 +949,7 @@ const contextTreeSnapshotSchema = z.object({
905
949
  snapshotStatus: contextTreeSnapshotStatusSchema,
906
950
  contextStatus: contextTreeStatusSchema,
907
951
  summary: contextTreeSummarySchema,
952
+ usage: contextTreeUsageSummarySchema,
908
953
  updates: z.array(contextTreeUpdateSchema),
909
954
  nodes: z.array(contextTreeNodeSchema),
910
955
  edges: z.array(contextTreeEdgeSchema),
@@ -1190,12 +1235,15 @@ const listMeChatsQuerySchema = z.object({
1190
1235
  cursor: z.string().optional(),
1191
1236
  limit: z.coerce.number().int().min(1).max(200).default(50),
1192
1237
  filter: meChatFilterSchema.default("all"),
1193
- engagement: chatEngagementViewSchema.default("active")
1238
+ engagement: chatEngagementViewSchema.default("active"),
1239
+ source: chatSourceSchema.optional()
1194
1240
  });
1195
1241
  const meChatParticipantSchema = z.object({
1196
1242
  agentId: z.string(),
1197
1243
  displayName: z.string(),
1198
- type: z.string()
1244
+ type: z.string(),
1245
+ avatarColorToken: z.string().nullable(),
1246
+ avatarImageUrl: z.string().nullable()
1199
1247
  });
1200
1248
  /**
1201
1249
  * Live activity hint surfaced in the conversation row's time slot. Derived
@@ -1257,6 +1305,47 @@ z.object({
1257
1305
  type: z.literal("chat:message"),
1258
1306
  chatId: z.string()
1259
1307
  });
1308
+ /**
1309
+ * Per-source aggregate for the conversation-list tag bar.
1310
+ *
1311
+ * - `chatCount` — number of chats the caller is in for this source. Used
1312
+ * to hide tags whose count is 0 ("don't render a PR tag if there are no
1313
+ * PRs").
1314
+ * - `unreadChatCount` — number of chats whose `unread_mention_count > 0`.
1315
+ * This is "chats with unread mentions", NOT "total mention count", so
1316
+ * the badge on each tag matches the semantics of the existing `unread`
1317
+ * filter pill (`totalUnread` in `pages/workspace/conversations`) — a
1318
+ * `2` on the PR tag means "2 PR chats have unread mentions", which is
1319
+ * what a user expects to click into.
1320
+ *
1321
+ * The map ALWAYS contains the `manual` key (the default tab is always
1322
+ * available, even at zero counts); other keys are present only when the
1323
+ * caller has at least one chat for that source.
1324
+ */
1325
+ const chatSourceCountSchema = z.object({
1326
+ chatCount: z.number().int().nonnegative(),
1327
+ unreadChatCount: z.number().int().nonnegative()
1328
+ });
1329
+ const listMeChatSourceCountsQuerySchema = z.object({ engagement: chatEngagementViewSchema.default("active") });
1330
+ z.object({ counts: z.partialRecord(chatSourceSchema, chatSourceCountSchema) });
1331
+ const workspaceDocRefSchema = z.object({
1332
+ type: z.literal("workspace"),
1333
+ chatId: z.string().trim().min(1),
1334
+ agentId: z.string().trim().min(1),
1335
+ basePath: z.string().trim().optional(),
1336
+ path: z.string().trim().min(1)
1337
+ });
1338
+ z.object({ basePath: z.string().trim().min(1) });
1339
+ const getMeDocSchema = z.object({
1340
+ agentId: z.string().trim().min(1),
1341
+ basePath: z.string().trim().optional(),
1342
+ path: z.string().trim().min(1)
1343
+ });
1344
+ const getMeDocResponseSchema = z.object({
1345
+ ref: workspaceDocRefSchema,
1346
+ path: z.string(),
1347
+ content: z.string()
1348
+ });
1260
1349
  z.enum([
1261
1350
  "connect",
1262
1351
  "create_agent",
@@ -1315,7 +1404,8 @@ z.object({
1315
1404
  organizationId: z.string(),
1316
1405
  organizationName: z.string(),
1317
1406
  role: z.enum(["admin", "member"]),
1318
- agentId: z.string()
1407
+ agentId: z.string(),
1408
+ orgHasOtherMembers: z.boolean()
1319
1409
  });
1320
1410
  const memberRoleSchema = z.enum(["admin", "member"]);
1321
1411
  const memberSchema = z.object({
@@ -1726,7 +1816,8 @@ const sessionEventKind = z.enum([
1726
1816
  "error",
1727
1817
  "assistant_text",
1728
1818
  "thinking",
1729
- "turn_end"
1819
+ "turn_end",
1820
+ "context_tree_usage"
1730
1821
  ]);
1731
1822
  const toolCallEventPayload = z.object({
1732
1823
  toolUseId: z.string(),
@@ -1768,6 +1859,10 @@ const thinkingEventPayload = z.object({});
1768
1859
  * completed turns to show only the final result message.
1769
1860
  */
1770
1861
  const turnEndEventPayload = z.object({ status: z.enum(["success", "error"]) });
1862
+ const contextTreeUsageEventPayload = z.object({
1863
+ purpose: z.literal("design_decision"),
1864
+ treeRepoUrl: z.string().nullable()
1865
+ });
1771
1866
  const sessionEventSchema = z.discriminatedUnion("kind", [
1772
1867
  z.object({
1773
1868
  kind: z.literal("tool_call"),
@@ -1788,6 +1883,10 @@ const sessionEventSchema = z.discriminatedUnion("kind", [
1788
1883
  z.object({
1789
1884
  kind: z.literal("turn_end"),
1790
1885
  payload: turnEndEventPayload
1886
+ }),
1887
+ z.object({
1888
+ kind: z.literal("context_tree_usage"),
1889
+ payload: contextTreeUsageEventPayload
1791
1890
  })
1792
1891
  ]);
1793
1892
  z.object({
@@ -1801,7 +1900,8 @@ z.object({
1801
1900
  errorEventPayload,
1802
1901
  assistantTextEventPayload,
1803
1902
  thinkingEventPayload,
1804
- turnEndEventPayload
1903
+ turnEndEventPayload,
1904
+ contextTreeUsageEventPayload
1805
1905
  ]),
1806
1906
  createdAt: z.string()
1807
1907
  });
@@ -1878,4 +1978,4 @@ z.object({
1878
1978
  capabilities: serverCapabilitiesSchema.optional()
1879
1979
  }).passthrough();
1880
1980
  //#endregion
1881
- export { notificationQuerySchema as $, createMemberSchema as A, githubDevCallbackQuerySchema as B, connectTokenExchangeSchema as C, updateChatSchema as Ct, createAgentSchema as D, wsAuthFrameSchema as Dt, createAdapterMappingSchema as E, updateOrganizationSchema as Et, dryRunAgentRuntimeConfigSchema as F, inboxPollQuerySchema as G, imageInlineContentSchema as H, extractMentions as I, isReservedAgentName as J, isOrgSettingNamespace as K, githubAppInstallationClaimBodySchema as L, defaultParticipantMode as M, defaultRuntimeConfigPayload as N, createChatSchema as O, delegateFeishuUserSchema as P, messageSourceSchema as Q, githubAppInstallationPermissionsSchema as R, clientRegisterSchema as S, updateAgentSchema as St, createAdapterConfigSchema as T, updateMemberSchema as Tt, inboxAckFrameSchema as U, githubStartQuerySchema as V, inboxDeliverFrameSchema as W, listMeChatsQuerySchema as X, joinByInvitationSchema as Y, loginSchema as Z, agentPinnedMessageSchema as _, sessionStateMessageSchema as _t, AGENT_TYPES as a, questionMessageContentSchema as at, chatMetadataSchema as b, updateAdapterConfigSchema as bt, DEFAULT_RUNTIME_PROVIDER as c, runtimeStateMessageSchema as ct, NOTIFICATION_TYPES as d, selfServiceFeishuBotSchema as dt, onboardingEventSchema as et, ORG_SETTINGS_NAMESPACES as f, sendMessageSchema as ft, agentBindRequestSchema as g, sessionReconcileRequestSchema as gt, addParticipantSchema as h, sessionEventSchema as ht, AGENT_STATUSES as i, questionAnswerMessageContentSchema as it, createOrgFromMeSchema as j, createMeChatSchema as k, LIVE_ACTIVITY_STALE_MS as l, safeRedirectPath as lt, addMeChatParticipantsSchema as m, sessionEventMessageSchema as mt, AGENT_NAME_REGEX as n, patchChatEngagementSchema as nt, AGENT_VISIBILITY as o, rebindAgentSchema as ot, WS_AUTH_FRAME_TIMEOUT_MS as p, sendToAgentSchema as pt, isRedactedEnvValue as q, AGENT_SELECTOR_HEADER as r, patchOnboardingSchema as rt, CHAT_ENGAGEMENT_STATUSES as s, refreshTokenSchema as st, AGENT_BIND_REJECT_REASONS as t, paginationQuerySchema as tt, MENTION_REGEX as u, scanMentionTokens as ut, agentRuntimeConfigPayloadSchema as v, stripCode as vt, contextTreeSnapshotSchema as w, updateClientCapabilitiesSchema as wt, clientCapabilitiesSchema as x, updateAgentRuntimeConfigSchema as xt, agentTypeSchema as y, submitQuestionAnswerSchema as yt, githubCallbackQuerySchema as z };
1981
+ export { listMeChatSourceCountsQuerySchema as $, createMeChatSchema as A, updateOrganizationSchema as At, githubAppInstallationClaimBodySchema as B, clientRegisterSchema as C, submitQuestionAnswerSchema as Ct, createAdapterMappingSchema as D, updateChatSchema as Dt, createAdapterConfigSchema as E, updateAgentSchema as Et, delegateFeishuUserSchema as F, imageInlineContentSchema as G, githubCallbackQuerySchema as H, dryRunAgentRuntimeConfigSchema as I, inboxPollQuerySchema as J, inboxAckFrameSchema as K, extractMentions as L, createOrgFromMeSchema as M, defaultParticipantMode as N, createAgentSchema as O, updateClientCapabilitiesSchema as Ot, defaultRuntimeConfigPayload as P, joinByInvitationSchema as Q, getMeDocResponseSchema as R, clientCapabilitiesSchema as S, stripCode as St, contextTreeSnapshotSchema as T, updateAgentRuntimeConfigSchema as Tt, githubDevCallbackQuerySchema as U, githubAppInstallationPermissionsSchema as V, githubStartQuerySchema as W, isRedactedEnvValue as X, isOrgSettingNamespace as Y, isReservedAgentName as Z, agentBindRequestSchema as _, sendToAgentSchema as _t, AGENT_TYPES as a, paginationQuerySchema as at, agentTypeSchema as b, sessionReconcileRequestSchema as bt, DEFAULT_RUNTIME_PROVIDER as c, questionAnswerMessageContentSchema as ct, MENTION_REGEX as d, refreshTokenSchema as dt, listMeChatsQuerySchema as et, NOTIFICATION_TYPES as f, runtimeStateMessageSchema as ft, addParticipantSchema as g, sendMessageSchema as gt, addMeChatParticipantsSchema as h, selfServiceFeishuBotSchema as ht, AGENT_STATUSES as i, onboardingEventSchema as it, createMemberSchema as j, wsAuthFrameSchema as jt, createChatSchema as k, updateMemberSchema as kt, GITHUB_ENTITY_TYPES as l, questionMessageContentSchema as lt, WS_AUTH_FRAME_TIMEOUT_MS as m, scanMentionTokens as mt, AGENT_NAME_REGEX as n, messageSourceSchema as nt, AGENT_VISIBILITY as o, patchChatEngagementSchema as ot, ORG_SETTINGS_NAMESPACES as p, safeRedirectPath as pt, inboxDeliverFrameSchema as q, AGENT_SELECTOR_HEADER as r, notificationQuerySchema as rt, CHAT_ENGAGEMENT_STATUSES as s, patchOnboardingSchema as st, AGENT_BIND_REJECT_REASONS as t, loginSchema as tt, LIVE_ACTIVITY_STALE_MS as u, rebindAgentSchema as ut, agentPinnedMessageSchema as v, sessionEventMessageSchema as vt, connectTokenExchangeSchema as w, updateAdapterConfigSchema as wt, chatMetadataSchema as x, sessionStateMessageSchema as xt, agentRuntimeConfigPayloadSchema as y, sessionEventSchema as yt, getMeDocSchema as z };
@@ -0,0 +1,11 @@
1
+ -- Add avatar_color_token to agents.
2
+ --
3
+ -- Manager-selected avatar fill color override for the web client. One of
4
+ -- "hue-0".."hue-7", matching the `--avatar-hue-*` CSS tokens in
5
+ -- `packages/web/src/index.css`. NULL means "auto" — the renderer falls
6
+ -- back to the deterministic djb2 hash of `uuid` (the previous default).
7
+ --
8
+ -- Nullable, no default, no backfill: existing rows continue to render the
9
+ -- hashed color until their manager picks something explicit. This is a
10
+ -- pure additive change; no NOT NULL constraint, no enum, no FK.
11
+ ALTER TABLE "agents" ADD COLUMN "avatar_color_token" text;
@@ -0,0 +1,17 @@
1
+ -- Add inline avatar image storage to agents.
2
+ --
3
+ -- The PRD chose PG bytea over object storage for the first cut: clients
4
+ -- always pre-resize to 256×256 WEBP (typically < 50 KB), and a few KB ×
5
+ -- N agents per org sits well within row-size budgets. Switching to S3/R2
6
+ -- later is a follow-up migration; the column shape stays the same except
7
+ -- avatar_image_data flips to NULL and avatar_image_url moves to a real
8
+ -- external URL.
9
+ --
10
+ -- All three columns are nullable and move together — either all three
11
+ -- carry an image (data + mime + updated_at) or all are NULL. The service
12
+ -- layer enforces that invariant on writes; SQL keeps it loose so a partial
13
+ -- backfill / restore doesn't break inserts.
14
+ ALTER TABLE "agents"
15
+ ADD COLUMN "avatar_image_data" bytea,
16
+ ADD COLUMN "avatar_image_mime" text,
17
+ ADD COLUMN "avatar_image_updated_at" timestamp with time zone;
@@ -309,6 +309,20 @@
309
309
  "when": 1779494400000,
310
310
  "tag": "0043_onboarding_completed_at",
311
311
  "breakpoints": true
312
+ },
313
+ {
314
+ "idx": 44,
315
+ "version": "7",
316
+ "when": 1779580800000,
317
+ "tag": "0044_agent_avatar_color",
318
+ "breakpoints": true
319
+ },
320
+ {
321
+ "idx": 45,
322
+ "version": "7",
323
+ "when": 1779667200000,
324
+ "tag": "0045_agent_avatar_image",
325
+ "breakpoints": true
312
326
  }
313
327
  ]
314
328
  }
@@ -1,6 +1,6 @@
1
1
  import { r as __exportAll } from "./chunk-BSw8zbkd.mjs";
2
2
  import { t as cliFetch } from "./cli-fetch--tiwKm5S.mjs";
3
- import { r as AGENT_SELECTOR_HEADER } from "./dist-C8yStx2L.mjs";
3
+ import { r as AGENT_SELECTOR_HEADER } from "./dist-1XGLJMOq.mjs";
4
4
  //#region src/core/feishu.ts
5
5
  var feishu_exports = /* @__PURE__ */ __exportAll({
6
6
  bindFeishuBot: () => bindFeishuBot,
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  import "./observability-BAScT_5S-BcW9HgkG.mjs";
2
- import { A as checkDocker, B as isServiceSupported, E as checkAgentConfigs, F as checkWebSocket, G as uninstallClientService, H as restartClientService, I as printResults, J as stopPostgres, K as ensurePostgres, M as checkServerConfig, N as checkServerHealth, O as checkClientConfig, P as checkServerReachable, R as getClientServiceStatus, S as runHomeMigration, T as runMigrations, U as startClientService, V as resolveCliInvocation, W as stopClientService, X as handleClientOrgMismatch, Y as ClientRuntime, Z as rotateClientIdWithBackup, _ as formatCheckReport, b as onboardCreate, d as startServer, g as promptMissingFields, h as promptAddAgent, j as checkNodeVersion, k as checkDatabase, lt as FirstTreeHubSDK, m as isInteractive, n as deriveHubUrlFromToken, nt as hasUser, q as isDockerAvailable, t as HubUrlDerivationError, tt as createOwner, ut as SdkError, y as onboardCheck, z as installClientService } from "./saas-connect-Bb5LR4y6.mjs";
2
+ import { A as checkDocker, B as isServiceSupported, E as checkAgentConfigs, F as checkWebSocket, G as uninstallClientService, H as restartClientService, I as printResults, J as stopPostgres, K as ensurePostgres, M as checkServerConfig, N as checkServerHealth, O as checkClientConfig, P as checkServerReachable, R as getClientServiceStatus, S as runHomeMigration, T as runMigrations, U as startClientService, V as resolveCliInvocation, W as stopClientService, X as handleClientOrgMismatch, Y as ClientRuntime, Z as rotateClientIdWithBackup, _ as formatCheckReport, b as onboardCreate, d as startServer, g as promptMissingFields, h as promptAddAgent, j as checkNodeVersion, k as checkDatabase, lt as FirstTreeHubSDK, m as isInteractive, n as deriveHubUrlFromToken, nt as hasUser, q as isDockerAvailable, t as HubUrlDerivationError, tt as createOwner, ut as SdkError, y as onboardCheck, z as installClientService } from "./saas-connect-BBRxjmBS.mjs";
3
3
  import "./logger-core-BTmvdflj-DjW8FM4T.mjs";
4
- import { a as ensureFreshAdminToken, c as resolveServerUrl, i as ensureFreshAccessToken, n as AuthRefreshRateLimitedError, s as resolveAccessToken, t as AuthRefreshFailedError } from "./bootstrap-Cya2OoHz.mjs";
4
+ import { a as ensureFreshAdminToken, c as resolveServerUrl, i as ensureFreshAccessToken, n as AuthRefreshRateLimitedError, s as resolveAccessToken, t as AuthRefreshFailedError } from "./bootstrap-C15ZBOCC.mjs";
5
5
  import { i as blank, s as status } from "./cli-fetch--tiwKm5S.mjs";
6
- import "./dist-C8yStx2L.mjs";
7
- import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-D_vnqC6a.mjs";
6
+ import "./dist-1XGLJMOq.mjs";
7
+ import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-BGx71p5s.mjs";
8
8
  import "./errors-LPcARA4K-Dbrptiyz.mjs";
9
9
  import "./src-DNBS5Yjj.mjs";
10
- import "./client-BH4CmUL0-CybE3kuP.mjs";
10
+ import "./client-CzXmweS9-DhUiuQvL.mjs";
11
11
  import "./invitation-DZO4NX3P-BPxTeHf-.mjs";
12
12
  export { AuthRefreshFailedError, AuthRefreshRateLimitedError, ClientRuntime, FirstTreeHubSDK, HubUrlDerivationError, SdkError, bindFeishuBot, bindFeishuUser, blank, checkAgentConfigs, checkClientConfig, checkDatabase, checkDocker, checkNodeVersion, checkServerConfig, checkServerHealth, checkServerReachable, checkWebSocket, createOwner, deriveHubUrlFromToken, ensureFreshAccessToken, ensureFreshAdminToken, ensurePostgres, formatCheckReport, getClientServiceStatus, handleClientOrgMismatch, hasUser, installClientService, isDockerAvailable, isInteractive, isServiceSupported, onboardCheck, onboardCreate, printResults, promptAddAgent, promptMissingFields, resolveAccessToken, resolveCliInvocation, resolveServerUrl, restartClientService, rotateClientIdWithBackup, runHomeMigration, runMigrations, startClientService, startServer, status, stopClientService, stopPostgres, uninstallClientService };
@@ -1,4 +1,4 @@
1
- import "./dist-C8yStx2L.mjs";
1
+ import "./dist-1XGLJMOq.mjs";
2
2
  import "./errors-LPcARA4K-Dbrptiyz.mjs";
3
3
  import { s as previewInvitation } from "./invitation-DZO4NX3P-BPxTeHf-.mjs";
4
4
  export { previewInvitation };