@agent-team-foundation/first-tree-hub 0.9.11 → 0.10.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/cli/index.mjs +35 -19
- package/dist/{core-CuSIXoof.mjs → core-BgiFGT7Y.mjs} +435 -50
- package/dist/drizzle/0024_display_name_not_null.sql +31 -0
- package/dist/drizzle/0025_inbox_silent_entries.sql +53 -0
- package/dist/drizzle/meta/_journal.json +14 -0
- package/dist/{feishu-B2sjp6Z6.mjs → feishu-DEmwoNn_.mjs} +45 -7
- package/dist/index.mjs +2 -2
- package/dist/web/assets/{index-Dwp1u5SF.js → index-Wgxk3V_m.js} +77 -77
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
-- Phase 2 of the agent-naming refactor (docs/agent-naming-design.md §4).
|
|
2
|
+
--
|
|
3
|
+
-- Before this migration, `display_name` was nullable and the web layer
|
|
4
|
+
-- silently fell back to `name` for rendering. CLI, server logs, and IM
|
|
5
|
+
-- bridges saw raw NULLs. This migration closes the gap in three steps:
|
|
6
|
+
--
|
|
7
|
+
-- 1. Backfill every NULL row with a non-empty label — the agent's `name`
|
|
8
|
+
-- when available, else the tombstone literal "[deleted agent]".
|
|
9
|
+
-- Important: the third branch must NOT be `uuid`, because UUIDs would
|
|
10
|
+
-- surface as human-visible strings in the chat roster and IM bridge.
|
|
11
|
+
-- Only `status = 'deleted'` rows can have both `name` and
|
|
12
|
+
-- `display_name` NULL (see `deleteAgent` in services/agent.ts), which
|
|
13
|
+
-- is why the literal refers to deletion.
|
|
14
|
+
--
|
|
15
|
+
-- 2. Add a temporary empty-string DEFAULT so any old server instance
|
|
16
|
+
-- still running during a rolling deploy — i.e. code that doesn't yet
|
|
17
|
+
-- default `display_name` in `createAgent` — can INSERT without
|
|
18
|
+
-- violating the upcoming NOT NULL. The application code treats an
|
|
19
|
+
-- empty string as "no display name" (the Zod read schema accepts it);
|
|
20
|
+
-- a follow-up migration can drop the default once the code is fully
|
|
21
|
+
-- rolled.
|
|
22
|
+
--
|
|
23
|
+
-- 3. Promote the column to NOT NULL. Combined with the service-level
|
|
24
|
+
-- default this closes the hole for new rows too.
|
|
25
|
+
|
|
26
|
+
UPDATE "agents"
|
|
27
|
+
SET "display_name" = COALESCE("display_name", "name", '[deleted agent]')
|
|
28
|
+
WHERE "display_name" IS NULL;
|
|
29
|
+
|
|
30
|
+
ALTER TABLE "agents" ALTER COLUMN "display_name" SET DEFAULT '';
|
|
31
|
+
ALTER TABLE "agents" ALTER COLUMN "display_name" SET NOT NULL;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
-- Silent inbox entries for missed group-chat context.
|
|
2
|
+
--
|
|
3
|
+
-- Adds `inbox_entries.notify`, defaulting to true so existing rows continue to
|
|
4
|
+
-- behave as "active" deliverables. Group-chat fan-out now writes a row for
|
|
5
|
+
-- every non-sender participant — for `mention_only` participants who aren't
|
|
6
|
+
-- explicitly @mentioned in the triggering message we set `notify = false` so
|
|
7
|
+
-- the row is silently parked. Subsequent active deliveries to the same chat
|
|
8
|
+
-- pick up these silent rows and replay them as preceding context, then
|
|
9
|
+
-- bulk-ack them so they don't get re-replayed.
|
|
10
|
+
--
|
|
11
|
+
-- See proposals/group-chat-ux-improvements §1 (silent inbox).
|
|
12
|
+
--
|
|
13
|
+
-- ──────────────── Indexes ────────────────
|
|
14
|
+
--
|
|
15
|
+
-- idx_inbox_pending_notify — partial index used by pollInbox's claim.
|
|
16
|
+
-- The query is "WHERE inbox_id = ? AND status = 'pending' AND notify = true
|
|
17
|
+
-- ORDER BY created_at LIMIT N FOR UPDATE SKIP LOCKED". Without `notify` in
|
|
18
|
+
-- the index, a chat that accumulates silent rows (mention_only agent in a
|
|
19
|
+
-- chatty group) forces the planner to scan past them before finding the
|
|
20
|
+
-- next trigger. Partial index keeps it bounded.
|
|
21
|
+
--
|
|
22
|
+
-- idx_inbox_chat_silent — used by collectPrecedingContext to walk the
|
|
23
|
+
-- silent rows in a single (inbox, chat) bucket between two triggers.
|
|
24
|
+
--
|
|
25
|
+
-- ──────────────── Operator note ────────────────
|
|
26
|
+
--
|
|
27
|
+
-- Drizzle migrator wraps every migration file in a single transaction (see the
|
|
28
|
+
-- comment block in 0020_unified_user_token.sql), which means we can't use
|
|
29
|
+
-- `CREATE INDEX CONCURRENTLY` here — PG rejects it inside a tx. On a small
|
|
30
|
+
-- `inbox_entries` table the regular `CREATE INDEX` finishes in <1s and is
|
|
31
|
+
-- fine. For a large production table, the runbook is:
|
|
32
|
+
--
|
|
33
|
+
-- 1. Stop applying new migrations briefly.
|
|
34
|
+
-- 2. Manually run, OUTSIDE a transaction:
|
|
35
|
+
-- CREATE INDEX CONCURRENTLY idx_inbox_pending_notify
|
|
36
|
+
-- ON inbox_entries (inbox_id, created_at)
|
|
37
|
+
-- WHERE status = 'pending' AND notify = true;
|
|
38
|
+
-- CREATE INDEX CONCURRENTLY idx_inbox_chat_silent
|
|
39
|
+
-- ON inbox_entries (inbox_id, chat_id, notify, status);
|
|
40
|
+
-- 3. Re-run `pnpm db:migrate`. The `IF NOT EXISTS` clauses below detect
|
|
41
|
+
-- the pre-created indexes and skip them.
|
|
42
|
+
|
|
43
|
+
ALTER TABLE "inbox_entries"
|
|
44
|
+
ADD COLUMN "notify" boolean DEFAULT true NOT NULL;
|
|
45
|
+
|
|
46
|
+
--> statement-breakpoint
|
|
47
|
+
CREATE INDEX IF NOT EXISTS "idx_inbox_pending_notify"
|
|
48
|
+
ON "inbox_entries" ("inbox_id", "created_at")
|
|
49
|
+
WHERE status = 'pending' AND notify = true;
|
|
50
|
+
|
|
51
|
+
--> statement-breakpoint
|
|
52
|
+
CREATE INDEX IF NOT EXISTS "idx_inbox_chat_silent"
|
|
53
|
+
ON "inbox_entries" ("inbox_id", "chat_id", "notify", "status");
|
|
@@ -169,6 +169,20 @@
|
|
|
169
169
|
"when": 1777248000000,
|
|
170
170
|
"tag": "0023_clients_org_scoping",
|
|
171
171
|
"breakpoints": true
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"idx": 24,
|
|
175
|
+
"version": "7",
|
|
176
|
+
"when": 1777334400000,
|
|
177
|
+
"tag": "0024_display_name_not_null",
|
|
178
|
+
"breakpoints": true
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"idx": 25,
|
|
182
|
+
"version": "7",
|
|
183
|
+
"when": 1777420800000,
|
|
184
|
+
"tag": "0025_inbox_silent_entries",
|
|
185
|
+
"breakpoints": true
|
|
172
186
|
}
|
|
173
187
|
]
|
|
174
188
|
}
|
|
@@ -23,6 +23,21 @@ function extractMentions(content, participants) {
|
|
|
23
23
|
}
|
|
24
24
|
return [...hits];
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Return every `@<name>` token that survives the code-stripping / word-
|
|
28
|
+
* boundary gates, regardless of whether it matches a participant. The
|
|
29
|
+
* caller uses this to log unmatched tokens (typos, renamed agents) without
|
|
30
|
+
* polluting the authoritative mention list.
|
|
31
|
+
*/
|
|
32
|
+
function scanMentionTokens(content) {
|
|
33
|
+
const stripped = stripCode(content);
|
|
34
|
+
const tokens = [];
|
|
35
|
+
for (const m of stripped.matchAll(MENTION_REGEX)) {
|
|
36
|
+
const token = m[1];
|
|
37
|
+
if (token) tokens.push(token.toLowerCase());
|
|
38
|
+
}
|
|
39
|
+
return tokens;
|
|
40
|
+
}
|
|
26
41
|
const adapterPlatformSchema = z.enum([
|
|
27
42
|
"feishu",
|
|
28
43
|
"slack",
|
|
@@ -204,7 +219,7 @@ function isReservedAgentName(name) {
|
|
|
204
219
|
const createAgentSchema = z.object({
|
|
205
220
|
name: z.string().min(1).max(64).regex(AGENT_NAME_REGEX, "Must start with a letter or digit and contain only lowercase letters, digits, hyphens (-), and underscores (_). Max 64 chars.").refine((n) => !isReservedAgentName(n), { message: "That agent name is reserved — pick a different one." }).optional(),
|
|
206
221
|
type: agentTypeSchema,
|
|
207
|
-
displayName: z.string().max(200).optional(),
|
|
222
|
+
displayName: z.string().min(1).max(200).optional(),
|
|
208
223
|
delegateMention: z.string().max(100).optional(),
|
|
209
224
|
organizationId: z.string().max(100).optional(),
|
|
210
225
|
source: agentSourceSchema.optional(),
|
|
@@ -215,7 +230,7 @@ const createAgentSchema = z.object({
|
|
|
215
230
|
});
|
|
216
231
|
const updateAgentSchema = z.object({
|
|
217
232
|
type: agentTypeSchema.optional(),
|
|
218
|
-
displayName: z.string().
|
|
233
|
+
displayName: z.string().min(1).max(200).optional(),
|
|
219
234
|
delegateMention: z.string().max(100).nullable().optional(),
|
|
220
235
|
visibility: agentVisibilitySchema.optional(),
|
|
221
236
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
@@ -227,7 +242,7 @@ z.object({
|
|
|
227
242
|
name: z.string().nullable(),
|
|
228
243
|
organizationId: z.string(),
|
|
229
244
|
type: agentTypeSchema,
|
|
230
|
-
displayName: z.string()
|
|
245
|
+
displayName: z.string(),
|
|
231
246
|
delegateMention: z.string().nullable(),
|
|
232
247
|
inboxId: z.string(),
|
|
233
248
|
status: z.string(),
|
|
@@ -254,7 +269,7 @@ const agentPinnedMessageSchema = z.object({
|
|
|
254
269
|
type: z.literal("agent:pinned"),
|
|
255
270
|
agentId: z.string(),
|
|
256
271
|
name: z.string().nullable(),
|
|
257
|
-
displayName: z.string()
|
|
272
|
+
displayName: z.string(),
|
|
258
273
|
agentType: agentTypeSchema
|
|
259
274
|
});
|
|
260
275
|
/**
|
|
@@ -450,7 +465,7 @@ const chatParticipantSchema = z.object({
|
|
|
450
465
|
});
|
|
451
466
|
chatParticipantSchema.extend({
|
|
452
467
|
name: z.string().nullable(),
|
|
453
|
-
displayName: z.string()
|
|
468
|
+
displayName: z.string(),
|
|
454
469
|
type: z.string()
|
|
455
470
|
});
|
|
456
471
|
z.object({
|
|
@@ -590,6 +605,23 @@ const inReplyToSnapshotSchema = z.object({
|
|
|
590
605
|
/** Per-chat participation mode exposed to the recipient runtime. */
|
|
591
606
|
const participantModeSchema = z.enum(["full", "mention_only"]);
|
|
592
607
|
/**
|
|
608
|
+
* Lightweight snapshot of an earlier message in the same chat that the
|
|
609
|
+
* recipient missed (because it was `mention_only` + not @mentioned). Server
|
|
610
|
+
* attaches a list of these to the next active delivery in the chat so the
|
|
611
|
+
* agent's prompt carries enough context to reply meaningfully.
|
|
612
|
+
*
|
|
613
|
+
* Smaller than `messageSchema` on purpose — drops fields that don't help the
|
|
614
|
+
* LLM (replyTo envelopes, source) and aren't safe to leak across recipients.
|
|
615
|
+
*/
|
|
616
|
+
const precedingMessageSchema = z.object({
|
|
617
|
+
id: z.string(),
|
|
618
|
+
senderId: z.string(),
|
|
619
|
+
format: z.string(),
|
|
620
|
+
content: z.unknown(),
|
|
621
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
622
|
+
createdAt: z.string()
|
|
623
|
+
});
|
|
624
|
+
/**
|
|
593
625
|
* Wire format for messages routed FROM the Hub TO a client runtime.
|
|
594
626
|
*
|
|
595
627
|
* Adds `configVersion` so the client can compare against its locally cached
|
|
@@ -605,11 +637,17 @@ const participantModeSchema = z.enum(["full", "mention_only"]);
|
|
|
605
637
|
*
|
|
606
638
|
* `inReplyToSnapshot` is populated when `inReplyTo` resolves to an existing
|
|
607
639
|
* message; runtime uses it to suppress self-reply echo on direct chats.
|
|
640
|
+
*
|
|
641
|
+
* `precedingMessages` is a (possibly empty) list of older messages in the
|
|
642
|
+
* same chat that this recipient did not previously receive (silent inbox
|
|
643
|
+
* context). The runtime renders them as "earlier in chat" before the
|
|
644
|
+
* triggering message — see proposals/group-chat-ux-improvements §1.
|
|
608
645
|
*/
|
|
609
646
|
const clientMessageSchema = messageSchema.extend({
|
|
610
647
|
configVersion: z.number().int().positive(),
|
|
611
648
|
recipientMode: participantModeSchema.default("full"),
|
|
612
|
-
inReplyToSnapshot: inReplyToSnapshotSchema.default(null)
|
|
649
|
+
inReplyToSnapshot: inReplyToSnapshotSchema.default(null),
|
|
650
|
+
precedingMessages: z.array(precedingMessageSchema).default([])
|
|
613
651
|
});
|
|
614
652
|
z.enum([
|
|
615
653
|
"pending",
|
|
@@ -1072,4 +1110,4 @@ async function bindFeishuUser(serverUrl, accessToken, agentId, humanAgentId, fei
|
|
|
1072
1110
|
}
|
|
1073
1111
|
}
|
|
1074
1112
|
//#endregion
|
|
1075
|
-
export {
|
|
1113
|
+
export { sessionEventMessageSchema as $, createChatSchema as A, isReservedAgentName as B, agentRuntimeConfigPayloadSchema as C, createAdapterConfigSchema as D, connectTokenExchangeSchema as E, dryRunAgentRuntimeConfigSchema as F, paginationQuerySchema as G, loginSchema as H, extractMentions as I, scanMentionTokens as J, refreshTokenSchema as K, imageInlineContentSchema as L, createOrganizationSchema as M, createTaskSchema as N, createAdapterMappingSchema as O, delegateFeishuUserSchema as P, sessionCompletionMessageSchema as Q, inboxPollQuerySchema as R, agentPinnedMessageSchema as S, clientRegisterSchema as T, messageSourceSchema as U, linkTaskChatSchema as V, notificationQuerySchema as W, sendMessageSchema as X, selfServiceFeishuBotSchema as Y, sendToAgentSchema as Z, WS_AUTH_FRAME_TIMEOUT_MS as _, AGENT_NAME_REGEX as a, updateAgentRuntimeConfigSchema as at, adminUpdateTaskSchema as b, AGENT_STATUSES as c, updateMemberSchema as ct, DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD as d, updateTaskStatusSchema as dt, sessionEventSchema as et, SYSTEM_CONFIG_DEFAULTS as f, wsAuthFrameSchema as ft, TASK_TERMINAL_STATUSES as g, TASK_STATUSES as h, AGENT_BIND_REJECT_REASONS as i, updateAdapterConfigSchema as it, createMemberSchema as j, createAgentSchema as k, AGENT_TYPES as l, updateOrganizationSchema as lt, TASK_HEALTH_SIGNALS as m, bindFeishuUser as n, sessionStateMessageSchema as nt, AGENT_SELECTOR_HEADER as o, updateAgentSchema as ot, TASK_CREATOR_TYPES as p, runtimeStateMessageSchema as q, feishu_exports as r, taskListQuerySchema as rt, AGENT_SOURCES as s, updateChatSchema as st, bindFeishuBot as t, sessionReconcileRequestSchema as tt, AGENT_VISIBILITY as u, updateSystemConfigSchema as ut, addParticipantSchema as v, agentTypeSchema as w, agentBindRequestSchema as x, adminCreateTaskSchema as y, isRedactedEnvValue as z };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./observability-DV_fQKqV-oxfXX6Z2.mjs";
|
|
2
|
-
import { A as
|
|
2
|
+
import { A as installClientService, B as createOwner, C as checkNodeVersion, D as checkWebSocket, E as checkServerReachable, F as isDockerAvailable, I as stopPostgres, J as FirstTreeHubSDK, K as status, L as ClientRuntime, M as resolveCliInvocation, N as uninstallClientService, O as printResults, P as ensurePostgres, R as handleClientOrgMismatch, S as checkDocker, T as checkServerHealth, U as blank, V as hasUser, Y as SdkError, _ as runMigrations, b as checkClientConfig, c as promptMissingFields, d as onboardCheck, f as onboardCreate, i as startServer, j as isServiceSupported, k as getClientServiceStatus, l as formatCheckReport, m as runHomeMigration, o as isInteractive, s as promptAddAgent, v as checkAgentConfigs, w as checkServerConfig, x as checkDatabase, z as rotateClientIdWithBackup } from "./core-BgiFGT7Y.mjs";
|
|
3
3
|
import "./logger-core-BTmvdflj-DjW8FM4T.mjs";
|
|
4
4
|
import { a as resolveAccessToken, n as ensureFreshAccessToken, o as resolveServerUrl, r as ensureFreshAdminToken } from "./bootstrap-CtVqQA8a.mjs";
|
|
5
|
-
import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-
|
|
5
|
+
import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-DEmwoNn_.mjs";
|
|
6
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 };
|