@agent-team-foundation/first-tree-hub 0.10.2 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -582,8 +582,7 @@ const serverConfigSchema = defineConfig({
582
582
  clientSecret: field(z.string(), {
583
583
  env: "FIRST_TREE_HUB_GITHUB_OAUTH_CLIENT_SECRET",
584
584
  secret: true
585
- }),
586
- devCallbackEnabled: field(z.boolean().default(false), { env: "FIRST_TREE_HUB_GITHUB_OAUTH_DEV_CALLBACK" })
585
+ })
587
586
  }) }),
588
587
  cors: optional({ origin: field(z.string(), { env: "FIRST_TREE_HUB_CORS_ORIGIN" }) }),
589
588
  rateLimit: optional({
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import "../observability-DPyf745N-BSc8QNcR.mjs";
3
- import { $ as FirstTreeHubSDK, A as checkWebSocket, B as ClientRuntime, C as checkClientConfig, D as checkServerConfig, E as checkNodeVersion, G as resolveReplyToFromEnv, K as fail, M as getClientServiceStatus, N as installClientService, O as checkServerHealth, P as isServiceSupported, Q as ClientOrgMismatchError, S as checkBackgroundService, T as checkDocker, U as createOwner, V as handleClientOrgMismatch, X as setJsonMode, Y as print, _ as runHomeMigration, a as declineUpdate, b as runMigrations, c as COMMAND_VERSION, d as promptMissingFields, et as SdkError, f as formatCheckReport, g as saveOnboardState, h as onboardCreate, i as createExecuteUpdate, it as configureClientLoggerForService, j as printResults, k as checkServerReachable, l as isInteractive, m as onboardCheck, nt as cleanWorkspaces, o as promptUpdate, p as loadOnboardState, q as success, r as registerSaaSConnectCommand, rt as applyClientLoggerConfig, s as startServer, tt as SessionRegistry, u as promptAddAgent, v as createApiNameResolver, w as checkDatabase, x as checkAgentConfigs, y as migrateLocalAgentDirs, z as stopPostgres } from "../saas-connect-idjpoPTk.mjs";
3
+ import { A as checkServerHealth, C as checkAgentConfigs, D as checkDocker, E as checkDatabase, F as installClientService, G as createOwner, H as ClientRuntime, I as isServiceSupported, J as fail, M as checkWebSocket, N as printResults, O as checkNodeVersion, P as getClientServiceStatus, Q as setJsonMode, S as runMigrations, T as checkClientConfig, U as handleClientOrgMismatch, V as stopPostgres, Y as success, Z as print, _ as onboardCreate, a as declineUpdate, at as probeCapabilities, b as createApiNameResolver, c as COMMAND_VERSION, d as isInteractive, et as ClientOrgMismatchError, f as promptAddAgent, g as onboardCheck, h as loadOnboardState, i as createExecuteUpdate, it as cleanWorkspaces, j as checkServerReachable, k as checkServerConfig, l as reconcileLocalRuntimeProviders, m as formatCheckReport, nt as SdkError, o as promptUpdate, ot as applyClientLoggerConfig, p as promptMissingFields, q as resolveReplyToFromEnv, r as registerSaaSConnectCommand, rt as SessionRegistry, s as startServer, st as configureClientLoggerForService, tt as FirstTreeHubSDK, u as uploadClientCapabilities, v as saveOnboardState, w as checkBackgroundService, x as migrateLocalAgentDirs, y as runHomeMigration } from "../saas-connect-3p-vBkuY.mjs";
4
4
  import "../logger-core-BTmvdflj-DjW8FM4T.mjs";
5
- import { C as serverConfigSchema, _ as loadAgents, b as resetConfig, c as saveCredentials, d as DEFAULT_HOME_DIR, f as agentConfigSchema, g as initConfig, h as getConfigValue, i as loadCredentials, l as DEFAULT_CONFIG_DIR, n as ensureFreshAccessToken, o as resolveServerUrl, p as clientConfigSchema, r as ensureFreshAdminToken, s as saveAgentConfig, u as DEFAULT_DATA_DIR, w as setConfigValue, x as resetConfigMeta, y as readConfigFile } from "../bootstrap-Ca5Fiqz6.mjs";
6
- import "../dist-CLiN7cVS.mjs";
7
- import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-FTWnoOsc.mjs";
5
+ import { C as serverConfigSchema, _ as loadAgents, b as resetConfig, c as saveCredentials, d as DEFAULT_HOME_DIR, f as agentConfigSchema, g as initConfig, h as getConfigValue, i as loadCredentials, l as DEFAULT_CONFIG_DIR, n as ensureFreshAccessToken, o as resolveServerUrl, p as clientConfigSchema, r as ensureFreshAdminToken, s as saveAgentConfig, u as DEFAULT_DATA_DIR, w as setConfigValue, x as resetConfigMeta, y as readConfigFile } from "../bootstrap-CBAVWQUT.mjs";
6
+ import "../dist-DUCelK3Z.mjs";
7
+ import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-Boy3n8CT.mjs";
8
8
  import "../invitation-C_zAhB8x-8Khychlu.mjs";
9
9
  import { join } from "node:path";
10
10
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "node:fs";
@@ -349,7 +349,8 @@ function registerAgentCommands(program) {
349
349
  const createBody = {
350
350
  name,
351
351
  type: options.type,
352
- clientId: options.clientId
352
+ clientId: options.clientId,
353
+ runtimeProvider: options.runtime
353
354
  };
354
355
  if (options.displayName) createBody.displayName = options.displayName;
355
356
  const createRes = await fetch(`${serverUrl}/api/v1/admin/agents`, {
@@ -916,6 +917,20 @@ function registerConnectCommand(parent) {
916
917
  const msg = err instanceof Error ? err.message : String(err);
917
918
  print.status("⚠️", `agent-dir migration skipped: ${msg}`);
918
919
  }
920
+ let probedCapabilities = null;
921
+ try {
922
+ const accessToken = await ensureFreshAccessToken();
923
+ probedCapabilities = await probeCapabilities();
924
+ await reconcileLocalRuntimeProviders({
925
+ serverUrl: config.server.url,
926
+ accessToken,
927
+ agentsDir,
928
+ log: (level, msg) => print.status(level === "warn" ? "⚠️" : "•", msg)
929
+ });
930
+ } catch (err) {
931
+ const msg = err instanceof Error ? err.message : String(err);
932
+ print.status("⚠️", `runtime-provider reconcile skipped: ${msg}`);
933
+ }
919
934
  const agents = loadAgents({
920
935
  schema: agentConfigSchema,
921
936
  agentsDir
@@ -930,6 +945,18 @@ function registerConnectCommand(parent) {
930
945
  });
931
946
  for (const [name, agentConfig] of agents) runtime.addAgent(name, agentConfig);
932
947
  await runtime.start();
948
+ if (probedCapabilities) try {
949
+ const accessToken = await ensureFreshAccessToken();
950
+ await uploadClientCapabilities({
951
+ serverUrl: config.server.url,
952
+ accessToken,
953
+ clientId: config.client.id,
954
+ capabilities: probedCapabilities
955
+ });
956
+ } catch (err) {
957
+ const msg = err instanceof Error ? err.message : String(err);
958
+ print.status("⚠️", `capabilities upload skipped: ${msg}`);
959
+ }
933
960
  runtime.watchAgentsDir(agentsDir);
934
961
  const shutdown = async () => {
935
962
  print.line("\n Shutting down...\n");
@@ -989,6 +1016,20 @@ function registerClientCommands(program) {
989
1016
  const msg = err instanceof Error ? err.message : String(err);
990
1017
  print.status("⚠️", `agent-dir migration skipped: ${msg}`);
991
1018
  }
1019
+ let probedCapabilities = null;
1020
+ try {
1021
+ const accessToken = await ensureFreshAccessToken();
1022
+ probedCapabilities = await probeCapabilities();
1023
+ await reconcileLocalRuntimeProviders({
1024
+ serverUrl: config.server.url,
1025
+ accessToken,
1026
+ agentsDir,
1027
+ log: (level, msg) => print.status(level === "warn" ? "⚠️" : "•", msg)
1028
+ });
1029
+ } catch (err) {
1030
+ const msg = err instanceof Error ? err.message : String(err);
1031
+ print.status("⚠️", `runtime-provider reconcile skipped: ${msg}`);
1032
+ }
992
1033
  const agents = loadAgents({
993
1034
  schema: agentConfigSchema,
994
1035
  agentsDir
@@ -1005,6 +1046,18 @@ function registerClientCommands(program) {
1005
1046
  });
1006
1047
  for (const [name, agentConfig] of agents) runtime.addAgent(name, agentConfig);
1007
1048
  await runtime.start();
1049
+ if (probedCapabilities) try {
1050
+ const accessToken = await ensureFreshAccessToken();
1051
+ await uploadClientCapabilities({
1052
+ serverUrl: config.server.url,
1053
+ accessToken,
1054
+ clientId: config.client.id,
1055
+ capabilities: probedCapabilities
1056
+ });
1057
+ } catch (err) {
1058
+ const msg = err instanceof Error ? err.message : String(err);
1059
+ print.status("⚠️", `capabilities upload skipped: ${msg}`);
1060
+ }
1008
1061
  runtime.watchAgentsDir(agentsDir);
1009
1062
  const shutdown = async () => {
1010
1063
  print.line("\n Shutting down...\n");
@@ -1212,13 +1265,13 @@ function isSecretField(schema, dotPath) {
1212
1265
  //#region src/commands/onboard.ts
1213
1266
  async function promptMissing(args) {
1214
1267
  if (!args.server) try {
1215
- const { resolveServerUrl } = await import("../bootstrap-Ca5Fiqz6.mjs").then((n) => n.t);
1268
+ const { resolveServerUrl } = await import("../bootstrap-CBAVWQUT.mjs").then((n) => n.t);
1216
1269
  resolveServerUrl();
1217
1270
  } catch {
1218
1271
  args.server = await input({ message: "Hub server URL:" });
1219
1272
  saveOnboardState(args);
1220
1273
  }
1221
- const { loadCredentials } = await import("../bootstrap-Ca5Fiqz6.mjs").then((n) => n.t);
1274
+ const { loadCredentials } = await import("../bootstrap-CBAVWQUT.mjs").then((n) => n.t);
1222
1275
  if (!loadCredentials()) throw new Error("No saved credentials. Run `first-tree-hub client connect <server-url>` before onboarding.");
1223
1276
  if (!args.id) {
1224
1277
  args.id = await input({ message: "Agent ID:" });
@@ -160,14 +160,16 @@ const AGENT_BIND_REJECT_REASONS = {
160
160
  NOT_OWNED: "not_owned",
161
161
  AGENT_SUSPENDED: "agent_suspended",
162
162
  WRONG_ORG: "wrong_org",
163
- UNKNOWN_AGENT: "unknown_agent"
163
+ UNKNOWN_AGENT: "unknown_agent",
164
+ RUNTIME_PROVIDER_MISMATCH: "runtime_provider_mismatch"
164
165
  };
165
166
  z.enum([
166
167
  "wrong_client",
167
168
  "not_owned",
168
169
  "agent_suspended",
169
170
  "wrong_org",
170
- "unknown_agent"
171
+ "unknown_agent",
172
+ "runtime_provider_mismatch"
171
173
  ]);
172
174
  /** Header used on agent-scoped HTTP calls to select which managed agent the JWT acts as. */
173
175
  const AGENT_SELECTOR_HEADER = "x-agent-id";
@@ -195,6 +197,8 @@ z.object({
195
197
  }),
196
198
  clients: z.number().int()
197
199
  });
200
+ const runtimeProviderSchema = z.enum(["claude-code", "codex"]);
201
+ const DEFAULT_RUNTIME_PROVIDER = "claude-code";
198
202
  const AGENT_TYPES = {
199
203
  HUMAN: "human",
200
204
  PERSONAL_ASSISTANT: "personal_assistant",
@@ -254,7 +258,8 @@ const createAgentSchema = z.object({
254
258
  visibility: agentVisibilitySchema.optional(),
255
259
  metadata: z.record(z.string(), z.unknown()).optional(),
256
260
  managerId: z.string().optional(),
257
- clientId: z.string().min(1).max(100).optional()
261
+ clientId: z.string().min(1).max(100).optional(),
262
+ runtimeProvider: runtimeProviderSchema.optional()
258
263
  });
259
264
  const updateAgentSchema = z.object({
260
265
  type: agentTypeSchema.optional(),
@@ -265,6 +270,18 @@ const updateAgentSchema = z.object({
265
270
  managerId: z.string().nullable().optional(),
266
271
  clientId: z.string().min(1).max(100).nullable().optional()
267
272
  });
273
+ /**
274
+ * Service-level rebind input. Admin / owner re-binds an agent to a new
275
+ * client and/or a new runtime provider in one atomic operation.
276
+ *
277
+ * `force` bypasses the capability-match check (e.g. when the client is
278
+ * offline and capabilities are stale).
279
+ */
280
+ const rebindAgentSchema = z.object({
281
+ clientId: z.string().min(1).max(100),
282
+ runtimeProvider: runtimeProviderSchema,
283
+ force: z.boolean().optional()
284
+ });
268
285
  z.object({
269
286
  uuid: z.string(),
270
287
  name: z.string().nullable(),
@@ -279,6 +296,7 @@ z.object({
279
296
  metadata: z.record(z.string(), z.unknown()),
280
297
  managerId: z.string().nullable(),
281
298
  clientId: z.string().nullable(),
299
+ runtimeProvider: runtimeProviderSchema,
282
300
  presenceStatus: presenceStatusSchema.optional(),
283
301
  createdAt: z.string(),
284
302
  updatedAt: z.string()
@@ -298,14 +316,16 @@ const agentPinnedMessageSchema = z.object({
298
316
  agentId: z.string(),
299
317
  name: z.string().nullable(),
300
318
  displayName: z.string(),
301
- agentType: agentTypeSchema
319
+ agentType: agentTypeSchema,
320
+ runtimeProvider: runtimeProviderSchema
302
321
  });
303
322
  /**
304
- * Agent runtime configuration — M1 (Claude Code only).
323
+ * Agent runtime configuration.
305
324
  *
306
325
  * Defines the 5 user-tunable field groups that the Hub centrally manages
307
326
  * and pushes down to the client runtime: prompt append, model, MCP servers,
308
- * env vars, and Git repos.
327
+ * env vars, and Git repos. Tagged by `kind` (a runtime provider) so future
328
+ * provider-specific fields can land on a dedicated variant.
309
329
  *
310
330
  * NOTE: do not co-locate with `packages/shared/src/config/` — that namespace
311
331
  * is reserved for the local YAML config (`agent.yaml` / server / client) and
@@ -349,9 +369,11 @@ const gitRepoSchema = z.object({
349
369
  localPath: z.string().min(1).optional()
350
370
  });
351
371
  /**
352
- * Base shape (no refinements) used for `.partial()` derivations such as the
353
- * PATCH payload schema. Zod 4 forbids `.partial()` on a refined object, so we
354
- * keep refinements on a separate full schema below.
372
+ * Untagged base shape 5 user-tunable fields, no `kind` discriminator.
373
+ * Used for `.partial()` derivations on the PATCH side, where `kind` is
374
+ * pinned to `agents.runtime_provider` and never changes via config PATCH.
375
+ * Zod 4 forbids `.partial()` on a refined object, so we keep refinements
376
+ * on the tagged schema below.
355
377
  */
356
378
  const agentRuntimeConfigPayloadShape = z.object({
357
379
  prompt: promptConfigSchema.default({ append: "" }),
@@ -360,6 +382,17 @@ const agentRuntimeConfigPayloadShape = z.object({
360
382
  env: z.array(envEntrySchema).default([]),
361
383
  gitRepos: z.array(gitRepoSchema).default([])
362
384
  });
385
+ /**
386
+ * Tagged variants — read-side, full payload including `kind`. Adding a new
387
+ * provider means adding a variant here, plus a handler factory and a
388
+ * capability probe module on the client side.
389
+ *
390
+ * Provider-specific fields (e.g. codex `sandboxMode`) belong on the
391
+ * matching variant, not on the base shape.
392
+ */
393
+ const claudeRuntimeConfigPayloadShape = agentRuntimeConfigPayloadShape.extend({ kind: z.literal("claude-code") });
394
+ const codexRuntimeConfigPayloadShape = agentRuntimeConfigPayloadShape.extend({ kind: z.literal("codex") });
395
+ const taggedPayloadUnion = z.discriminatedUnion("kind", [claudeRuntimeConfigPayloadShape, codexRuntimeConfigPayloadShape]);
363
396
  const payloadDuplicatesRefinement = (payload, ctx) => {
364
397
  const seenMcp = /* @__PURE__ */ new Set();
365
398
  payload.mcpServers.forEach((server, idx) => {
@@ -404,15 +437,54 @@ const payloadDuplicatesRefinement = (payload, ctx) => {
404
437
  seenPaths.add(path);
405
438
  });
406
439
  };
407
- const agentRuntimeConfigPayloadSchema = agentRuntimeConfigPayloadShape.superRefine(payloadDuplicatesRefinement);
408
- /** Default payload used when creating a fresh agent. */
440
+ /**
441
+ * Read-side full payload schema. Rows persisted before 0026 do not carry
442
+ * `kind`; `z.preprocess` injects `"claude-code"` so they parse cleanly into
443
+ * the claude variant. The service layer separately enforces
444
+ * `payload.kind === agents.runtime_provider` on writes.
445
+ */
446
+ const agentRuntimeConfigPayloadSchema = z.preprocess((input) => {
447
+ if (input && typeof input === "object" && !Array.isArray(input) && !("kind" in input)) return {
448
+ ...input,
449
+ kind: "claude-code"
450
+ };
451
+ return input;
452
+ }, taggedPayloadUnion).superRefine((payload, ctx) => {
453
+ payloadDuplicatesRefinement(payload, ctx);
454
+ });
455
+ /** Default payload used when creating a fresh claude-code agent. */
409
456
  const DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD = {
457
+ kind: "claude-code",
410
458
  prompt: { append: "" },
411
459
  model: "opus",
412
460
  mcpServers: [],
413
461
  env: [],
414
462
  gitRepos: []
415
463
  };
464
+ /**
465
+ * Default payload for a fresh codex agent. Same 5 fields as claude-code.
466
+ * `model` is left empty by default so the Codex CLI picks one matching the
467
+ * user's auth mode — `gpt-5-codex` is rejected by ChatGPT-account auth, while
468
+ * an empty string lets the SDK fall through to its built-in default.
469
+ */
470
+ const DEFAULT_CODEX_RUNTIME_CONFIG_PAYLOAD = {
471
+ kind: "codex",
472
+ prompt: { append: "" },
473
+ model: "",
474
+ mcpServers: [],
475
+ env: [],
476
+ gitRepos: []
477
+ };
478
+ /**
479
+ * Default payload selector by runtime provider.
480
+ */
481
+ function defaultRuntimeConfigPayload(provider) {
482
+ switch (provider) {
483
+ case "codex": return { ...DEFAULT_CODEX_RUNTIME_CONFIG_PAYLOAD };
484
+ case "claude-code": return { ...DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD };
485
+ default: return { ...DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD };
486
+ }
487
+ }
416
488
  const agentRuntimeConfigSchema = z.object({
417
489
  agentId: z.string(),
418
490
  version: z.number().int().positive(),
@@ -531,6 +603,37 @@ const clientRegisterSchema = z.object({
531
603
  os: z.string().max(50).optional(),
532
604
  sdkVersion: z.string().max(50).optional()
533
605
  });
606
+ const capabilityStateSchema = z.enum([
607
+ "ok",
608
+ "missing",
609
+ "unauthenticated",
610
+ "error"
611
+ ]);
612
+ const capabilityAuthMethodSchema = z.enum([
613
+ "api_key",
614
+ "oauth",
615
+ "auth_json",
616
+ "none"
617
+ ]);
618
+ const capabilityEntrySchema = z.object({
619
+ state: capabilityStateSchema,
620
+ available: z.boolean(),
621
+ authenticated: z.boolean(),
622
+ sdkVersion: z.string().nullable().optional(),
623
+ authMethod: capabilityAuthMethodSchema,
624
+ error: z.string().nullable().optional(),
625
+ detectedAt: z.string()
626
+ });
627
+ /**
628
+ * Capabilities snapshot keyed by runtime provider name. Recorded as a plain
629
+ * `Record<string, CapabilityEntry>` — every entry is optional (a client may
630
+ * report only the runtimes it actually probed) and the key set evolves
631
+ * naturally as new providers ship without a schema migration. Service-layer
632
+ * lookups (`agents.runtime_provider ∈ keys(capabilities)`) treat the keys
633
+ * as `RuntimeProvider` strings.
634
+ */
635
+ const clientCapabilitiesSchema = z.record(z.string(), capabilityEntrySchema);
636
+ const updateClientCapabilitiesSchema = z.object({ capabilities: clientCapabilitiesSchema });
534
637
  const paginationQuerySchema = z.object({
535
638
  limit: z.coerce.number().int().min(1).max(100).default(20),
536
639
  cursor: z.string().optional()
@@ -801,7 +904,7 @@ const githubCallbackQuerySchema = z.object({
801
904
  });
802
905
  /**
803
906
  * Dev-only callback to bypass the GitHub round-trip — sign in as a stub
804
- * Github user. Gated by NODE_ENV !== 'production' AND `oauth.github.devCallbackEnabled`.
907
+ * Github user. Gated by NODE_ENV !== 'production'; production always 404s.
805
908
  */
806
909
  const githubDevCallbackQuerySchema = z.object({
807
910
  githubId: z.string().min(1),
@@ -1147,4 +1250,4 @@ z.object({
1147
1250
  serverTimeMs: z.number().int().nonnegative()
1148
1251
  }).passthrough();
1149
1252
  //#endregion
1150
- export { sendMessageSchema as $, createOrganizationSchema as A, isRedactedEnvValue as B, connectTokenExchangeSchema as C, createChatSchema as D, createAgentSchema as E, githubCallbackQuerySchema as F, messageSourceSchema as G, joinByInvitationSchema as H, githubDevCallbackQuerySchema as I, refreshTokenSchema as J, notificationQuerySchema as K, githubStartQuerySchema as L, delegateFeishuUserSchema as M, dryRunAgentRuntimeConfigSchema as N, createMemberSchema as O, extractMentions as P, selfServiceFeishuBotSchema as Q, imageInlineContentSchema as R, clientRegisterSchema as S, createAdapterMappingSchema as T, linkTaskChatSchema as U, isReservedAgentName as V, loginSchema as W, safeRedirectPath as X, runtimeStateMessageSchema as Y, scanMentionTokens as Z, adminUpdateTaskSchema as _, AGENT_STATUSES as a, sessionStateMessageSchema as at, agentRuntimeConfigPayloadSchema as b, DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD as c, updateAdapterConfigSchema as ct, TASK_HEALTH_SIGNALS as d, updateChatSchema as dt, sendToAgentSchema as et, TASK_STATUSES as f, updateMemberSchema as ft, adminCreateTaskSchema as g, wsAuthFrameSchema as gt, addParticipantSchema as h, updateTaskStatusSchema as ht, AGENT_SOURCES as i, sessionReconcileRequestSchema as it, createTaskSchema as j, createOrgFromMeSchema as k, SYSTEM_CONFIG_DEFAULTS as l, updateAgentRuntimeConfigSchema as lt, WS_AUTH_FRAME_TIMEOUT_MS as m, updateSystemConfigSchema as mt, AGENT_NAME_REGEX as n, sessionEventMessageSchema as nt, AGENT_TYPES as o, switchOrgSchema as ot, TASK_TERMINAL_STATUSES as p, updateOrganizationSchema as pt, paginationQuerySchema as q, AGENT_SELECTOR_HEADER as r, sessionEventSchema as rt, AGENT_VISIBILITY as s, taskListQuerySchema as st, AGENT_BIND_REJECT_REASONS as t, sessionCompletionMessageSchema as tt, TASK_CREATOR_TYPES as u, updateAgentSchema as ut, agentBindRequestSchema as v, createAdapterConfigSchema as w, agentTypeSchema as x, agentPinnedMessageSchema as y, inboxPollQuerySchema as z };
1253
+ export { safeRedirectPath as $, createOrgFromMeSchema as A, imageInlineContentSchema as B, clientRegisterSchema as C, createAgentSchema as D, createAdapterMappingSchema as E, dryRunAgentRuntimeConfigSchema as F, linkTaskChatSchema as G, isRedactedEnvValue as H, extractMentions as I, notificationQuerySchema as J, loginSchema as K, githubCallbackQuerySchema as L, createTaskSchema as M, defaultRuntimeConfigPayload as N, createChatSchema as O, delegateFeishuUserSchema as P, runtimeStateMessageSchema as Q, githubDevCallbackQuerySchema as R, clientCapabilitiesSchema as S, createAdapterConfigSchema as T, isReservedAgentName as U, inboxPollQuerySchema as V, joinByInvitationSchema as W, rebindAgentSchema as X, paginationQuerySchema as Y, refreshTokenSchema as Z, adminUpdateTaskSchema as _, updateOrganizationSchema as _t, AGENT_STATUSES as a, sessionEventMessageSchema as at, agentRuntimeConfigPayloadSchema as b, wsAuthFrameSchema as bt, DEFAULT_RUNTIME_PROVIDER as c, sessionStateMessageSchema as ct, TASK_HEALTH_SIGNALS as d, updateAdapterConfigSchema as dt, scanMentionTokens as et, TASK_STATUSES as f, updateAgentRuntimeConfigSchema as ft, adminCreateTaskSchema as g, updateMemberSchema as gt, addParticipantSchema as h, updateClientCapabilitiesSchema as ht, AGENT_SOURCES as i, sessionCompletionMessageSchema as it, createOrganizationSchema as j, createMemberSchema as k, SYSTEM_CONFIG_DEFAULTS as l, switchOrgSchema as lt, WS_AUTH_FRAME_TIMEOUT_MS as m, updateChatSchema as mt, AGENT_NAME_REGEX as n, sendMessageSchema as nt, AGENT_TYPES as o, sessionEventSchema as ot, TASK_TERMINAL_STATUSES as p, updateAgentSchema as pt, messageSourceSchema as q, AGENT_SELECTOR_HEADER as r, sendToAgentSchema as rt, AGENT_VISIBILITY as s, sessionReconcileRequestSchema as st, AGENT_BIND_REJECT_REASONS as t, selfServiceFeishuBotSchema as tt, TASK_CREATOR_TYPES as u, taskListQuerySchema as ut, agentBindRequestSchema as v, updateSystemConfigSchema as vt, connectTokenExchangeSchema as w, agentTypeSchema as x, agentPinnedMessageSchema as y, updateTaskStatusSchema as yt, githubStartQuerySchema as z };
@@ -0,0 +1,10 @@
1
+ -- Add runtime_provider to agents.
2
+ --
3
+ -- Tags each agent with the runtime that drives it (e.g. "claude-code", "codex").
4
+ -- DEFAULT 'claude-code' backfills every existing row so the NOT NULL constraint
5
+ -- is safe to land in a single step. Hub deploys are stop-migrate-restart, not
6
+ -- rolling, so we don't need a two-phase add (nullable → backfill → not null).
7
+ --
8
+ -- Capabilities reporting reuses the existing `clients.metadata` jsonb column
9
+ -- under the `capabilities` subkey (Option C); no SQL change for clients.
10
+ ALTER TABLE "agents" ADD COLUMN "runtime_provider" text DEFAULT 'claude-code' NOT NULL;
@@ -0,0 +1,12 @@
1
+ -- Partial unique index: each user can hold at most one github identity.
2
+ --
3
+ -- Defense-in-depth. The (provider, identifier) UNIQUE catches duplicates
4
+ -- of the SAME githubId, but does not stop a single user from collecting
5
+ -- multiple DIFFERENT githubIds (e.g. a future "merge accounts" or
6
+ -- "rebind" flow that misfires, or a one-off SQL migration that errs).
7
+ -- This index makes any such double-bind fail atomically with a
8
+ -- unique-violation at the storage layer.
9
+
10
+ CREATE UNIQUE INDEX IF NOT EXISTS "uq_auth_identities_user_github"
11
+ ON "auth_identities" ("user_id")
12
+ WHERE "provider" = 'github';
@@ -190,6 +190,20 @@
190
190
  "when": 1777507200000,
191
191
  "tag": "0026_saas_onboarding",
192
192
  "breakpoints": true
193
+ },
194
+ {
195
+ "idx": 27,
196
+ "version": "7",
197
+ "when": 1777593600000,
198
+ "tag": "0027_runtime_provider",
199
+ "breakpoints": true
200
+ },
201
+ {
202
+ "idx": 28,
203
+ "version": "7",
204
+ "when": 1777680000000,
205
+ "tag": "0028_auth_identity_user_github_unique",
206
+ "breakpoints": true
193
207
  }
194
208
  ]
195
209
  }
@@ -1,5 +1,5 @@
1
1
  import { d as __exportAll } from "./esm-CYu4tXXn.mjs";
2
- import { r as AGENT_SELECTOR_HEADER } from "./dist-CLiN7cVS.mjs";
2
+ import { r as AGENT_SELECTOR_HEADER } from "./dist-DUCelK3Z.mjs";
3
3
  //#region src/core/feishu.ts
4
4
  var feishu_exports = /* @__PURE__ */ __exportAll({
5
5
  bindFeishuBot: () => bindFeishuBot,
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import "./observability-DPyf745N-BSc8QNcR.mjs";
2
- import { $ as FirstTreeHubSDK, A as checkWebSocket, B as ClientRuntime, C as checkClientConfig, D as checkServerConfig, E as checkNodeVersion, F as resolveCliInvocation, H as rotateClientIdWithBackup, I as uninstallClientService, J as blank, L as ensurePostgres, M as getClientServiceStatus, N as installClientService, O as checkServerHealth, P as isServiceSupported, R as isDockerAvailable, T as checkDocker, U as createOwner, V as handleClientOrgMismatch, W as hasUser, Z as status, _ as runHomeMigration, b as runMigrations, d as promptMissingFields, et as SdkError, f as formatCheckReport, h as onboardCreate, j as printResults, k as checkServerReachable, l as isInteractive, m as onboardCheck, n as deriveHubUrlFromToken, s as startServer, t as HubUrlDerivationError, u as promptAddAgent, w as checkDatabase, x as checkAgentConfigs, z as stopPostgres } from "./saas-connect-idjpoPTk.mjs";
2
+ import { $ as status, A as checkServerHealth, B as isDockerAvailable, C as checkAgentConfigs, D as checkDocker, E as checkDatabase, F as installClientService, G as createOwner, H as ClientRuntime, I as isServiceSupported, K as hasUser, L as resolveCliInvocation, M as checkWebSocket, N as printResults, O as checkNodeVersion, P as getClientServiceStatus, R as uninstallClientService, S as runMigrations, T as checkClientConfig, U as handleClientOrgMismatch, V as stopPostgres, W as rotateClientIdWithBackup, X as blank, _ as onboardCreate, d as isInteractive, f as promptAddAgent, g as onboardCheck, j as checkServerReachable, k as checkServerConfig, m as formatCheckReport, n as deriveHubUrlFromToken, nt as SdkError, p as promptMissingFields, s as startServer, t as HubUrlDerivationError, tt as FirstTreeHubSDK, y as runHomeMigration, z as ensurePostgres } from "./saas-connect-3p-vBkuY.mjs";
3
3
  import "./logger-core-BTmvdflj-DjW8FM4T.mjs";
4
- import { a as resolveAccessToken, n as ensureFreshAccessToken, o as resolveServerUrl, r as ensureFreshAdminToken } from "./bootstrap-Ca5Fiqz6.mjs";
5
- import "./dist-CLiN7cVS.mjs";
6
- import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-FTWnoOsc.mjs";
4
+ import { a as resolveAccessToken, n as ensureFreshAccessToken, o as resolveServerUrl, r as ensureFreshAdminToken } from "./bootstrap-CBAVWQUT.mjs";
5
+ import "./dist-DUCelK3Z.mjs";
6
+ import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-Boy3n8CT.mjs";
7
7
  import "./invitation-C_zAhB8x-8Khychlu.mjs";
8
8
  export { 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, rotateClientIdWithBackup, runHomeMigration, runMigrations, startServer, status, stopPostgres, uninstallClientService };
@@ -1,3 +1,3 @@
1
- import "./dist-CLiN7cVS.mjs";
1
+ import "./dist-DUCelK3Z.mjs";
2
2
  import { _ as rotateInvitation, h as previewInvitation } from "./invitation-C_zAhB8x-8Khychlu.mjs";
3
3
  export { previewInvitation, rotateInvitation };