@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.
- package/dist/cli/index.mjs +2 -2
- package/dist/{client-WubcgX-W-B2bOvgJ1.mjs → client-0RrgrMjR-DPyuu6Ls.mjs} +1 -1
- package/dist/{client-DHCSQ8kg-DjlSmE9q.mjs → client-D1TDiik_-gxtXN9bj.mjs} +12 -10
- package/dist/index.mjs +2 -2
- package/dist/{saas-connect-_2M4kfPR.mjs → saas-connect-_lNV0Liy.mjs} +146 -38
- package/dist/web/assets/index-B4EaL8S9.css +1 -0
- package/dist/web/assets/index-CJr7zpx-.js +401 -0
- package/dist/web/assets/{index-D5RJDuFw.js → index-DcMORzyx.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-C1DBMrHD.js +0 -391
- package/dist/web/assets/index-CwC0zzF5.css +0 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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:
|
|
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-
|
|
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-
|
|
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-
|
|
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 (
|
|
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
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
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(
|
|
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
|
-
|
|
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-
|
|
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)
|
|
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: "
|
|
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: "
|
|
16141
|
+
label: "Context Tree needs attention",
|
|
16086
16142
|
detail: warning.detail,
|
|
16087
16143
|
severity: "warning"
|
|
16088
16144
|
};
|
|
16089
16145
|
return {
|
|
16090
|
-
label: "
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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: [
|
|
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"],
|