@agent-team-foundation/first-tree-hub 0.12.10 → 0.13.0
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/{bootstrap-CW73oEYn.mjs → bootstrap-Cya2OoHz.mjs} +6 -6
- package/dist/cli/index.mjs +268 -480
- package/dist/{client-BViGcaUC-CZb2Svgh.mjs → client-BH4CmUL0-CybE3kuP.mjs} +41 -5
- package/dist/{client-DNiLcPEq-db3YS57z.mjs → client-h4KZ3b9o-CQyibXig.mjs} +3 -3
- package/dist/{dist-B1GHzMLc.mjs → dist-C8yStx2L.mjs} +55 -3
- package/dist/drizzle/0043_onboarding_completed_at.sql +32 -0
- package/dist/drizzle/meta/_journal.json +7 -0
- package/dist/{errors-CF5evtJt-B0NTIVPt.mjs → errors-LPcARA4K-Dbrptiyz.mjs} +2 -1
- package/dist/{feishu-30vUx69l.mjs → feishu-D_vnqC6a.mjs} +1 -1
- package/dist/index.mjs +7 -7
- package/dist/invitation-CNv7gfFF-D93KQte0.mjs +4 -0
- package/dist/{invitation-Bg0TRiyx-BsZH4GCS.mjs → invitation-DZO4NX3P-BPxTeHf-.mjs} +2 -2
- package/dist/{saas-connect-Fgnnnola.mjs → saas-connect-Bb5LR4y6.mjs} +224 -48
- package/dist/web/assets/{index-B7FIVwrn.js → index-CJcRUZ8l.js} +1 -1
- package/dist/web/assets/index-DL_9NFkt.js +421 -0
- package/dist/web/assets/index-DaWEZnjh.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/invitation-C299fxkP-CZgGbsN_.mjs +0 -4
- package/dist/web/assets/index-DJbUySaH.css +0 -1
- package/dist/web/assets/index-DiDfVdIH.js +0 -421
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { a as __toCommonJS, o as __toESM, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
|
|
2
2
|
import { A as FIRST_TREE_HUB_ATTR, C as stampOrgScope, D as untrustedAttrs, E as startWsConnectionSpan, M as require_pino, O as withSpan, S as stampChatResource, _ as rootLogger$1, a as buildRateLimitError, c as currentTraceId, g as reportErrorToRoot, i as bodyCaptureOnSendHook, j as redactUrl, k as withWsMessageSpan, l as decodeJwtForTrace, m as observabilityPlugin, n as applyLoggerConfig, o as classifyJoseError, r as attachRequestContext, s as createLogger$1, t as adapterAttrs, u as endWsConnectionSpan, x as stampAgentResource, y as setWsConnectionAttrs } from "./observability-BAScT_5S-BcW9HgkG.mjs";
|
|
3
|
-
import { C as resetConfigMeta, E as setConfigValue, S as resetConfig, T as serverConfigSchema, b as migrateLegacyHome, c as resolveServerUrl, d as DEFAULT_CONFIG_DIR, f as DEFAULT_DATA_DIR$1, g as collectMissingPrompts, h as clientConfigSchema, i as ensureFreshAccessToken, l as saveAgentConfig, m as agentConfigSchema, o as loadCredentials, p as DEFAULT_HOME_DIR$1, u as saveCredentials, v as initConfig, w as resolveConfigReadonly, y as loadAgents } from "./bootstrap-
|
|
3
|
+
import { C as resetConfigMeta, E as setConfigValue, S as resetConfig, T as serverConfigSchema, b as migrateLegacyHome, c as resolveServerUrl, d as DEFAULT_CONFIG_DIR, f as DEFAULT_DATA_DIR$1, g as collectMissingPrompts, h as clientConfigSchema, i as ensureFreshAccessToken, l as saveAgentConfig, m as agentConfigSchema, o as loadCredentials, p as DEFAULT_HOME_DIR$1, u as saveCredentials, v as initConfig, w as resolveConfigReadonly, y as loadAgents } from "./bootstrap-Cya2OoHz.mjs";
|
|
4
4
|
import { a as print, i as blank, n as CLI_USER_AGENT, r as COMMAND_VERSION, s as status, t as cliFetch } from "./cli-fetch--tiwKm5S.mjs";
|
|
5
|
-
import { $ as
|
|
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-
|
|
5
|
+
import { $ as notificationQuerySchema, A as createMemberSchema, B as githubDevCallbackQuerySchema, C as connectTokenExchangeSchema, Ct as updateChatSchema, D as createAgentSchema, Dt as wsAuthFrameSchema, E as createAdapterMappingSchema, Et as updateOrganizationSchema, F as dryRunAgentRuntimeConfigSchema, G as inboxPollQuerySchema, H as imageInlineContentSchema, J as isReservedAgentName$1, K as isOrgSettingNamespace, L as githubAppInstallationClaimBodySchema, N as defaultRuntimeConfigPayload, O as createChatSchema, P as delegateFeishuUserSchema, Q as messageSourceSchema$1, R as githubAppInstallationPermissionsSchema$1, S as clientRegisterSchema, St as updateAgentSchema, T as createAdapterConfigSchema, Tt as updateMemberSchema, U as inboxAckFrameSchema, V as githubStartQuerySchema, W as inboxDeliverFrameSchema$1, X as listMeChatsQuerySchema, Y as joinByInvitationSchema, Z as loginSchema, _ as agentPinnedMessageSchema$1, _t as sessionStateMessageSchema, a as AGENT_TYPES, b as chatMetadataSchema$1, bt as updateAdapterConfigSchema, ct as runtimeStateMessageSchema, d as NOTIFICATION_TYPES, dt as selfServiceFeishuBotSchema, et as onboardingEventSchema, f as ORG_SETTINGS_NAMESPACES$1, ft as sendMessageSchema, g as agentBindRequestSchema, gt as sessionReconcileRequestSchema, h as addParticipantSchema, ht as sessionEventSchema$1, i as AGENT_STATUSES, j as createOrgFromMeSchema, k as createMeChatSchema, lt as safeRedirectPath, m as addMeChatParticipantsSchema, mt as sessionEventMessageSchema, n as AGENT_NAME_REGEX$1, nt as patchChatEngagementSchema, o as AGENT_VISIBILITY, ot as rebindAgentSchema, p as WS_AUTH_FRAME_TIMEOUT_MS, pt as sendToAgentSchema, q as isRedactedEnvValue, r as AGENT_SELECTOR_HEADER$1, rt as patchOnboardingSchema, s as CHAT_ENGAGEMENT_STATUSES, st as refreshTokenSchema, t as AGENT_BIND_REJECT_REASONS, tt as paginationQuerySchema, u as MENTION_REGEX, v as agentRuntimeConfigPayloadSchema$1, vt as stripCode, w as contextTreeSnapshotSchema, wt as updateClientCapabilitiesSchema, xt as updateAgentRuntimeConfigSchema, y as agentTypeSchema$1, yt as submitQuestionAnswerSchema, z as githubCallbackQuerySchema } from "./dist-C8yStx2L.mjs";
|
|
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-LPcARA4K-Dbrptiyz.mjs";
|
|
7
7
|
import { n as init_esm, r as trace, t as esm_exports } from "./esm-iadMkGbV.mjs";
|
|
8
|
-
import { $ as notifyRecipients, A as getPresence, B as listAgentsManagedByUser, C as ensureParticipant, D as getChatDetail, E as getCachedAudience, F as joinAsParticipant, G as listClients, H as listChatParticipantsWithNames, I as joinChat, K as listClientsForOrgAdmin, L as leaveAsParticipant, M as heartbeatInstance, N as inboxEntries, O as getClient, P as invalidateChatAudience, Q as messages, R as leaveChat, S as ensureCanJoin, T as getActivityOverview, U as listChats, V as listAgentsWithRuntime, W as listChatsForMember, X as markSupersededByChat, Y as markStaleAgents, Z as members, _ as createChat, _t as unbindAgent, a as agentVisibilityCondition, at as registerClient, b as disconnectClient, c as assertParticipant, ct as resolveChatMembership, d as chatMembership, dt as sendToAgent$1, et as pendingQuestions, f as chats, ft as serverInstances, g as clients, gt as touchAgent, h as cleanupStalePresence, ht as submitAnswer, i as agentPresence, it as registerChatMessageDispatcher, j as heartbeatClient, k as getOnlineCount, l as bindAgent, lt as retireClient, m as cleanupStaleClients, mt as setRuntimeState, n as addParticipant, nt as recomputeWatchersForAgent, o as agents, ot as removeParticipant, p as claimClient, pt as setOffline, q as listMessages, r as agentChatSessions, rt as recomputeWatchersForMember, s as assertClientOwner, st as resetActivity, t as addChatParticipants, tt as recomputeChatWatchers, u as changeChatType, ut as sendMessage, v as createNotifier, vt as updateClientCapabilities, w as findOrCreateDirectChat, x as editMessage, y as deriveAuthState, yt as upsertSessionState, z as listActiveAgentsPinnedToClient } from "./client-
|
|
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-
|
|
8
|
+
import { $ as notifyRecipients, A as getPresence, B as listAgentsManagedByUser, C as ensureParticipant, D as getChatDetail, E as getCachedAudience, F as joinAsParticipant, G as listClients, H as listChatParticipantsWithNames, I as joinChat, K as listClientsForOrgAdmin, L as leaveAsParticipant, M as heartbeatInstance, N as inboxEntries, O as getClient, P as invalidateChatAudience, Q as messages, R as leaveChat, S as ensureCanJoin, T as getActivityOverview, U as listChats, V as listAgentsWithRuntime, W as listChatsForMember, X as markSupersededByChat, Y as markStaleAgents, Z as members, _ as createChat, _t as unbindAgent, a as agentVisibilityCondition, at as registerClient, b as disconnectClient, c as assertParticipant, ct as resolveChatMembership, d as chatMembership, dt as sendToAgent$1, et as pendingQuestions, f as chats, ft as serverInstances, g as clients, gt as touchAgent, h as cleanupStalePresence, ht as submitAnswer, i as agentPresence, it as registerChatMessageDispatcher, j as heartbeatClient, k as getOnlineCount, l as bindAgent, lt as retireClient, m as cleanupStaleClients, mt as setRuntimeState, n as addParticipant, nt as recomputeWatchersForAgent, o as agents, ot as removeParticipant, p as claimClient, pt as setOffline, q as listMessages, r as agentChatSessions, rt as recomputeWatchersForMember, s as assertClientOwner, st as resetActivity, t as addChatParticipants, tt as recomputeChatWatchers, u as changeChatType, ut as sendMessage, v as createNotifier, vt as updateClientCapabilities, w as findOrCreateDirectChat, x as editMessage, y as deriveAuthState, yt as upsertSessionState, z as listActiveAgentsPinnedToClient } from "./client-BH4CmUL0-CybE3kuP.mjs";
|
|
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-DZO4NX3P-BPxTeHf-.mjs";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { ZodError, z } from "zod";
|
|
12
12
|
import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
@@ -386,6 +386,7 @@ z.object({
|
|
|
386
386
|
const PROMPT_APPEND_MAX_LENGTH = 32e3;
|
|
387
387
|
const MCP_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
388
388
|
const ENV_KEY_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
389
|
+
const WINDOWS_DRIVE_PATH_PATTERN = /^[A-Za-z]:/;
|
|
389
390
|
const promptConfigSchema = z.object({ append: z.string().max(PROMPT_APPEND_MAX_LENGTH).default("") });
|
|
390
391
|
const mcpStdioServerSchema = z.object({
|
|
391
392
|
name: z.string().regex(MCP_NAME_PATTERN, "MCP name must match /^[a-z0-9][a-z0-9_-]{0,63}$/i"),
|
|
@@ -415,10 +416,38 @@ const envEntrySchema = z.object({
|
|
|
415
416
|
value: z.string(),
|
|
416
417
|
sensitive: z.boolean().default(false)
|
|
417
418
|
});
|
|
419
|
+
function hasControlCharacters(value) {
|
|
420
|
+
for (let idx = 0; idx < value.length; idx++) {
|
|
421
|
+
const code = value.charCodeAt(idx);
|
|
422
|
+
if (code <= 31 || code === 127) return true;
|
|
423
|
+
}
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
function getRepoLocalPathSafetyError(localPath) {
|
|
427
|
+
if (localPath.length === 0) return "Git repo local path must not be empty";
|
|
428
|
+
if (localPath.trim() !== localPath) return "Git repo local path must not have leading or trailing whitespace";
|
|
429
|
+
if (hasControlCharacters(localPath)) return "Git repo local path must not contain control characters";
|
|
430
|
+
if (localPath.includes("\\")) return "Git repo local path must use forward slashes";
|
|
431
|
+
if (localPath.startsWith("/") || WINDOWS_DRIVE_PATH_PATTERN.test(localPath)) return "Git repo local path must be relative";
|
|
432
|
+
const segments = localPath.split("/");
|
|
433
|
+
for (const segment of segments) {
|
|
434
|
+
if (!segment) return "Git repo local path must not contain empty path segments";
|
|
435
|
+
if (segment === "." || segment === "..") return "Git repo local path must not contain dot segments";
|
|
436
|
+
if (segment.trim() !== segment) return "Git repo local path segments must not have leading or trailing whitespace";
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
418
440
|
const gitRepoSchema = z.object({
|
|
419
441
|
url: z.string().min(1),
|
|
420
442
|
ref: z.string().min(1).optional(),
|
|
421
|
-
localPath: z.string().min(1).
|
|
443
|
+
localPath: z.string().min(1).superRefine((localPath, ctx) => {
|
|
444
|
+
const safetyError = getRepoLocalPathSafetyError(localPath);
|
|
445
|
+
if (!safetyError) return;
|
|
446
|
+
ctx.addIssue({
|
|
447
|
+
code: z.ZodIssueCode.custom,
|
|
448
|
+
message: safetyError
|
|
449
|
+
});
|
|
450
|
+
}).optional()
|
|
422
451
|
});
|
|
423
452
|
/**
|
|
424
453
|
* Untagged base shape — 5 user-tunable fields, no `kind` discriminator.
|
|
@@ -1261,6 +1290,26 @@ const meChatParticipantSchema = z.object({
|
|
|
1261
1290
|
displayName: z.string(),
|
|
1262
1291
|
type: z.string()
|
|
1263
1292
|
});
|
|
1293
|
+
/**
|
|
1294
|
+
* Live activity hint surfaced in the conversation row's time slot. Derived
|
|
1295
|
+
* server-side from the latest `session_events` row for the chat. See
|
|
1296
|
+
* `MeChatRow.liveActivity` for the lifecycle rules.
|
|
1297
|
+
*
|
|
1298
|
+
* `kind` is intentionally narrower than the full `sessionEventKind` enum:
|
|
1299
|
+
* `turn_end` / `error` produce `liveActivity: null` rather than a live
|
|
1300
|
+
* indicator.
|
|
1301
|
+
*/
|
|
1302
|
+
const liveActivityKindSchema = z.enum([
|
|
1303
|
+
"tool_call",
|
|
1304
|
+
"thinking",
|
|
1305
|
+
"assistant_text"
|
|
1306
|
+
]);
|
|
1307
|
+
const liveActivitySchema = z.object({
|
|
1308
|
+
agentId: z.string(),
|
|
1309
|
+
kind: liveActivityKindSchema,
|
|
1310
|
+
label: z.string(),
|
|
1311
|
+
startedAt: z.string()
|
|
1312
|
+
});
|
|
1264
1313
|
const meChatRowSchema = z.object({
|
|
1265
1314
|
chatId: z.string(),
|
|
1266
1315
|
type: z.string(),
|
|
@@ -1274,7 +1323,8 @@ const meChatRowSchema = z.object({
|
|
|
1274
1323
|
unreadMentionCount: z.number().int(),
|
|
1275
1324
|
canReply: z.boolean(),
|
|
1276
1325
|
engagementStatus: chatEngagementStatusSchema,
|
|
1277
|
-
|
|
1326
|
+
engagedAgentIds: z.array(z.string()),
|
|
1327
|
+
liveActivity: liveActivitySchema.nullable()
|
|
1278
1328
|
});
|
|
1279
1329
|
z.object({
|
|
1280
1330
|
rows: z.array(meChatRowSchema),
|
|
@@ -3309,7 +3359,7 @@ You are running inside **Agent Hub**, a messaging platform for agent teams.
|
|
|
3309
3359
|
|
|
3310
3360
|
- Messages from other team members arrive as your prompt input
|
|
3311
3361
|
- Each message includes a \`[From: <agent-name>]\` header — that name is also
|
|
3312
|
-
what you pass back to \`
|
|
3362
|
+
what you pass back to \`chat send\` to reply to or address that agent
|
|
3313
3363
|
- **Your final text response is automatically delivered** to the chat — just respond normally
|
|
3314
3364
|
- For **proactive communication** (sending to other agents, other chats, or structured data),
|
|
3315
3365
|
use the \`first-tree-hub\` CLI below
|
|
@@ -3333,7 +3383,7 @@ The \`first-tree-hub\` CLI reads these automatically — no extra setup needed.
|
|
|
3333
3383
|
|
|
3334
3384
|
## Sending Messages
|
|
3335
3385
|
|
|
3336
|
-
Use the \`first-tree-hub
|
|
3386
|
+
Use the \`first-tree-hub chat send\` CLI — it reads the env vars above and
|
|
3337
3387
|
attaches the \`Authorization\` + \`X-Agent-Id\` headers automatically:
|
|
3338
3388
|
|
|
3339
3389
|
\`\`\`bash
|
|
@@ -3345,28 +3395,38 @@ attaches the \`Authorization\` + \`X-Agent-Id\` headers automatically:
|
|
|
3345
3395
|
# the case in a group chat where someone @-mentioned you to talk to them),
|
|
3346
3396
|
# the message stays in that chat. Otherwise it falls back to a direct chat
|
|
3347
3397
|
# between you and the recipient. You don't need to think about which.
|
|
3348
|
-
first-tree-hub
|
|
3398
|
+
first-tree-hub chat send <agentName> "your message"
|
|
3349
3399
|
|
|
3350
3400
|
# Send into a specific chat by id — use this only when you explicitly want
|
|
3351
3401
|
# to address a chat your current session is NOT bound to.
|
|
3352
|
-
first-tree-hub
|
|
3402
|
+
first-tree-hub chat send --chat <chatId> "your message"
|
|
3353
3403
|
|
|
3354
3404
|
# Send markdown (default format is text)
|
|
3355
|
-
first-tree-hub
|
|
3405
|
+
first-tree-hub chat send <agentName> -f markdown "**bold** message"
|
|
3356
3406
|
|
|
3357
3407
|
# Reply to a specific message
|
|
3358
|
-
first-tree-hub
|
|
3408
|
+
first-tree-hub chat send <agentName> --reply-to <messageId> "reply content"
|
|
3359
3409
|
|
|
3360
3410
|
# Pipe long content via stdin (recommended for special characters)
|
|
3361
|
-
echo "long message body" | first-tree-hub
|
|
3411
|
+
echo "long message body" | first-tree-hub chat send <agentName>
|
|
3362
3412
|
\`\`\`
|
|
3363
3413
|
|
|
3364
|
-
> Agent uuids appear in \`
|
|
3365
|
-
> but they are NOT accepted by \`
|
|
3414
|
+
> Agent uuids appear in \`chat list\`, chat history, and participant lists,
|
|
3415
|
+
> but they are NOT accepted by \`chat send\` — always use the name.
|
|
3366
3416
|
|
|
3367
3417
|
For content with quotes, \`$\`, backticks, or newlines, prefer stdin to avoid shell escaping issues.
|
|
3368
3418
|
`;
|
|
3369
3419
|
}
|
|
3420
|
+
function resolveGitRepoTargetPath(workspace, localPath) {
|
|
3421
|
+
const safetyError = getRepoLocalPathSafetyError(localPath);
|
|
3422
|
+
if (safetyError) throw new Error(`Unsafe git repo localPath "${localPath}": ${safetyError}`);
|
|
3423
|
+
const workspaceRoot = resolve(workspace);
|
|
3424
|
+
const targetPath = resolve(workspaceRoot, localPath);
|
|
3425
|
+
const relativeTarget = relative(workspaceRoot, targetPath);
|
|
3426
|
+
const escapesWorkspace = relativeTarget === ".." || relativeTarget.startsWith(`..${sep}`);
|
|
3427
|
+
if (!relativeTarget || escapesWorkspace || isAbsolute(relativeTarget)) throw new Error(`Unsafe git repo localPath "${localPath}": resolved path escapes the session workspace`);
|
|
3428
|
+
return targetPath;
|
|
3429
|
+
}
|
|
3370
3430
|
const DEFAULT_CLONE_TIMEOUT_MS = 300 * 1e3;
|
|
3371
3431
|
const FETCH_REFSPEC = "+refs/heads/*:refs/remotes/origin/*";
|
|
3372
3432
|
const SESSION_BRANCH_PREFIX = "hub-session";
|
|
@@ -4994,7 +5054,7 @@ const createClaudeCodeHandler = (config) => {
|
|
|
4994
5054
|
if (!gitMirrorManager || !payload?.gitRepos?.length) return;
|
|
4995
5055
|
for (const repo of payload.gitRepos) {
|
|
4996
5056
|
const localPath = repo.localPath ?? deriveRepoLocalPath(repo.url);
|
|
4997
|
-
const targetPath =
|
|
5057
|
+
const targetPath = resolveGitRepoTargetPath(workspace, localPath);
|
|
4998
5058
|
sessionCtx.log(`Git: preparing ${repo.url} → ${localPath}${repo.ref ? ` @ ${repo.ref}` : ""}`);
|
|
4999
5059
|
const mirror = await gitMirrorManager.ensureMirror(repo.url);
|
|
5000
5060
|
if (mirror.cloned) sessionCtx.log(`Git: cloned ${repo.url} in ${mirror.elapsedMs}ms`);
|
|
@@ -5205,7 +5265,7 @@ function buildCodexThreadOptions(payload, workspaceCwd) {
|
|
|
5205
5265
|
for (const repo of payload.gitRepos) {
|
|
5206
5266
|
const localPath = repo.localPath ?? deriveRepoLocalPath(repo.url);
|
|
5207
5267
|
if (!localPath) continue;
|
|
5208
|
-
additionalDirectories.push(
|
|
5268
|
+
additionalDirectories.push(resolveGitRepoTargetPath(workspaceCwd, localPath));
|
|
5209
5269
|
}
|
|
5210
5270
|
const opts = {
|
|
5211
5271
|
workingDirectory: workspaceCwd,
|
|
@@ -5302,7 +5362,7 @@ const createCodexHandler = (config) => {
|
|
|
5302
5362
|
for (const repo of payload.gitRepos) {
|
|
5303
5363
|
const localPath = repo.localPath ?? deriveRepoLocalPath(repo.url);
|
|
5304
5364
|
if (!localPath) continue;
|
|
5305
|
-
const targetPath =
|
|
5365
|
+
const targetPath = resolveGitRepoTargetPath(workspaceCwd, localPath);
|
|
5306
5366
|
if (existsSync(targetPath)) continue;
|
|
5307
5367
|
try {
|
|
5308
5368
|
await gitMirrorManager.ensureMirror(repo.url);
|
|
@@ -7276,7 +7336,7 @@ function fail(code, message, exitCode = 1) {
|
|
|
7276
7336
|
//#endregion
|
|
7277
7337
|
//#region src/core/agent-messaging.ts
|
|
7278
7338
|
/**
|
|
7279
|
-
* Resolve `replyTo` envelope fields for `
|
|
7339
|
+
* Resolve `replyTo` envelope fields for `chat send`. When the CLI is invoked
|
|
7280
7340
|
* from inside a claude-code session (the handler exports
|
|
7281
7341
|
* `FIRST_TREE_HUB_CHAT_ID` + `FIRST_TREE_HUB_INBOX_ID`), default the reply
|
|
7282
7342
|
* target to the calling session's own chat so the peer's reply routes back
|
|
@@ -7568,7 +7628,7 @@ function rotateClientIdWithBackup(configDir) {
|
|
|
7568
7628
|
}
|
|
7569
7629
|
/**
|
|
7570
7630
|
* Shared handler for `CLIENT_ORG_MISMATCH` across CLI entry points
|
|
7571
|
-
* (`client start` and `
|
|
7631
|
+
* (`client start` and `connect <token> --no-service`). Prompts interactively,
|
|
7572
7632
|
* rotates the local clientId, and always exits the current process — the
|
|
7573
7633
|
* runtime is already poisoned (wrong clientId in memory), so continuing
|
|
7574
7634
|
* in-band is not safe. Service-supervised (managed) runs skip the prompt and
|
|
@@ -8331,7 +8391,7 @@ function installLaunchd() {
|
|
|
8331
8391
|
lastBootstrapErr = res;
|
|
8332
8392
|
if (attempt < 2) sleepSync(1e3);
|
|
8333
8393
|
}
|
|
8334
|
-
if (lastBootstrapErr) throw new Error(`launchctl bootstrap failed: ${lastBootstrapErr.stderr || `exit ${lastBootstrapErr.code ?? "unknown"}`}\n Command: launchctl bootstrap ${target} ${plistPath}\n Recovery: \`launchctl bootout ${target}/${LAUNCHD_LABEL}\` then \`first-tree-hub
|
|
8394
|
+
if (lastBootstrapErr) throw new Error(`launchctl bootstrap failed: ${lastBootstrapErr.stderr || `exit ${lastBootstrapErr.code ?? "unknown"}`}\n Command: launchctl bootstrap ${target} ${plistPath}\n Recovery: \`launchctl bootout ${target}/${LAUNCHD_LABEL}\` then \`first-tree-hub connect <token>\`.`);
|
|
8335
8395
|
const enableRes = runCapture("launchctl", ["enable", `${target}/${LAUNCHD_LABEL}`], 5e3);
|
|
8336
8396
|
if (!enableRes.ok) print.line(` warning: launchctl enable: ${enableRes.stderr || `exit ${enableRes.code ?? "unknown"}`}\n`);
|
|
8337
8397
|
const { state, pid, detail } = launchdState();
|
|
@@ -8491,7 +8551,7 @@ function installSystemd() {
|
|
|
8491
8551
|
"--now",
|
|
8492
8552
|
SYSTEMD_UNIT
|
|
8493
8553
|
], 1e4);
|
|
8494
|
-
if (!enableRes.ok) throw new Error(`systemctl --user enable --now ${SYSTEMD_UNIT} failed: ${enableRes.stderr || `exit ${enableRes.code ?? "unknown"}`}\n Recovery: \`systemctl --user stop ${SYSTEMD_UNIT}\` then \`first-tree-hub
|
|
8554
|
+
if (!enableRes.ok) throw new Error(`systemctl --user enable --now ${SYSTEMD_UNIT} failed: ${enableRes.stderr || `exit ${enableRes.code ?? "unknown"}`}\n Recovery: \`systemctl --user stop ${SYSTEMD_UNIT}\` then \`first-tree-hub connect <token>\`.`);
|
|
8495
8555
|
const { state, pid, detail } = systemdState();
|
|
8496
8556
|
return {
|
|
8497
8557
|
platform: "systemd",
|
|
@@ -9003,7 +9063,7 @@ function checkBackgroundService() {
|
|
|
9003
9063
|
return {
|
|
9004
9064
|
label: "Background service",
|
|
9005
9065
|
ok: false,
|
|
9006
|
-
detail: "not installed — re-run `first-tree-hub
|
|
9066
|
+
detail: "not installed — re-run `first-tree-hub connect <token>` to install"
|
|
9007
9067
|
};
|
|
9008
9068
|
}
|
|
9009
9069
|
async function checkWebSocket() {
|
|
@@ -9297,7 +9357,7 @@ function runHomeMigration() {
|
|
|
9297
9357
|
}
|
|
9298
9358
|
print.line(`[first-tree-hub] Copied client home to new layout: ${result.from} → ${result.to}\n (Legacy directory preserved as a backup — delete it manually once you've verified the new location works.)\n`);
|
|
9299
9359
|
if (process.argv.includes("--no-interactive")) {
|
|
9300
|
-
print.line("[first-tree-hub] Note: running as background service — skipped auto re-register to avoid self-termination.\n Service paths will refresh on the next `first-tree-hub
|
|
9360
|
+
print.line("[first-tree-hub] Note: running as background service — skipped auto re-register to avoid self-termination.\n Service paths will refresh on the next `first-tree-hub connect <token>`.\n");
|
|
9301
9361
|
return;
|
|
9302
9362
|
}
|
|
9303
9363
|
const status = getClientServiceStatus();
|
|
@@ -9307,7 +9367,7 @@ function runHomeMigration() {
|
|
|
9307
9367
|
print.line(`[first-tree-hub] Re-registered background service with new home paths.\n`);
|
|
9308
9368
|
} catch (err) {
|
|
9309
9369
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9310
|
-
print.line(`[first-tree-hub] WARNING: home migration succeeded but re-registering the background service failed: ${msg}\n Re-run \`first-tree-hub
|
|
9370
|
+
print.line(`[first-tree-hub] WARNING: home migration succeeded but re-registering the background service failed: ${msg}\n Re-run \`first-tree-hub connect <token>\` to refresh service paths.\n`);
|
|
9311
9371
|
}
|
|
9312
9372
|
}
|
|
9313
9373
|
//#endregion
|
|
@@ -9339,7 +9399,7 @@ async function onboardCheck(args) {
|
|
|
9339
9399
|
key: "connect",
|
|
9340
9400
|
label: "Signed in",
|
|
9341
9401
|
status: "missing_required",
|
|
9342
|
-
hint: "Run `first-tree-hub
|
|
9402
|
+
hint: "Run `first-tree-hub connect <token>` first"
|
|
9343
9403
|
});
|
|
9344
9404
|
try {
|
|
9345
9405
|
const serverUrl = resolveServerUrl(args.server);
|
|
@@ -9493,7 +9553,7 @@ async function onboardCreate(args) {
|
|
|
9493
9553
|
}
|
|
9494
9554
|
const runtimeAgent = args.type === "human" ? args.assistant : args.id;
|
|
9495
9555
|
if (args.feishuBotAppId && args.feishuBotAppSecret) {
|
|
9496
|
-
const { bindFeishuBot } = await import("./feishu-
|
|
9556
|
+
const { bindFeishuBot } = await import("./feishu-D_vnqC6a.mjs").then((n) => n.r);
|
|
9497
9557
|
const targetAgentUuid = args.type === "human" ? assistantUuid : primary.uuid;
|
|
9498
9558
|
if (!targetAgentUuid) print.line(`Warning: Cannot bind Feishu bot — no runtime agent available for "${args.id}".\n`);
|
|
9499
9559
|
else {
|
|
@@ -9580,13 +9640,13 @@ async function promptMissingFields(options) {
|
|
|
9580
9640
|
* add (there's nothing sensible to key the local dir on).
|
|
9581
9641
|
*/
|
|
9582
9642
|
async function promptAddAgent(opts = {}) {
|
|
9583
|
-
if (loadCredentials() === null) throw new Error("Not connected. Run `first-tree-hub
|
|
9643
|
+
if (loadCredentials() === null) throw new Error("Not connected. Run `first-tree-hub connect <token>` first.");
|
|
9584
9644
|
let serverUrl;
|
|
9585
9645
|
try {
|
|
9586
9646
|
serverUrl = resolveServerUrl(process.env.FIRST_TREE_HUB_SERVER_URL);
|
|
9587
9647
|
} catch (err) {
|
|
9588
9648
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9589
|
-
throw new Error(`${msg} Run \`first-tree-hub
|
|
9649
|
+
throw new Error(`${msg} Run \`first-tree-hub connect <token>\` or set FIRST_TREE_HUB_SERVER_URL.`);
|
|
9590
9650
|
}
|
|
9591
9651
|
const agentId = opts.agentId ?? await input({
|
|
9592
9652
|
message: "Agent UUID on the Hub:",
|
|
@@ -10706,7 +10766,7 @@ function createFeedbackHandler(config) {
|
|
|
10706
10766
|
return { handle };
|
|
10707
10767
|
}
|
|
10708
10768
|
//#endregion
|
|
10709
|
-
//#region ../server/dist/app-
|
|
10769
|
+
//#region ../server/dist/app-DQVUb4ZY.mjs
|
|
10710
10770
|
var import_fastify_opentelemetry = /* @__PURE__ */ __toESM(require_fastify_opentelemetry(), 1);
|
|
10711
10771
|
init_esm();
|
|
10712
10772
|
var __defProp = Object.defineProperty;
|
|
@@ -11406,7 +11466,7 @@ async function resolveAgentClient(db, data) {
|
|
|
11406
11466
|
userId: clients.userId
|
|
11407
11467
|
}).from(clients).where(eq(clients.id, data.clientId)).limit(1);
|
|
11408
11468
|
if (!client) throw new BadRequestError(`Client "${data.clientId}" not found`);
|
|
11409
|
-
if (!client.userId) throw new BadRequestError(`Client "${data.clientId}" has not been claimed by a user yet. Have the operator run \`first-tree-hub
|
|
11469
|
+
if (!client.userId) throw new BadRequestError(`Client "${data.clientId}" has not been claimed by a user yet. Have the operator run \`first-tree-hub connect <token>\` on that machine before pinning an agent to it.`);
|
|
11410
11470
|
if (client.userId !== manager.userId) throw new ForbiddenError(`Client "${data.clientId}" is not owned by the manager's user — pick a client belonging to that user.`);
|
|
11411
11471
|
return client.id;
|
|
11412
11472
|
}
|
|
@@ -12964,8 +13024,9 @@ async function pushToWebhook(notification) {
|
|
|
12964
13024
|
}
|
|
12965
13025
|
/**
|
|
12966
13026
|
* Session events — structured event stream per (agent, chat) session.
|
|
12967
|
-
* `kind` is 'tool_call' | 'error'
|
|
12968
|
-
*
|
|
13027
|
+
* `kind` is one of `'tool_call' | 'error' | 'assistant_text' | 'thinking'
|
|
13028
|
+
* | 'turn_end'`; payload shape per kind is enforced by the service layer
|
|
13029
|
+
* via Zod (no FK / CHECK on this table per project rule).
|
|
12969
13030
|
*
|
|
12970
13031
|
* `seq` is monotonic per (agent_id, chat_id). The single-writer invariant
|
|
12971
13032
|
* in the client-side session-manager guarantees ordering; the service wraps
|
|
@@ -13623,9 +13684,11 @@ function clientWsRoutes(notifier, instanceId) {
|
|
|
13623
13684
|
return;
|
|
13624
13685
|
}
|
|
13625
13686
|
const payload = sessionEventMessageSchema.parse(msg);
|
|
13687
|
+
const boundInfo = boundAgents.get(agentId);
|
|
13626
13688
|
chainSessionOp(agentId, payload.chatId, async () => {
|
|
13627
13689
|
try {
|
|
13628
13690
|
await appendEvent(app.db, agentId, payload.chatId, payload.event);
|
|
13691
|
+
if (boundInfo) notifier.notifySessionEvent(agentId, payload.chatId, payload.event.kind, boundInfo.organizationId).catch(() => {});
|
|
13629
13692
|
} catch (err) {
|
|
13630
13693
|
socket.send(JSON.stringify({
|
|
13631
13694
|
type: "error",
|
|
@@ -13827,7 +13890,7 @@ async function agentRoutes(app) {
|
|
|
13827
13890
|
};
|
|
13828
13891
|
if (health === "disconnected") return reply.status(200).send({
|
|
13829
13892
|
status: "offline",
|
|
13830
|
-
message: "Agent is not connected.
|
|
13893
|
+
message: "Agent is not connected. Connect the client with: first-tree-hub connect <token>",
|
|
13831
13894
|
connection
|
|
13832
13895
|
});
|
|
13833
13896
|
if (health === "stale") return reply.status(200).send({
|
|
@@ -15857,10 +15920,10 @@ async function listMeChats(db, humanAgentId, organizationId, query) {
|
|
|
15857
15920
|
agentId: chatMembership.agentId,
|
|
15858
15921
|
displayName: agents.displayName,
|
|
15859
15922
|
type: agents.type,
|
|
15860
|
-
|
|
15861
|
-
}).from(chatMembership).innerJoin(agents, eq(chatMembership.agentId, agents.uuid)).leftJoin(
|
|
15923
|
+
sessionState: agentChatSessions.state
|
|
15924
|
+
}).from(chatMembership).innerJoin(agents, eq(chatMembership.agentId, agents.uuid)).leftJoin(agentChatSessions, and(eq(agentChatSessions.agentId, chatMembership.agentId), eq(agentChatSessions.chatId, chatMembership.chatId))).where(and(inArray(chatMembership.chatId, chatIds), eq(chatMembership.accessMode, "speaker")));
|
|
15862
15925
|
const participantsByChat = /* @__PURE__ */ new Map();
|
|
15863
|
-
const
|
|
15926
|
+
const engagedByChat = /* @__PURE__ */ new Map();
|
|
15864
15927
|
for (const p of participantRows) {
|
|
15865
15928
|
const list = participantsByChat.get(p.chatId) ?? [];
|
|
15866
15929
|
list.push({
|
|
@@ -15869,12 +15932,13 @@ async function listMeChats(db, humanAgentId, organizationId, query) {
|
|
|
15869
15932
|
type: p.type
|
|
15870
15933
|
});
|
|
15871
15934
|
participantsByChat.set(p.chatId, list);
|
|
15872
|
-
if (p.
|
|
15873
|
-
const
|
|
15874
|
-
|
|
15875
|
-
|
|
15935
|
+
if (p.sessionState === "active") {
|
|
15936
|
+
const engaged = engagedByChat.get(p.chatId) ?? [];
|
|
15937
|
+
engaged.push(p.agentId);
|
|
15938
|
+
engagedByChat.set(p.chatId, engaged);
|
|
15876
15939
|
}
|
|
15877
15940
|
}
|
|
15941
|
+
const liveActivityByChat = await deriveLiveActivity(db, chatIds);
|
|
15878
15942
|
const firstMessageRows = chatIds.length > 0 ? await db.selectDistinctOn([messages.chatId], {
|
|
15879
15943
|
chatId: messages.chatId,
|
|
15880
15944
|
content: messages.content
|
|
@@ -15902,13 +15966,98 @@ async function listMeChats(db, humanAgentId, organizationId, query) {
|
|
|
15902
15966
|
unreadMentionCount: r.unread_mention_count,
|
|
15903
15967
|
canReply: isSpeaker,
|
|
15904
15968
|
engagementStatus: r.engagement_status,
|
|
15905
|
-
|
|
15969
|
+
engagedAgentIds: engagedByChat.get(r.chat_id) ?? [],
|
|
15970
|
+
liveActivity: liveActivityByChat.get(r.chat_id) ?? null
|
|
15906
15971
|
};
|
|
15907
15972
|
}),
|
|
15908
15973
|
nextCursor
|
|
15909
15974
|
};
|
|
15910
15975
|
}
|
|
15911
15976
|
/**
|
|
15977
|
+
* Per-chat live activity, derived from the most recent `session_events` row.
|
|
15978
|
+
*
|
|
15979
|
+
* Returns a chatId → LiveActivity map; chats with no activity (or where the
|
|
15980
|
+
* latest event is terminal / stale) are absent from the map (caller treats
|
|
15981
|
+
* absence as null).
|
|
15982
|
+
*/
|
|
15983
|
+
async function deriveLiveActivity(db, chatIds) {
|
|
15984
|
+
if (chatIds.length === 0) return /* @__PURE__ */ new Map();
|
|
15985
|
+
const chatIdInClause = sql.join(chatIds.map((id) => sql`${id}`), sql`, `);
|
|
15986
|
+
const rows = (await db.execute(sql`
|
|
15987
|
+
SELECT acs.agent_id AS agent_id,
|
|
15988
|
+
acs.chat_id AS chat_id,
|
|
15989
|
+
e.kind AS kind,
|
|
15990
|
+
e.payload AS payload,
|
|
15991
|
+
e.created_at AS created_at
|
|
15992
|
+
FROM agent_chat_sessions acs
|
|
15993
|
+
CROSS JOIN LATERAL (
|
|
15994
|
+
SELECT kind, payload, created_at, seq
|
|
15995
|
+
FROM session_events se
|
|
15996
|
+
WHERE se.agent_id = acs.agent_id
|
|
15997
|
+
AND se.chat_id = acs.chat_id
|
|
15998
|
+
ORDER BY se.seq DESC
|
|
15999
|
+
LIMIT 1
|
|
16000
|
+
) e
|
|
16001
|
+
WHERE acs.chat_id IN (${chatIdInClause})
|
|
16002
|
+
AND acs.state <> 'evicted'
|
|
16003
|
+
`)).map((r) => ({
|
|
16004
|
+
agent_id: r.agent_id,
|
|
16005
|
+
chat_id: r.chat_id,
|
|
16006
|
+
kind: r.kind,
|
|
16007
|
+
payload: r.payload,
|
|
16008
|
+
created_at: r.created_at
|
|
16009
|
+
}));
|
|
16010
|
+
const now = Date.now();
|
|
16011
|
+
const byChat = /* @__PURE__ */ new Map();
|
|
16012
|
+
for (const row of rows) {
|
|
16013
|
+
const activity = toLiveActivity(row);
|
|
16014
|
+
if (!activity) continue;
|
|
16015
|
+
const createdAtMs = new Date(row.created_at).getTime();
|
|
16016
|
+
if (now - createdAtMs > 6e4) continue;
|
|
16017
|
+
const existing = byChat.get(row.chat_id);
|
|
16018
|
+
if (!existing || createdAtMs > existing.createdAtMs) byChat.set(row.chat_id, {
|
|
16019
|
+
activity,
|
|
16020
|
+
createdAtMs
|
|
16021
|
+
});
|
|
16022
|
+
}
|
|
16023
|
+
const out = /* @__PURE__ */ new Map();
|
|
16024
|
+
for (const [chatId, { activity }] of byChat) out.set(chatId, activity);
|
|
16025
|
+
return out;
|
|
16026
|
+
}
|
|
16027
|
+
/**
|
|
16028
|
+
* Translate a `session_events` row into a `LiveActivity`, or null when the
|
|
16029
|
+
* kind is terminal (`turn_end` / `error`) or unrecognised. Pure & exported
|
|
16030
|
+
* for unit testing.
|
|
16031
|
+
*/
|
|
16032
|
+
function toLiveActivity(row) {
|
|
16033
|
+
const startedAt = new Date(row.created_at).toISOString();
|
|
16034
|
+
switch (row.kind) {
|
|
16035
|
+
case "tool_call": {
|
|
16036
|
+
const payload = row.payload ?? {};
|
|
16037
|
+
const label = typeof payload.name === "string" && payload.name.length > 0 ? payload.name : "Tool";
|
|
16038
|
+
return {
|
|
16039
|
+
agentId: row.agent_id,
|
|
16040
|
+
kind: "tool_call",
|
|
16041
|
+
label,
|
|
16042
|
+
startedAt
|
|
16043
|
+
};
|
|
16044
|
+
}
|
|
16045
|
+
case "thinking": return {
|
|
16046
|
+
agentId: row.agent_id,
|
|
16047
|
+
kind: "thinking",
|
|
16048
|
+
label: "Thinking",
|
|
16049
|
+
startedAt
|
|
16050
|
+
};
|
|
16051
|
+
case "assistant_text": return {
|
|
16052
|
+
agentId: row.agent_id,
|
|
16053
|
+
kind: "assistant_text",
|
|
16054
|
+
label: "Writing",
|
|
16055
|
+
startedAt
|
|
16056
|
+
};
|
|
16057
|
+
default: return null;
|
|
16058
|
+
}
|
|
16059
|
+
}
|
|
16060
|
+
/**
|
|
15912
16061
|
* Title resolution priority:
|
|
15913
16062
|
*
|
|
15914
16063
|
* 1. `chat.topic` (manual, set via `PATCH /chats/:chatId`)
|
|
@@ -17570,7 +17719,7 @@ async function healthzRoutes(app) {
|
|
|
17570
17719
|
* `api/orgs/invitations.ts` (Class B, admin-gated).
|
|
17571
17720
|
*/
|
|
17572
17721
|
async function publicInvitationRoutes(app) {
|
|
17573
|
-
const { previewInvitation } = await import("./invitation-
|
|
17722
|
+
const { previewInvitation } = await import("./invitation-CNv7gfFF-D93KQte0.mjs");
|
|
17574
17723
|
app.get("/:token/preview", async (request, reply) => {
|
|
17575
17724
|
if (!request.params.token) throw new UnauthorizedError("Token required");
|
|
17576
17725
|
const preview = await previewInvitation(app.db, request.params.token);
|
|
@@ -17640,7 +17789,8 @@ async function meRoutes(app) {
|
|
|
17640
17789
|
username: users.username,
|
|
17641
17790
|
displayName: users.displayName,
|
|
17642
17791
|
avatarUrl: users.avatarUrl,
|
|
17643
|
-
onboardingDismissedAt: users.onboardingDismissedAt
|
|
17792
|
+
onboardingDismissedAt: users.onboardingDismissedAt,
|
|
17793
|
+
onboardingCompletedAt: users.onboardingCompletedAt
|
|
17644
17794
|
}).from(users).where(eq(users.id, userId)).limit(1);
|
|
17645
17795
|
const memberships = await listActiveMemberships(app.db, userId);
|
|
17646
17796
|
const defaultMembership = pickDefaultMembership(memberships.map((m) => ({
|
|
@@ -17668,7 +17818,8 @@ async function meRoutes(app) {
|
|
|
17668
17818
|
})),
|
|
17669
17819
|
onboarding: {
|
|
17670
17820
|
step: onboardingStep,
|
|
17671
|
-
dismissedAt: user?.onboardingDismissedAt ? user.onboardingDismissedAt.toISOString() : null
|
|
17821
|
+
dismissedAt: user?.onboardingDismissedAt ? user.onboardingDismissedAt.toISOString() : null,
|
|
17822
|
+
completedAt: user?.onboardingCompletedAt ? user.onboardingCompletedAt.toISOString() : null
|
|
17672
17823
|
},
|
|
17673
17824
|
inviteUrl
|
|
17674
17825
|
};
|
|
@@ -17694,6 +17845,25 @@ async function meRoutes(app) {
|
|
|
17694
17845
|
return reply.status(200).send({ dismissedAt: u?.onboardingDismissedAt ? u.onboardingDismissedAt.toISOString() : null });
|
|
17695
17846
|
});
|
|
17696
17847
|
/**
|
|
17848
|
+
* POST /me/onboarding-completed — stamp the terminal-state column when
|
|
17849
|
+
* the user walks Step 3 to success (admin Continue, invitee Confirm /
|
|
17850
|
+
* Continue). Distinct from PATCH /me/onboarding { dismissed: true },
|
|
17851
|
+
* which only hides the stepper UI. Once stamped, the web sidebar drops
|
|
17852
|
+
* the Settings → Onboarding entry point and /settings/onboarding
|
|
17853
|
+
* redirects, so the wizard cannot re-enter.
|
|
17854
|
+
*
|
|
17855
|
+
* Idempotent: only writes when the column is still NULL — re-calling on
|
|
17856
|
+
* an already-completed user is a no-op rather than resetting the stamp.
|
|
17857
|
+
*/
|
|
17858
|
+
app.post("/me/onboarding-completed", async (request, reply) => {
|
|
17859
|
+
const { userId } = requireUser(request);
|
|
17860
|
+
if ((await app.db.update(users).set({ onboardingCompletedAt: /* @__PURE__ */ new Date() }).where(and(eq(users.id, userId), isNull(users.onboardingCompletedAt))).returning({ id: users.id })).length > 0) app.log.info({
|
|
17861
|
+
event: "onboarding.completed",
|
|
17862
|
+
userId
|
|
17863
|
+
}, "onboarding funnel: setup completed");
|
|
17864
|
+
return reply.status(200).send({ ok: true });
|
|
17865
|
+
});
|
|
17866
|
+
/**
|
|
17697
17867
|
* POST /me/onboarding/events — web-side onboarding funnel reporter.
|
|
17698
17868
|
* Server-side milestones (`team_created` at OAuth, `dismissed` on PATCH)
|
|
17699
17869
|
* are emitted directly; this endpoint surfaces the web-driven ones into
|
|
@@ -17836,7 +18006,7 @@ async function meRoutes(app) {
|
|
|
17836
18006
|
*/
|
|
17837
18007
|
app.get("/me/pinned-agents", async (request) => {
|
|
17838
18008
|
const { userId } = requireUser(request);
|
|
17839
|
-
const { listMyPinnedAgents } = await import("./client-
|
|
18009
|
+
const { listMyPinnedAgents } = await import("./client-h4KZ3b9o-CQyibXig.mjs");
|
|
17840
18010
|
return listMyPinnedAgents(app.db, { userId });
|
|
17841
18011
|
});
|
|
17842
18012
|
/**
|
|
@@ -18757,6 +18927,12 @@ function orgWsRoutes(notifier, jwtSecret) {
|
|
|
18757
18927
|
...payload
|
|
18758
18928
|
});
|
|
18759
18929
|
});
|
|
18930
|
+
notifier.onSessionEvent((payload) => {
|
|
18931
|
+
broadcastOrgScoped({
|
|
18932
|
+
type: "session:event",
|
|
18933
|
+
...payload
|
|
18934
|
+
});
|
|
18935
|
+
});
|
|
18760
18936
|
notifier.onChatMessage(({ chatId }) => {
|
|
18761
18937
|
dispatchChatMessage(chatId);
|
|
18762
18938
|
});
|
|
@@ -22147,7 +22323,7 @@ const declineUpdate = async () => false;
|
|
|
22147
22323
|
* relaunch picks up the new binary.
|
|
22148
22324
|
*
|
|
22149
22325
|
* `managed=false` means the process is running standalone (e.g. manual
|
|
22150
|
-
* `client start`, `
|
|
22326
|
+
* `client start`, `connect <token> --no-service`, CI without a supervisor).
|
|
22151
22327
|
* Exiting in that mode would leave the client offline until an operator
|
|
22152
22328
|
* noticed — so the callback instead prints a restart hint, returns
|
|
22153
22329
|
* `{ installed: true }`, and the UpdateManager stops retrying until the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{c as r,j as e,U as o,L as s,A as i,F as a,r as l}from"./index-
|
|
1
|
+
import{c as r,j as e,U as o,L as s,A as i,F as a,r as l}from"./index-DL_9NFkt.js";/**
|
|
2
2
|
* @license lucide-react v0.577.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|