@agent-team-foundation/first-tree-hub 0.9.7 → 0.9.9
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-DWifXj9b.mjs → bootstrap-hh_PkTu6.mjs} +16 -0
- package/dist/cli/index.mjs +24 -10
- package/dist/{core-USyOOh7y.mjs → core-B2YUTpgg.mjs} +2100 -400
- package/dist/drizzle/0023_clients_org_scoping.sql +40 -0
- package/dist/drizzle/meta/_journal.json +7 -0
- package/dist/{feishu-GlaczcVf.mjs → feishu-B1Kiq7S6.mjs} +104 -14
- package/dist/index.mjs +4 -4
- package/dist/web/assets/{index-CzlAfdgm.js → index-DkzjED0c.js} +77 -77
- package/dist/web/index.html +15 -1
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
-- Multi-tenancy hardening:
|
|
2
|
+
-- 1. Drop dead column `agents.cloud_user_id` (unused since introduction in
|
|
3
|
+
-- 0010; never written by any code path).
|
|
4
|
+
-- 2. Bind every client to exactly one organization via `clients.organization_id`.
|
|
5
|
+
--
|
|
6
|
+
-- A client is bound to one org for its lifetime — Rule R-RUN and the
|
|
7
|
+
-- `client:register` handshake reject cross-org reuse of a clientId. See
|
|
8
|
+
-- docs/multi-tenancy-hardening-design.md.
|
|
9
|
+
--
|
|
10
|
+
-- Backfill strategy (guarded for safety across environments):
|
|
11
|
+
-- * Current production: exactly one org → UPDATE fills every row.
|
|
12
|
+
-- * Fresh installs / empty DB: clients table is empty → UPDATE is a no-op,
|
|
13
|
+
-- SET NOT NULL succeeds on the empty table.
|
|
14
|
+
-- * Any environment reaching this migration with multi-org data but
|
|
15
|
+
-- unpopulated clients.organization_id: the guard skips the UPDATE, and
|
|
16
|
+
-- SET NOT NULL fails loudly rather than misassigning rows to an
|
|
17
|
+
-- arbitrary org. Operator must backfill manually, then re-run.
|
|
18
|
+
|
|
19
|
+
ALTER TABLE "agents" DROP COLUMN "cloud_user_id";
|
|
20
|
+
|
|
21
|
+
--> statement-breakpoint
|
|
22
|
+
ALTER TABLE "clients" ADD COLUMN "organization_id" text;
|
|
23
|
+
|
|
24
|
+
--> statement-breakpoint
|
|
25
|
+
ALTER TABLE "clients"
|
|
26
|
+
ADD CONSTRAINT "clients_organization_id_organizations_id_fk"
|
|
27
|
+
FOREIGN KEY ("organization_id") REFERENCES "organizations"("id")
|
|
28
|
+
ON DELETE no action ON UPDATE no action;
|
|
29
|
+
|
|
30
|
+
--> statement-breakpoint
|
|
31
|
+
UPDATE "clients"
|
|
32
|
+
SET "organization_id" = (SELECT "id" FROM "organizations" LIMIT 1)
|
|
33
|
+
WHERE "organization_id" IS NULL
|
|
34
|
+
AND (SELECT count(*) FROM "organizations") = 1;
|
|
35
|
+
|
|
36
|
+
--> statement-breakpoint
|
|
37
|
+
ALTER TABLE "clients" ALTER COLUMN "organization_id" SET NOT NULL;
|
|
38
|
+
|
|
39
|
+
--> statement-breakpoint
|
|
40
|
+
CREATE INDEX IF NOT EXISTS "idx_clients_org" ON "clients" ("organization_id");
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import { d as __exportAll } from "./esm-CYu4tXXn.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region ../shared/dist/index.mjs
|
|
4
|
+
const MENTION_REGEX = /(?<![A-Za-z0-9_.@-])@([A-Za-z0-9_-]{1,64})\b/g;
|
|
5
|
+
function stripCode(content) {
|
|
6
|
+
return content.replace(/```[\s\S]*?```/g, "").replace(/~~~[\s\S]*?~~~/g, "").replace(/`[^`\n]+`/g, "");
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve `@<name>` mentions in `content` to a list of participant agentIds.
|
|
10
|
+
* Names match case-insensitively; unknown `@tokens` are dropped.
|
|
11
|
+
*/
|
|
12
|
+
function extractMentions(content, participants) {
|
|
13
|
+
const stripped = stripCode(content);
|
|
14
|
+
const nameMap = /* @__PURE__ */ new Map();
|
|
15
|
+
for (const p of participants) if (p.name) nameMap.set(p.name.toLowerCase(), p.agentId);
|
|
16
|
+
if (nameMap.size === 0) return [];
|
|
17
|
+
const hits = /* @__PURE__ */ new Set();
|
|
18
|
+
for (const m of stripped.matchAll(MENTION_REGEX)) {
|
|
19
|
+
const token = m[1];
|
|
20
|
+
if (!token) continue;
|
|
21
|
+
const id = nameMap.get(token.toLowerCase());
|
|
22
|
+
if (id) hits.add(id);
|
|
23
|
+
}
|
|
24
|
+
return [...hits];
|
|
25
|
+
}
|
|
4
26
|
const adapterPlatformSchema = z.enum([
|
|
5
27
|
"feishu",
|
|
6
28
|
"slack",
|
|
@@ -187,7 +209,6 @@ z.object({
|
|
|
187
209
|
inboxId: z.string(),
|
|
188
210
|
status: z.string(),
|
|
189
211
|
source: z.string().nullable().optional(),
|
|
190
|
-
cloudUserId: z.string().nullable().optional(),
|
|
191
212
|
visibility: agentVisibilitySchema,
|
|
192
213
|
metadata: z.record(z.string(), z.unknown()),
|
|
193
214
|
managerId: z.string().nullable(),
|
|
@@ -404,6 +425,11 @@ const chatParticipantSchema = z.object({
|
|
|
404
425
|
mode: z.string(),
|
|
405
426
|
joinedAt: z.string()
|
|
406
427
|
});
|
|
428
|
+
chatParticipantSchema.extend({
|
|
429
|
+
name: z.string().nullable(),
|
|
430
|
+
displayName: z.string().nullable(),
|
|
431
|
+
type: z.string()
|
|
432
|
+
});
|
|
407
433
|
z.object({
|
|
408
434
|
id: z.string(),
|
|
409
435
|
organizationId: z.string(),
|
|
@@ -443,6 +469,41 @@ const paginationQuerySchema = z.object({
|
|
|
443
469
|
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
444
470
|
cursor: z.string().optional()
|
|
445
471
|
});
|
|
472
|
+
const supportedImageMimeSchema = z.enum([
|
|
473
|
+
"image/png",
|
|
474
|
+
"image/jpeg",
|
|
475
|
+
"image/gif",
|
|
476
|
+
"image/webp"
|
|
477
|
+
]);
|
|
478
|
+
/**
|
|
479
|
+
* Legacy inbound shape: an image message with base64 bytes inlined into
|
|
480
|
+
* `messages.content`. Web still uploads in this shape so no new endpoint is
|
|
481
|
+
* needed; the server extracts the bytes, broadcasts them as an `image_payload`
|
|
482
|
+
* WS frame, then rewrites `content` to {@link imageRefContentSchema} before
|
|
483
|
+
* the DB insert.
|
|
484
|
+
*/
|
|
485
|
+
const imageInlineContentSchema = z.object({
|
|
486
|
+
data: z.string().min(1),
|
|
487
|
+
mimeType: supportedImageMimeSchema,
|
|
488
|
+
filename: z.string().min(1),
|
|
489
|
+
size: z.number().int().nonnegative().optional(),
|
|
490
|
+
imageId: z.string().uuid().optional()
|
|
491
|
+
});
|
|
492
|
+
z.object({
|
|
493
|
+
imageId: z.string().uuid(),
|
|
494
|
+
mimeType: supportedImageMimeSchema,
|
|
495
|
+
filename: z.string().min(1),
|
|
496
|
+
size: z.number().int().nonnegative().optional()
|
|
497
|
+
});
|
|
498
|
+
z.object({
|
|
499
|
+
type: z.literal("image_payload"),
|
|
500
|
+
imageId: z.string().uuid(),
|
|
501
|
+
chatId: z.string(),
|
|
502
|
+
base64: z.string().min(1),
|
|
503
|
+
mimeType: supportedImageMimeSchema,
|
|
504
|
+
filename: z.string().min(1),
|
|
505
|
+
size: z.number().int().nonnegative().optional()
|
|
506
|
+
});
|
|
446
507
|
const messageSourceSchema = z.enum([
|
|
447
508
|
"hub_ui",
|
|
448
509
|
"cli",
|
|
@@ -475,17 +536,7 @@ const sendToAgentSchema = z.object({
|
|
|
475
536
|
replyToChat: z.string().optional(),
|
|
476
537
|
source: messageSourceSchema.optional()
|
|
477
538
|
});
|
|
478
|
-
|
|
479
|
-
* Wire format for messages routed FROM the Hub TO a client runtime.
|
|
480
|
-
*
|
|
481
|
-
* Adds `configVersion` so the client can compare against its locally cached
|
|
482
|
-
* agent runtime config and refresh before delivering the message to the SDK.
|
|
483
|
-
*
|
|
484
|
-
* Step 3: this is the single shape used by `buildClientMessagePayload` —
|
|
485
|
-
* never serialise a raw `messageSchema` row to a client; always go through
|
|
486
|
-
* the dispatcher.
|
|
487
|
-
*/
|
|
488
|
-
const clientMessageSchema = z.object({
|
|
539
|
+
const messageSchema = z.object({
|
|
489
540
|
id: z.string(),
|
|
490
541
|
chatId: z.string(),
|
|
491
542
|
senderId: z.string(),
|
|
@@ -497,7 +548,46 @@ const clientMessageSchema = z.object({
|
|
|
497
548
|
inReplyTo: z.string().nullable(),
|
|
498
549
|
source: messageSourceSchema.nullable(),
|
|
499
550
|
createdAt: z.string()
|
|
500
|
-
})
|
|
551
|
+
});
|
|
552
|
+
/**
|
|
553
|
+
* Snapshot of the `in_reply_to` target that the server materialises at
|
|
554
|
+
* dispatch time so the receiving runtime can decide whether this is an
|
|
555
|
+
* echo it should suppress (see proposal hub-agent-messaging-reply-and-mentions).
|
|
556
|
+
*
|
|
557
|
+
* `chatId` is the original message's `chat_id`; `replyToChat` is the chat
|
|
558
|
+
* its sender expected replies to flow back to (often a different chat).
|
|
559
|
+
* `null` when the message is not a reply, or the original could not be
|
|
560
|
+
* resolved (e.g. deleted).
|
|
561
|
+
*/
|
|
562
|
+
const inReplyToSnapshotSchema = z.object({
|
|
563
|
+
senderId: z.string(),
|
|
564
|
+
chatId: z.string(),
|
|
565
|
+
replyToChat: z.string().nullable()
|
|
566
|
+
}).nullable();
|
|
567
|
+
/** Per-chat participation mode exposed to the recipient runtime. */
|
|
568
|
+
const participantModeSchema = z.enum(["full", "mention_only"]);
|
|
569
|
+
/**
|
|
570
|
+
* Wire format for messages routed FROM the Hub TO a client runtime.
|
|
571
|
+
*
|
|
572
|
+
* Adds `configVersion` so the client can compare against its locally cached
|
|
573
|
+
* agent runtime config and refresh before delivering the message to the SDK.
|
|
574
|
+
*
|
|
575
|
+
* Step 3: this is the single shape used by `buildClientMessagePayload` —
|
|
576
|
+
* never serialise a raw `messageSchema` row to a client; always go through
|
|
577
|
+
* the dispatcher.
|
|
578
|
+
*
|
|
579
|
+
* `recipientMode` is the receiving agent's own mode in the entry's chat —
|
|
580
|
+
* `mention_only` participants must only start a session when they appear in
|
|
581
|
+
* `metadata.mentions` (see session-manager.ts).
|
|
582
|
+
*
|
|
583
|
+
* `inReplyToSnapshot` is populated when `inReplyTo` resolves to an existing
|
|
584
|
+
* message; runtime uses it to suppress self-reply echo on direct chats.
|
|
585
|
+
*/
|
|
586
|
+
const clientMessageSchema = messageSchema.extend({
|
|
587
|
+
configVersion: z.number().int().positive(),
|
|
588
|
+
recipientMode: participantModeSchema.default("full"),
|
|
589
|
+
inReplyToSnapshot: inReplyToSnapshotSchema.default(null)
|
|
590
|
+
});
|
|
501
591
|
z.enum([
|
|
502
592
|
"pending",
|
|
503
593
|
"delivered",
|
|
@@ -959,4 +1049,4 @@ async function bindFeishuUser(serverUrl, accessToken, agentId, humanAgentId, fei
|
|
|
959
1049
|
}
|
|
960
1050
|
}
|
|
961
1051
|
//#endregion
|
|
962
|
-
export {
|
|
1052
|
+
export { sessionStateMessageSchema as $, createMemberSchema as A, loginSchema as B, agentTypeSchema as C, createAdapterMappingSchema as D, createAdapterConfigSchema as E, extractMentions as F, runtimeStateMessageSchema as G, notificationQuerySchema as H, imageInlineContentSchema as I, sendToAgentSchema as J, selfServiceFeishuBotSchema as K, inboxPollQuerySchema as L, createTaskSchema as M, delegateFeishuUserSchema as N, createAgentSchema as O, dryRunAgentRuntimeConfigSchema as P, sessionReconcileRequestSchema as Q, isRedactedEnvValue as R, agentRuntimeConfigPayloadSchema as S, connectTokenExchangeSchema as T, paginationQuerySchema as U, messageSourceSchema as V, refreshTokenSchema as W, sessionEventMessageSchema as X, sessionCompletionMessageSchema as Y, sessionEventSchema as Z, addParticipantSchema as _, AGENT_SELECTOR_HEADER as a, updateMemberSchema as at, agentBindRequestSchema as b, AGENT_TYPES as c, updateTaskStatusSchema as ct, SYSTEM_CONFIG_DEFAULTS as d, taskListQuerySchema as et, TASK_CREATOR_TYPES as f, WS_AUTH_FRAME_TIMEOUT_MS as g, TASK_TERMINAL_STATUSES as h, AGENT_BIND_REJECT_REASONS as i, updateChatSchema as it, createOrganizationSchema as j, createChatSchema as k, AGENT_VISIBILITY as l, wsAuthFrameSchema as lt, TASK_STATUSES as m, bindFeishuUser as n, updateAgentRuntimeConfigSchema as nt, AGENT_SOURCES as o, updateOrganizationSchema as ot, TASK_HEALTH_SIGNALS as p, sendMessageSchema as q, feishu_exports as r, updateAgentSchema as rt, AGENT_STATUSES as s, updateSystemConfigSchema as st, bindFeishuBot as t, updateAdapterConfigSchema as tt, DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD as u, adminCreateTaskSchema as v, clientRegisterSchema as w, agentPinnedMessageSchema as x, adminUpdateTaskSchema as y, linkTaskChatSchema as z };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./observability-DV_fQKqV-CuLWzBxQ.mjs";
|
|
2
|
-
import { A as checkServerHealth, B as
|
|
2
|
+
import { A as checkServerHealth, B as createOwner, C as runMigrations, D as checkDocker, E as checkDatabase, F as isDockerAvailable, I as stopPostgres, J as FirstTreeHubSDK, K as status, L as ClientRuntime, M as checkWebSocket, N as printResults, O as checkNodeVersion, P as ensurePostgres, R as handleClientOrgMismatch, S as uninstallClientService, T as checkClientConfig, U as blank, V as hasUser, Y as SdkError, _ as runHomeMigration, b as isServiceSupported, d as promptMissingFields, f as formatCheckReport, h as onboardCreate, j as checkServerReachable, k as checkServerConfig, l as isInteractive, m as onboardCheck, s as startServer, u as promptAddAgent, v as getClientServiceStatus, w as checkAgentConfigs, x as resolveCliInvocation, y as installClientService, z as rotateClientIdWithBackup } from "./core-B2YUTpgg.mjs";
|
|
3
3
|
import "./logger-core-BTmvdflj-DhdipBkV.mjs";
|
|
4
|
-
import { a as resolveAccessToken, n as ensureFreshAccessToken, o as resolveServerUrl, r as ensureFreshAdminToken } from "./bootstrap-
|
|
5
|
-
import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-
|
|
6
|
-
export { ClientRuntime, FirstTreeHubSDK, SdkError, bindFeishuBot, bindFeishuUser, blank, checkAgentConfigs, checkClientConfig, checkDatabase, checkDocker, checkNodeVersion, checkServerConfig, checkServerHealth, checkServerReachable, checkWebSocket, createOwner, ensureFreshAccessToken, ensureFreshAdminToken, ensurePostgres, formatCheckReport, getClientServiceStatus, hasUser, installClientService, isDockerAvailable, isInteractive, isServiceSupported, onboardCheck, onboardCreate, printResults, promptAddAgent, promptMissingFields, resolveAccessToken, resolveCliInvocation, resolveServerUrl, runHomeMigration, runMigrations, startServer, status, stopPostgres, uninstallClientService };
|
|
4
|
+
import { a as resolveAccessToken, n as ensureFreshAccessToken, o as resolveServerUrl, r as ensureFreshAdminToken } from "./bootstrap-hh_PkTu6.mjs";
|
|
5
|
+
import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-B1Kiq7S6.mjs";
|
|
6
|
+
export { ClientRuntime, FirstTreeHubSDK, SdkError, bindFeishuBot, bindFeishuUser, blank, checkAgentConfigs, checkClientConfig, checkDatabase, checkDocker, checkNodeVersion, checkServerConfig, checkServerHealth, checkServerReachable, checkWebSocket, createOwner, ensureFreshAccessToken, ensureFreshAdminToken, ensurePostgres, formatCheckReport, getClientServiceStatus, handleClientOrgMismatch, hasUser, installClientService, isDockerAvailable, isInteractive, isServiceSupported, onboardCheck, onboardCreate, printResults, promptAddAgent, promptMissingFields, resolveAccessToken, resolveCliInvocation, resolveServerUrl, rotateClientIdWithBackup, runHomeMigration, runMigrations, startServer, status, stopPostgres, uninstallClientService };
|