@agent-team-foundation/first-tree-hub 0.12.2 → 0.12.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.
@@ -1,6 +1,6 @@
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 success, b as onboardCreate, c as detectInstallMode, ct as FirstTreeHubSDK, d as startServer, dt as cleanWorkspaces, et as removeLocalAgent, f as reconcileLocalRuntimeProviders, ft as probeCapabilities, g as promptMissingFields, h as promptAddAgent, i as createExecuteUpdate, it as fail, j as checkNodeVersion, k as checkDatabase, l as fetchLatestVersion, lt as SdkError, m as isInteractive, mt as configureClientLoggerForService, o as promptUpdate, ot as ClientOrgMismatchError, p as uploadClientCapabilities, pt as applyClientLoggerConfig, r as registerSaaSConnectCommand, rt as resolveReplyToFromEnv, s as PACKAGE_NAME, st as ClientUserMismatchError, tt as createOwner, u as installGlobalLatest, ut as SessionRegistry, v as loadOnboardState, w as migrateLocalAgentDirs, x as saveOnboardState, y as onboardCheck, z as installClientService } from "../saas-connect-_2M4kfPR.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 success, b as onboardCreate, c as detectInstallMode, ct as FirstTreeHubSDK, d as startServer, dt as cleanWorkspaces, et as removeLocalAgent, f as reconcileLocalRuntimeProviders, ft as probeCapabilities, g as promptMissingFields, h as promptAddAgent, i as createExecuteUpdate, it as fail, j as checkNodeVersion, k as checkDatabase, l as fetchLatestVersion, lt as SdkError, m as isInteractive, mt as configureClientLoggerForService, o as promptUpdate, ot as ClientOrgMismatchError, p as uploadClientCapabilities, pt as applyClientLoggerConfig, r as registerSaaSConnectCommand, rt as resolveReplyToFromEnv, s as PACKAGE_NAME, st as ClientUserMismatchError, tt as createOwner, u as installGlobalLatest, ut as SessionRegistry, v as loadOnboardState, w as migrateLocalAgentDirs, x as saveOnboardState, y as onboardCheck, z as installClientService } from "../saas-connect-_lNV0Liy.mjs";
4
4
  import "../logger-core-BTmvdflj-DjW8FM4T.mjs";
5
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";
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";
@@ -8,7 +8,7 @@ import "../dist-DHHd2dar.mjs";
8
8
  import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-fLnwqCOs.mjs";
9
9
  import "../errors-CF5evtJt-B0NTIVPt.mjs";
10
10
  import "../src-DNBS5Yjj.mjs";
11
- import "../client-DHCSQ8kg-DjlSmE9q.mjs";
11
+ import "../client-D1TDiik_-gxtXN9bj.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";
@@ -3,5 +3,5 @@ import "./logger-core-BTmvdflj-DjW8FM4T.mjs";
3
3
  import "./dist-DHHd2dar.mjs";
4
4
  import "./errors-CF5evtJt-B0NTIVPt.mjs";
5
5
  import "./src-DNBS5Yjj.mjs";
6
- import { q as listMyPinnedAgents } from "./client-DHCSQ8kg-DjlSmE9q.mjs";
6
+ import { q as listMyPinnedAgents } from "./client-D1TDiik_-gxtXN9bj.mjs";
7
7
  export { listMyPinnedAgents };
@@ -4,7 +4,7 @@ import { a as ConflictError, i as ClientUserMismatchError, l as organizations, n
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-DHCSQ8kg.mjs
7
+ //#region ../server/dist/client-D1TDiik_.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
@@ -691,14 +691,6 @@ async function leaveChat(db, chatId, humanAgentId) {
691
691
  return db.select().from(chatParticipants).where(eq(chatParticipants.chatId, chatId));
692
692
  }
693
693
  async function findOrCreateDirectChat(db, agentAId, agentBId) {
694
- const aChats = await db.select({ chatId: chatParticipants.chatId }).from(chatParticipants).where(eq(chatParticipants.agentId, agentAId));
695
- const bChats = await db.select({ chatId: chatParticipants.chatId }).from(chatParticipants).where(eq(chatParticipants.agentId, agentBId));
696
- const bChatIds = new Set(bChats.map((r) => r.chatId));
697
- const commonChatIds = aChats.map((r) => r.chatId).filter((id) => bChatIds.has(id));
698
- if (commonChatIds.length > 0) {
699
- const directChats = await db.select().from(chats).where(and(inArray(chats.id, commonChatIds), eq(chats.type, "direct"))).orderBy(chats.createdAt, chats.id).limit(1);
700
- if (directChats.length > 0 && directChats[0]) return directChats[0];
701
- }
702
694
  const ends = await db.select({
703
695
  uuid: agents.uuid,
704
696
  organizationId: agents.organizationId,
@@ -708,12 +700,22 @@ async function findOrCreateDirectChat(db, agentAId, agentBId) {
708
700
  if (!agentA) throw new NotFoundError(`Agent "${agentAId}" not found`);
709
701
  const agentB = ends.find((a) => a.uuid === agentBId);
710
702
  if (!agentB) throw new NotFoundError(`Agent "${agentBId}" not found`);
703
+ if (agentA.organizationId !== agentB.organizationId) throw new BadRequestError(`Cannot create direct chat across organizations: agent "${agentAId}" (org "${agentA.organizationId}") vs agent "${agentBId}" (org "${agentB.organizationId}")`);
704
+ const orgId = agentA.organizationId;
705
+ const aChats = await db.select({ chatId: chatParticipants.chatId }).from(chatParticipants).where(eq(chatParticipants.agentId, agentAId));
706
+ const bChats = await db.select({ chatId: chatParticipants.chatId }).from(chatParticipants).where(eq(chatParticipants.agentId, agentBId));
707
+ const bChatIds = new Set(bChats.map((r) => r.chatId));
708
+ const commonChatIds = aChats.map((r) => r.chatId).filter((id) => bChatIds.has(id));
709
+ if (commonChatIds.length > 0) {
710
+ 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);
711
+ if (directChats.length > 0 && directChats[0]) return directChats[0];
712
+ }
711
713
  const mode = agentA.type !== "human" && agentB.type !== "human" ? "mention_only" : "full";
712
714
  const chatId = randomUUID();
713
715
  return db.transaction(async (tx) => {
714
716
  const [chat] = await tx.insert(chats).values({
715
717
  id: chatId,
716
- organizationId: agentA.organizationId,
718
+ organizationId: orgId,
717
719
  type: "direct"
718
720
  }).returning();
719
721
  await tx.insert(chatParticipants).values([{
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
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, ct as FirstTreeHubSDK, d as startServer, g as promptMissingFields, h as promptAddAgent, j as checkNodeVersion, k as checkDatabase, lt as SdkError, m as isInteractive, n as deriveHubUrlFromToken, nt as hasUser, q as isDockerAvailable, t as HubUrlDerivationError, tt as createOwner, y as onboardCheck, z as installClientService } from "./saas-connect-_2M4kfPR.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, ct as FirstTreeHubSDK, d as startServer, g as promptMissingFields, h as promptAddAgent, j as checkNodeVersion, k as checkDatabase, lt as SdkError, m as isInteractive, n as deriveHubUrlFromToken, nt as hasUser, q as isDockerAvailable, t as HubUrlDerivationError, tt as createOwner, y as onboardCheck, z as installClientService } from "./saas-connect-_lNV0Liy.mjs";
3
3
  import "./logger-core-BTmvdflj-DjW8FM4T.mjs";
4
4
  import { a as ensureFreshAdminToken, c as resolveServerUrl, i as ensureFreshAccessToken, n as AuthRefreshRateLimitedError, s as resolveAccessToken, t as AuthRefreshFailedError } from "./bootstrap-C_K2CKXC.mjs";
5
5
  import { i as blank, s as status } from "./cli-fetch--tiwKm5S.mjs";
@@ -7,6 +7,6 @@ import "./dist-DHHd2dar.mjs";
7
7
  import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-fLnwqCOs.mjs";
8
8
  import "./errors-CF5evtJt-B0NTIVPt.mjs";
9
9
  import "./src-DNBS5Yjj.mjs";
10
- import "./client-DHCSQ8kg-DjlSmE9q.mjs";
10
+ import "./client-D1TDiik_-gxtXN9bj.mjs";
11
11
  import "./invitation-Bg0TRiyx-BsZH4GCS.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 };
@@ -5,7 +5,7 @@ import { a as print, i as blank, n as CLI_USER_AGENT, r as COMMAND_VERSION, s as
5
5
  import { $ as loginSchema, A as createAgentSchema, At as updateTaskStatusSchema, B as githubCallbackQuerySchema, C as agentTypeSchema$1, Ct as updateAdapterConfigSchema, D as contextTreeSnapshotSchema, Dt as updateClientCapabilitiesSchema, E as connectTokenExchangeSchema, Et as updateChatSchema, F as createTaskSchema, G as inboxDeliverFrameSchema$1, H as githubStartQuerySchema, I as defaultRuntimeConfigPayload, J as isRedactedEnvValue, K as inboxPollQuerySchema, L as delegateFeishuUserSchema, M as createMeChatSchema, N as createMemberSchema, O as createAdapterConfigSchema, Ot as updateMemberSchema, P as createOrgFromMeSchema, Q as listMeChatsQuerySchema, R as dryRunAgentRuntimeConfigSchema, S as agentRuntimeConfigPayloadSchema$1, St as taskListQuerySchema, T as clientRegisterSchema, Tt as updateAgentSchema, U as imageInlineContentSchema, V as githubDevCallbackQuerySchema, W as inboxAckFrameSchema, X as joinByInvitationSchema, Y as isReservedAgentName$1, Z as linkTaskChatSchema, _ as addParticipantSchema, _t as sessionEventSchema$1, a as AGENT_STATUSES, b as agentBindRequestSchema, bt as stripCode, ct as refreshTokenSchema, d as TASK_CREATOR_TYPES, et as messageSourceSchema$1, f as TASK_HEALTH_SIGNALS, ft as selfServiceFeishuBotSchema, g as addMeChatParticipantsSchema, gt as sessionEventMessageSchema, h as WS_AUTH_FRAME_TIMEOUT_MS, ht as sessionCompletionMessageSchema, i as AGENT_SOURCES, it as patchOnboardingSchema, j as createChatSchema, jt as wsAuthFrameSchema, k as createAdapterMappingSchema, kt as updateOrganizationSchema, l as MENTION_REGEX, lt as runtimeStateMessageSchema, m as TASK_TERMINAL_STATUSES, mt as sendToAgentSchema, n as AGENT_NAME_REGEX$1, nt as onboardingEventSchema, o as AGENT_TYPES, p as TASK_STATUSES, pt as sendMessageSchema, q as isOrgSettingNamespace, r as AGENT_SELECTOR_HEADER$1, rt as paginationQuerySchema, s as AGENT_VISIBILITY, st as rebindAgentSchema, t as AGENT_BIND_REJECT_REASONS, tt as notificationQuerySchema, u as ORG_SETTINGS_NAMESPACES$1, ut as safeRedirectPath, v as adminCreateTaskSchema, vt as sessionReconcileRequestSchema, wt as updateAgentRuntimeConfigSchema, x as agentPinnedMessageSchema$1, xt as submitQuestionAnswerSchema, y as adminUpdateTaskSchema, yt as sessionStateMessageSchema } from "./dist-DHHd2dar.mjs";
6
6
  import { a as ConflictError, c as UnauthorizedError, i as ClientUserMismatchError$1, l as organizations, n as BadRequestError, o as ForbiddenError, r as ClientOrgMismatchError$1, s as NotFoundError, t as AppError, u as users } from "./errors-CF5evtJt-B0NTIVPt.mjs";
7
7
  import { n as init_esm, r as trace, t as esm_exports } from "./esm-iadMkGbV.mjs";
8
- import { $ as pendingQuestions, A as heartbeatClient, B as listAgentsWithRuntime, C as findOrCreateDirectChat, D as getClient, E as getChatDetail, F as joinChat, G as listClientsForOrgAdmin, H as listChats, I as leaveAsParticipant, J as markStaleAgents, K as listMessages, L as leaveChat, M as inboxEntries, N as invalidateChatAudience, O as getOnlineCount, P as joinAsParticipant, Q as notifyRecipients, R as listActiveAgentsPinnedToClient, S as ensureParticipant$1, T as getCachedAudience, U as listChatsForMember, V as listChatParticipantsWithNames, W as listClients, X as members, Y as markSupersededByChat, Z as messages, _ as createNotifier, _t as updateClientCapabilities, a as agents, at as removeParticipant, b as editMessage, c as bindAgent, ct as retireClient, d as chats, dt as serverInstances, et as recomputeChatWatchers, f as claimClient, ft as setOffline, g as createChat, gt as unbindAgent, h as clients, ht as touchAgent, i as agentVisibilityCondition, it as registerClient, j as heartbeatInstance, k as getPresence, l as chatParticipants, lt as sendMessage, m as cleanupStalePresence, mt as submitAnswer, n as agentChatSessions, nt as recomputeWatchersForMember, o as assertClientOwner, ot as resetActivity, p as cleanupStaleClients, pt as setRuntimeState, r as agentPresence, rt as registerChatMessageDispatcher, s as assertParticipant, st as resolveChatMembership, t as addParticipant, tt as recomputeWatchersForAgent, u as chatSubscriptions, ut as sendToAgent$1, v as deriveAuthState, vt as upsertSessionState, w as getActivityOverview, x as ensureCanJoin, y as disconnectClient, z as listAgentsManagedByUser } from "./client-DHCSQ8kg-DjlSmE9q.mjs";
8
+ import { $ as pendingQuestions, A as heartbeatClient, B as listAgentsWithRuntime, C as findOrCreateDirectChat, D as getClient, E as getChatDetail, F as joinChat, G as listClientsForOrgAdmin, H as listChats, I as leaveAsParticipant, J as markStaleAgents, K as listMessages, L as leaveChat, M as inboxEntries, N as invalidateChatAudience, O as getOnlineCount, P as joinAsParticipant, Q as notifyRecipients, R as listActiveAgentsPinnedToClient, S as ensureParticipant$1, T as getCachedAudience, U as listChatsForMember, V as listChatParticipantsWithNames, W as listClients, X as members, Y as markSupersededByChat, Z as messages, _ as createNotifier, _t as updateClientCapabilities, a as agents, at as removeParticipant, b as editMessage, c as bindAgent, ct as retireClient, d as chats, dt as serverInstances, et as recomputeChatWatchers, f as claimClient, ft as setOffline, g as createChat, gt as unbindAgent, h as clients, ht as touchAgent, i as agentVisibilityCondition, it as registerClient, j as heartbeatInstance, k as getPresence, l as chatParticipants, lt as sendMessage, m as cleanupStalePresence, mt as submitAnswer, n as agentChatSessions, nt as recomputeWatchersForMember, o as assertClientOwner, ot as resetActivity, p as cleanupStaleClients, pt as setRuntimeState, r as agentPresence, rt as registerChatMessageDispatcher, s as assertParticipant, st as resolveChatMembership, t as addParticipant, tt as recomputeWatchersForAgent, u as chatSubscriptions, ut as sendToAgent$1, v as deriveAuthState, vt as upsertSessionState, w as getActivityOverview, x as ensureCanJoin, y as disconnectClient, z as listAgentsManagedByUser } from "./client-D1TDiik_-gxtXN9bj.mjs";
9
9
  import { a as invitationRedemptions, c as recordRedemption, i as getActiveInvitation, l as rotateInvitation, n as ensureActiveInvitation, o as invitations, r as findActiveByToken, t as buildInviteUrl, u as uuidv7 } from "./invitation-Bg0TRiyx-BsZH4GCS.mjs";
10
10
  import { createRequire } from "node:module";
11
11
  import { ZodError, z } from "zod";
@@ -2948,8 +2948,6 @@ function installFirstTreeIntegration(options) {
2948
2948
  const integrateArgs = [
2949
2949
  "tree",
2950
2950
  "integrate",
2951
- "--source-path",
2952
- workspacePath,
2953
2951
  "--tree-path",
2954
2952
  contextTreePath,
2955
2953
  "--mode",
@@ -2984,8 +2982,13 @@ function installFirstTreeIntegration(options) {
2984
2982
  } catch (err) {
2985
2983
  const msg = err instanceof Error ? err.message : String(err);
2986
2984
  const binaryMissing = /ENOENT|not found|command not found/i.test(msg);
2985
+ const unsupportedByThisCli = /unknown (?:option|command|argument)|unrecognized option/i.test(msg);
2986
+ const shouldRetry = binaryMissing || unsupportedByThisCli;
2987
2987
  const isLastAttempt = index === attempts.length - 1;
2988
- if (binaryMissing && !isLastAttempt) continue;
2988
+ if (shouldRetry && !isLastAttempt) {
2989
+ log(`First-tree integration via ${attempt.label} unusable; falling back: ${msg.slice(0, 200)}`);
2990
+ continue;
2991
+ }
2989
2992
  log(`First-tree integration skipped (${attempt.label}): ${msg.slice(0, 200)}`);
2990
2993
  return false;
2991
2994
  }
@@ -4608,6 +4611,8 @@ const createClaudeCodeHandler = (config) => {
4608
4611
  }
4609
4612
  }
4610
4613
  const contextTreePath = config.contextTreePath ?? null;
4614
+ const contextTreeRepoUrl = config.contextTreeRepoUrl ?? null;
4615
+ const agentName = config.agentName ?? null;
4611
4616
  /**
4612
4617
  * Materialise the runtime config's `gitRepos` into worktrees under `cwd`.
4613
4618
  * Idempotent across resumes: reuses an existing Hub-managed worktree if
@@ -4675,7 +4680,8 @@ const createClaudeCodeHandler = (config) => {
4675
4680
  if (contextTreePath) installFirstTreeIntegration({
4676
4681
  workspacePath: workspace,
4677
4682
  contextTreePath,
4678
- workspaceId: sessionCtx.chatId,
4683
+ workspaceId: agentName ?? sessionCtx.chatId,
4684
+ treeRepoUrl: contextTreeRepoUrl ?? void 0,
4679
4685
  log: (msg) => sessionCtx.log(msg)
4680
4686
  });
4681
4687
  }
@@ -4864,6 +4870,8 @@ const createCodexHandler = (config) => {
4864
4870
  const agentConfigCache = config.agentConfigCache ?? null;
4865
4871
  const gitMirrorManager = config.gitMirrorManager ?? null;
4866
4872
  const contextTreePath = config.contextTreePath ?? null;
4873
+ const contextTreeRepoUrl = config.contextTreeRepoUrl ?? null;
4874
+ const agentName = config.agentName ?? null;
4867
4875
  let cwd = null;
4868
4876
  let codex = null;
4869
4877
  let thread = null;
@@ -5135,6 +5143,17 @@ const createCodexHandler = (config) => {
5135
5143
  if (inputs.length === 0) return;
5136
5144
  await runTurn(inputs.join("\n\n"), sessionCtx);
5137
5145
  }
5146
+ /** Install the first-tree skill + binding block; no-op when context tree is unconfigured. */
5147
+ function ensureFirstTreeBinding(workspace, sessionCtx) {
5148
+ if (!contextTreePath) return;
5149
+ installFirstTreeIntegration({
5150
+ workspacePath: workspace,
5151
+ contextTreePath,
5152
+ workspaceId: agentName ?? sessionCtx.chatId,
5153
+ treeRepoUrl: contextTreeRepoUrl ?? void 0,
5154
+ log: (msg) => sessionCtx.log(msg)
5155
+ });
5156
+ }
5138
5157
  return {
5139
5158
  async start(message, sessionCtx) {
5140
5159
  ctx = sessionCtx;
@@ -5160,6 +5179,7 @@ const createCodexHandler = (config) => {
5160
5179
  content: buildAgentBriefing(payload)
5161
5180
  }
5162
5181
  });
5182
+ ensureFirstTreeBinding(cwd, sessionCtx);
5163
5183
  await prepareGitWorktrees(payload, cwd, sessionCtx.chatId);
5164
5184
  codex = new Codex({
5165
5185
  env: buildEnv(sessionCtx),
@@ -5184,17 +5204,20 @@ const createCodexHandler = (config) => {
5184
5204
  env: [],
5185
5205
  gitRepos: []
5186
5206
  };
5187
- bootstrapWorkspace({
5188
- workspacePath: cwd,
5189
- identity: sessionCtx.agent,
5190
- contextTreePath,
5191
- serverUrl: sessionCtx.sdk.serverUrl,
5192
- chatId: sessionCtx.chatId,
5193
- briefing: {
5194
- format: "agents-md",
5195
- content: buildAgentBriefing(payload)
5196
- }
5197
- });
5207
+ if (!existsSync(join(cwd, ".agent", "identity.json"))) {
5208
+ bootstrapWorkspace({
5209
+ workspacePath: cwd,
5210
+ identity: sessionCtx.agent,
5211
+ contextTreePath,
5212
+ serverUrl: sessionCtx.sdk.serverUrl,
5213
+ chatId: sessionCtx.chatId,
5214
+ briefing: {
5215
+ format: "agents-md",
5216
+ content: buildAgentBriefing(payload)
5217
+ }
5218
+ });
5219
+ ensureFirstTreeBinding(cwd, sessionCtx);
5220
+ }
5198
5221
  await prepareGitWorktrees(payload, cwd, sessionCtx.chatId);
5199
5222
  codex = new Codex({
5200
5223
  env: buildEnv(sessionCtx),
@@ -6227,7 +6250,7 @@ var AgentSlot = class {
6227
6250
  lastActivityMs: 0
6228
6251
  };
6229
6252
  }
6230
- async start(contextTreePath) {
6253
+ async start(contextTreeBinding) {
6231
6254
  const sdk = (await this.clientConnection.bindAgent(this.config.agentId, this.config.runtimeType ?? this.config.type, this.config.runtimeVersion)).sdk;
6232
6255
  this.sdk = sdk;
6233
6256
  const agent = await sdk.register();
@@ -6305,7 +6328,9 @@ var AgentSlot = class {
6305
6328
  handlerFactory: this.config.handlerFactory,
6306
6329
  handlerConfig: {
6307
6330
  workspaceRoot: join(DEFAULT_DATA_DIR, "workspaces", this.config.name),
6308
- contextTreePath: contextTreePath ?? void 0,
6331
+ agentName: this.config.name,
6332
+ contextTreePath: contextTreeBinding?.path,
6333
+ contextTreeRepoUrl: contextTreeBinding?.repoUrl,
6309
6334
  gitMirrorManager
6310
6335
  },
6311
6336
  agentIdentity: {
@@ -10242,7 +10267,7 @@ function createFeedbackHandler(config) {
10242
10267
  return { handle };
10243
10268
  }
10244
10269
  //#endregion
10245
- //#region ../server/dist/app-CkYiQS_D.mjs
10270
+ //#region ../server/dist/app-BXdU2BzM.mjs
10246
10271
  var import_fastify_opentelemetry = /* @__PURE__ */ __toESM(require_fastify_opentelemetry(), 1);
10247
10272
  init_esm();
10248
10273
  var __defProp = Object.defineProperty;
@@ -11007,6 +11032,28 @@ async function resolveAgentClient(db, data) {
11007
11032
  return client.id;
11008
11033
  }
11009
11034
  /**
11035
+ * Validate a `delegateMention` write at the service layer. Two checks:
11036
+ * 1. Target uuid must resolve to an existing agent — dangling references
11037
+ * would silently break webhook delegation at runtime.
11038
+ * 2. Target must belong to the same organization as the source agent —
11039
+ * cross-org delegate links are rejected here at the source so the
11040
+ * database never accumulates dirty rows. The webhook router has a
11041
+ * defense-in-depth check that filters them at fan-out time, but this
11042
+ * keeps the data clean and gives the admin UI an immediate 422 instead
11043
+ * of a silent runtime drop.
11044
+ *
11045
+ * `null` clears the field — handled by the caller; we are only invoked when
11046
+ * the caller wrote a non-null uuid.
11047
+ */
11048
+ async function validateDelegateMentionTarget(db, targetUuid, sourceOrgId) {
11049
+ const [target] = await db.select({
11050
+ uuid: agents.uuid,
11051
+ organizationId: agents.organizationId
11052
+ }).from(agents).where(eq(agents.uuid, targetUuid)).limit(1);
11053
+ if (!target) throw new BadRequestError(`delegateMention target "${targetUuid}" not found`);
11054
+ if (target.organizationId !== sourceOrgId) throw new BadRequestError("delegateMention target must belong to the same organization as the agent");
11055
+ }
11056
+ /**
11010
11057
  * Pick the first admin member in the org for internal system agents. Throws
11011
11058
  * if the org has no admin — the caller should surface the error so an admin
11012
11059
  * is created before the system tries to register more agents.
@@ -11046,6 +11093,7 @@ async function createAgent(db, data, options = {}) {
11046
11093
  type: data.type
11047
11094
  });
11048
11095
  await ensureClientSupportsRuntimeProvider(db, clientId, runtimeProvider, { force: options.force });
11096
+ if (data.delegateMention) await validateDelegateMentionTarget(db, data.delegateMention, orgId);
11049
11097
  const [org] = await db.select({ maxAgents: organizations.maxAgents }).from(organizations).where(eq(organizations.id, orgId)).limit(1);
11050
11098
  if (org && org.maxAgents > 0) {
11051
11099
  if (((await db.select({ value: count() }).from(agents).where(and(eq(agents.organizationId, orgId), ne(agents.status, AGENT_STATUSES.DELETED))))[0]?.value ?? 0) >= org.maxAgents) throw new ForbiddenError(`Organization "${orgId}" has reached its agent limit (${org.maxAgents}). Upgrade your plan or delete unused agents.`);
@@ -11192,7 +11240,10 @@ async function updateAgent(db, uuid, data) {
11192
11240
  const updates = { updatedAt: /* @__PURE__ */ new Date() };
11193
11241
  if (data.type !== void 0) updates.type = data.type;
11194
11242
  if (data.displayName !== void 0) updates.displayName = data.displayName;
11195
- if (data.delegateMention !== void 0) updates.delegateMention = data.delegateMention;
11243
+ if (data.delegateMention !== void 0) {
11244
+ if (data.delegateMention !== null) await validateDelegateMentionTarget(db, data.delegateMention, agent.organizationId);
11245
+ updates.delegateMention = data.delegateMention;
11246
+ }
11196
11247
  if (data.visibility !== void 0) updates.visibility = data.visibility;
11197
11248
  if (data.metadata !== void 0) updates.metadata = data.metadata;
11198
11249
  if (data.managerId !== void 0) {
@@ -13779,7 +13830,7 @@ async function agentRoutes(app) {
13779
13830
  });
13780
13831
  app.post("/:uuid/test", async (request, reply) => {
13781
13832
  const { uuid } = request.params;
13782
- await requireAgentAccess(request, app.db, "manage");
13833
+ const { agent: targetAgent } = await requireAgentAccess(request, app.db, "manage");
13783
13834
  const presence = await getPresence(app.db, uuid);
13784
13835
  const wsConnected = hasActiveConnection(uuid);
13785
13836
  const clientId = getAgentClientId(uuid) ?? presence?.clientId ?? null;
@@ -13816,15 +13867,15 @@ async function agentRoutes(app) {
13816
13867
  message: "Agent connection is stale — heartbeat lost. The client process may have crashed.",
13817
13868
  connection
13818
13869
  });
13819
- const [owner] = await app.db.select({ uuid: agents.uuid }).from(agents).where(and(eq(agents.delegateMention, uuid), eq(agents.status, "active"))).limit(1);
13870
+ const [owner] = await app.db.select({ uuid: agents.uuid }).from(agents).where(and(eq(agents.delegateMention, uuid), eq(agents.status, "active"), eq(agents.organizationId, targetAgent.organizationId))).limit(1);
13820
13871
  let senderId = owner?.uuid ?? null;
13821
13872
  if (!senderId) {
13822
- const [other] = await app.db.select({ uuid: agents.uuid }).from(agents).where(and(ne(agents.uuid, uuid), eq(agents.status, "active"))).limit(1);
13873
+ const [other] = await app.db.select({ uuid: agents.uuid }).from(agents).where(and(ne(agents.uuid, uuid), eq(agents.status, "active"), eq(agents.organizationId, targetAgent.organizationId))).limit(1);
13823
13874
  senderId = other?.uuid ?? null;
13824
13875
  }
13825
13876
  if (!senderId) return reply.status(200).send({
13826
13877
  status: "error",
13827
- message: "No suitable sender found. Need at least one other active agent.",
13878
+ message: "No suitable sender found. Need at least one other active agent in the same organization.",
13828
13879
  connection
13829
13880
  });
13830
13881
  const chat = await findOrCreateDirectChat(app.db, senderId, uuid);
@@ -15017,7 +15068,7 @@ function decodeCursor(cursor) {
15017
15068
  * - Cursor narrows the result to rows STRICTLY before `(cursor.ts, cursor.id)`.
15018
15069
  * - Followed by a small participant-list lookup for the page only.
15019
15070
  */
15020
- async function listMeChats(db, humanAgentId, query) {
15071
+ async function listMeChats(db, humanAgentId, organizationId, query) {
15021
15072
  const limit = query.limit;
15022
15073
  const cursor = query.cursor ? decodeCursor(query.cursor) : null;
15023
15074
  if (query.cursor && !cursor) throw new BadRequestError("Invalid cursor");
@@ -15058,6 +15109,11 @@ async function listMeChats(db, humanAgentId, query) {
15058
15109
  FROM chats c
15059
15110
  JOIN deduped d ON d.chat_id = c.id
15060
15111
  WHERE c.parent_chat_id IS NULL
15112
+ /* Scope to the caller's org. Without this, cross-org dirty chats
15113
+ whose chat_participants still reference the caller's human agent
15114
+ (historical pollution — see fix/cross-org-direct-chat-pollution)
15115
+ would leak into the list and 404 on click via requireChatAccess. */
15116
+ AND c.organization_id = ${organizationId}
15061
15117
  /* Filter: unread / watching */
15062
15118
  AND (${!filterUnreadOnly}::bool OR d.unread_mention_count > 0)
15063
15119
  AND (${!filterWatchingOnly}::bool OR d.membership_kind = 'watching')
@@ -16077,17 +16133,17 @@ function isGithubHttpsRepo(repoUrl) {
16077
16133
  }
16078
16134
  function contextStatus(warning) {
16079
16135
  if (warning?.stale) return {
16080
- label: "Team context is stale",
16136
+ label: "Context Tree may be stale",
16081
16137
  detail: warning.detail,
16082
16138
  severity: "warning"
16083
16139
  };
16084
16140
  if (warning) return {
16085
- label: "Team context needs attention",
16141
+ label: "Context Tree needs attention",
16086
16142
  detail: warning.detail,
16087
16143
  severity: "warning"
16088
16144
  };
16089
16145
  return {
16090
- label: "Team context is current",
16146
+ label: "Context Tree is up to date",
16091
16147
  detail: "Agents have a synced team context snapshot available.",
16092
16148
  severity: "ok"
16093
16149
  };
@@ -17018,7 +17074,7 @@ async function meRoutes(app) {
17018
17074
  */
17019
17075
  app.get("/me/pinned-agents", async (request) => {
17020
17076
  const { userId } = requireUser(request);
17021
- const { listMyPinnedAgents } = await import("./client-WubcgX-W-B2bOvgJ1.mjs");
17077
+ const { listMyPinnedAgents } = await import("./client-0RrgrMjR-DPyuu6Ls.mjs");
17022
17078
  return listMyPinnedAgents(app.db, { userId });
17023
17079
  });
17024
17080
  /**
@@ -17451,7 +17507,7 @@ async function orgChatRoutes(app) {
17451
17507
  }
17452
17508
  if (view === "grouped") return listChatsForMember(app.db, scope.memberId, scope.humanAgentId);
17453
17509
  const query = listMeChatsQuerySchema.parse(request.query);
17454
- return listMeChats(app.db, scope.humanAgentId, query);
17510
+ return listMeChats(app.db, scope.humanAgentId, scope.organizationId, query);
17455
17511
  });
17456
17512
  /**
17457
17513
  * POST /orgs/:orgId/chats — create a new chat. The :orgId path param
@@ -18187,6 +18243,31 @@ function extractMentions$1(text) {
18187
18243
  }
18188
18244
  return [...names];
18189
18245
  }
18246
+ /** Extract mentions from structural payload fields (not free-form text).
18247
+ * GitHub's `pull_request.review_requested` puts the targeted reviewer in
18248
+ * `requested_reviewer.login`, not in any text body — `extractMentions` would
18249
+ * miss it. Team requests use `requested_team` instead, which we deliberately
18250
+ * skip to stay consistent with `extractMentions` ignoring `@org/team`. */
18251
+ function extractStructuralMentions(eventType, payload) {
18252
+ if (!isRecord(payload)) return [];
18253
+ if (eventType !== "pull_request") return [];
18254
+ if (payload.action !== "review_requested") return [];
18255
+ const reviewer = isRecord(payload.requested_reviewer) ? payload.requested_reviewer : null;
18256
+ const login = typeof reviewer?.login === "string" ? reviewer.login : null;
18257
+ return login ? [login.toLowerCase()] : [];
18258
+ }
18259
+ const DELEGATE_VERDICT_MESSAGES = {
18260
+ ok: "delegate_mention target eligible",
18261
+ not_found: "delegate_mention target not found, skipping",
18262
+ cross_org: "delegate_mention target belongs to another org, skipping",
18263
+ inactive: "delegate_mention target not active, skipping"
18264
+ };
18265
+ function evaluateDelegateTarget(target, sourceOrgId) {
18266
+ if (!target) return "not_found";
18267
+ if (target.organizationId !== sourceOrgId) return "cross_org";
18268
+ if (target.status !== "active") return "inactive";
18269
+ return "ok";
18270
+ }
18190
18271
  /**
18191
18272
  * Route @mentions to delegate agents.
18192
18273
  * For each mentioned user who has delegate_mention configured,
@@ -18205,13 +18286,19 @@ async function routeMentionDelegations(app, organizationId, mentionedNames, ctx)
18205
18286
  if (agent.status !== "active" || !agent.delegateMention) continue;
18206
18287
  const [target] = await app.db.select({
18207
18288
  id: agents.uuid,
18208
- status: agents.status
18289
+ status: agents.status,
18290
+ organizationId: agents.organizationId
18209
18291
  }).from(agents).where(eq(agents.uuid, agent.delegateMention)).limit(1);
18210
- if (!target || target.status !== "active") {
18292
+ const verdict = evaluateDelegateTarget(target, organizationId);
18293
+ if (verdict !== "ok") {
18211
18294
  log$1.warn({
18212
18295
  targetAgent: agent.delegateMention,
18213
- sourceAgent: agent.name
18214
- }, "delegate_mention target not active, skipping");
18296
+ sourceAgent: agent.name,
18297
+ sourceOrg: organizationId,
18298
+ targetOrg: target?.organizationId,
18299
+ targetStatus: target?.status,
18300
+ verdict
18301
+ }, DELEGATE_VERDICT_MESSAGES[verdict]);
18215
18302
  continue;
18216
18303
  }
18217
18304
  try {
@@ -18222,6 +18309,7 @@ async function routeMentionDelegations(app, organizationId, mentionedNames, ctx)
18222
18309
  type: "github_mention",
18223
18310
  mentionedUser: agent.name,
18224
18311
  event: ctx.event,
18312
+ action: ctx.action,
18225
18313
  repository: ctx.repository,
18226
18314
  sender: ctx.sender,
18227
18315
  title: ctx.title,
@@ -18231,7 +18319,8 @@ async function routeMentionDelegations(app, organizationId, mentionedNames, ctx)
18231
18319
  metadata: {
18232
18320
  source: "github",
18233
18321
  event: "mention_delegation",
18234
- mentionedUser: agent.name
18322
+ mentionedUser: agent.name,
18323
+ action: ctx.action
18235
18324
  }
18236
18325
  });
18237
18326
  notifyRecipients(app.notifier, recipients, msg.id);
@@ -18388,12 +18477,14 @@ function extractEventContext(eventType, payload) {
18388
18477
  const sender = isRecord(payload.sender) ? payload.sender : null;
18389
18478
  const repository = typeof repo?.full_name === "string" ? repo.full_name : "";
18390
18479
  const senderLogin = typeof sender?.login === "string" ? sender.login : "";
18480
+ const action = typeof payload.action === "string" ? payload.action : void 0;
18391
18481
  switch (eventType) {
18392
18482
  case "issues": {
18393
18483
  const issue = isRecord(payload.issue) ? payload.issue : null;
18394
18484
  if (!issue) return null;
18395
18485
  return {
18396
18486
  event: "issues",
18487
+ action,
18397
18488
  repository,
18398
18489
  sender: senderLogin,
18399
18490
  title: `Issue #${issue.number}: ${issue.title}`,
@@ -18407,6 +18498,7 @@ function extractEventContext(eventType, payload) {
18407
18498
  if (!issue || !comment) return null;
18408
18499
  return {
18409
18500
  event: "issue_comment",
18501
+ action,
18410
18502
  repository,
18411
18503
  sender: senderLogin,
18412
18504
  title: `Issue #${issue.number}: ${issue.title}`,
@@ -18419,6 +18511,7 @@ function extractEventContext(eventType, payload) {
18419
18511
  if (!pr) return null;
18420
18512
  return {
18421
18513
  event: "pull_request",
18514
+ action,
18422
18515
  repository,
18423
18516
  sender: senderLogin,
18424
18517
  title: `PR #${pr.number}: ${pr.title}`,
@@ -18432,6 +18525,7 @@ function extractEventContext(eventType, payload) {
18432
18525
  if (!pr || !review) return null;
18433
18526
  return {
18434
18527
  event: "pull_request_review",
18528
+ action,
18435
18529
  repository,
18436
18530
  sender: senderLogin,
18437
18531
  title: `PR #${pr.number}: ${pr.title}`,
@@ -18445,6 +18539,7 @@ function extractEventContext(eventType, payload) {
18445
18539
  if (!pr || !comment) return null;
18446
18540
  return {
18447
18541
  event: "pull_request_review_comment",
18542
+ action,
18448
18543
  repository,
18449
18544
  sender: senderLogin,
18450
18545
  title: `PR #${pr.number}: ${pr.title}`,
@@ -18457,6 +18552,7 @@ function extractEventContext(eventType, payload) {
18457
18552
  if (!disc) return null;
18458
18553
  return {
18459
18554
  event: "discussion",
18555
+ action,
18460
18556
  repository,
18461
18557
  sender: senderLogin,
18462
18558
  title: typeof disc.title === "string" ? disc.title : "",
@@ -18470,6 +18566,7 @@ function extractEventContext(eventType, payload) {
18470
18566
  if (!disc || !comment) return null;
18471
18567
  return {
18472
18568
  event: "discussion_comment",
18569
+ action,
18473
18570
  repository,
18474
18571
  sender: senderLogin,
18475
18572
  title: typeof disc.title === "string" ? disc.title : "",
@@ -18482,6 +18579,7 @@ function extractEventContext(eventType, payload) {
18482
18579
  if (!comment) return null;
18483
18580
  return {
18484
18581
  event: "commit_comment",
18582
+ action,
18485
18583
  repository,
18486
18584
  sender: senderLogin,
18487
18585
  title: "Commit comment",
@@ -18497,16 +18595,26 @@ function extractEventContext(eventType, payload) {
18497
18595
  * Only called after action gating confirms this is a "new content" event.
18498
18596
  */
18499
18597
  async function handleMentionDelegation(app, organizationId, eventType, payload) {
18500
- const mentions = extractMentions$1(extractEventText(eventType, payload));
18598
+ const textMentions = extractMentions$1(extractEventText(eventType, payload));
18599
+ const structuralMentions = extractStructuralMentions(eventType, payload);
18600
+ const mentions = [...new Set([...textMentions, ...structuralMentions])];
18501
18601
  const mentionCtx = extractEventContext(eventType, payload);
18502
18602
  if (mentions.length > 0 && mentionCtx) return routeMentionDelegations(app, organizationId, mentions, mentionCtx);
18503
18603
  return 0;
18504
18604
  }
18505
- /** Actions that represent new/changed content (worth scanning for @mentions). */
18605
+ /** Actions that represent new/changed content (worth scanning for @mentions).
18606
+ * Note: `pull_request.review_requested` doesn't carry an @mention in any
18607
+ * text body — the reviewer is in `requested_reviewer.login`. We pick it up
18608
+ * via `extractStructuralMentions`. The complementary `review_request_removed`
18609
+ * is intentionally omitted to avoid notifying the reviewer twice. */
18506
18610
  const MENTION_ACTIONS = {
18507
18611
  issues: ["opened", "edited"],
18508
18612
  issue_comment: ["created"],
18509
- pull_request: ["opened", "edited"],
18613
+ pull_request: [
18614
+ "opened",
18615
+ "edited",
18616
+ "review_requested"
18617
+ ],
18510
18618
  pull_request_review: ["submitted"],
18511
18619
  pull_request_review_comment: ["created"],
18512
18620
  discussion: ["created", "edited"],