@agent-team-foundation/first-tree-hub 0.6.2 → 0.6.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/{bootstrap-DW7aIpmE.mjs → bootstrap-DNL1cEwv.mjs} +10 -5
- package/dist/cli/index.mjs +98 -103
- package/dist/{core-RXUUKkCO.mjs → core-B10jgThe.mjs} +62 -26
- package/dist/drizzle/0020_unified_user_token.sql +50 -44
- package/dist/{feishu-BZ8pnMrQ.mjs → feishu-BoMJHlOv.mjs} +1 -1
- package/dist/index.mjs +3 -3
- package/dist/web/assets/{index-BMOr9-X2.js → index-CnLpaSBg.js} +2 -2
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
|
@@ -6,59 +6,63 @@
|
|
|
6
6
|
-- 1. Drop `agent_tokens` (table + FK cascade).
|
|
7
7
|
-- 2. Add `clients.user_id` (nullable) — owning user of the physical client.
|
|
8
8
|
-- 3. Add `agents.client_id` (nullable FK) — pin an agent to the client that
|
|
9
|
-
-- runs it. `client_id` is backfilled from `agent_presence
|
|
10
|
-
--
|
|
11
|
-
--
|
|
9
|
+
-- runs it. `client_id` is backfilled from `agent_presence`. Rows that
|
|
10
|
+
-- cannot be backfilled stay NULL and bind on first WS connect (see
|
|
11
|
+
-- `api/agent/ws-client.ts` first-bind path). Originally this migration
|
|
12
|
+
-- raised an exception when any non-human agent was unbacked — that
|
|
13
|
+
-- gated startup on a data state the operator usually can't fix until
|
|
14
|
+
-- the server is up. Relaxed to NOTICE so the loop is broken; runtime
|
|
15
|
+
-- enforcement (Rule R-RUN in `services/agent.ts` + `agent-selector.ts`)
|
|
16
|
+
-- still rejects unclaimed agents on the request path.
|
|
12
17
|
-- 4. Make `agents.manager_id` NOT NULL after backfilling the first admin
|
|
13
18
|
-- member of each org onto the unmanaged rows.
|
|
14
19
|
--
|
|
15
20
|
-- There is no compatibility layer: operators stop SDK/CLI processes, run
|
|
16
21
|
-- `db:migrate`, then re-login via `first-tree-hub connect`. See the proposal
|
|
17
22
|
-- "unified-user-token.20260417" for the full upgrade runbook.
|
|
23
|
+
--
|
|
24
|
+
-- NOTE: Do NOT wrap this file in BEGIN;/COMMIT;. The Drizzle migrator already
|
|
25
|
+
-- runs every pending migration inside a single outer transaction, so a nested
|
|
26
|
+
-- BEGIN raises WARNING 25001 and the inner COMMIT prematurely closes the
|
|
27
|
+
-- outer transaction — which prevents the migration hash from being recorded
|
|
28
|
+
-- and causes the server to loop through the same failure on every restart.
|
|
18
29
|
|
|
19
|
-
BEGIN;
|
|
20
|
-
|
|
21
|
-
-- ---------------------------------------------------------------------------
|
|
22
|
-
-- 1. Drop agent_tokens (FK cascade handles row removal).
|
|
23
|
-
-- ---------------------------------------------------------------------------
|
|
24
30
|
DROP TABLE IF EXISTS "agent_tokens";
|
|
31
|
+
--> statement-breakpoint
|
|
25
32
|
|
|
26
33
|
-- ---------------------------------------------------------------------------
|
|
27
|
-
--
|
|
28
|
-
--
|
|
29
|
-
--
|
|
34
|
+
-- clients.user_id — nullable FK to users(id).
|
|
35
|
+
-- Nullable so legacy rows (created before handshake auth) keep existing;
|
|
36
|
+
-- the WS handshake claims them on first re-register under a JWT.
|
|
30
37
|
-- ---------------------------------------------------------------------------
|
|
31
38
|
ALTER TABLE "clients"
|
|
32
39
|
ADD COLUMN IF NOT EXISTS "user_id" text REFERENCES "users"("id") ON DELETE SET NULL;
|
|
40
|
+
--> statement-breakpoint
|
|
33
41
|
|
|
34
42
|
CREATE INDEX IF NOT EXISTS "idx_clients_user" ON "clients" ("user_id");
|
|
43
|
+
--> statement-breakpoint
|
|
35
44
|
|
|
36
45
|
-- ---------------------------------------------------------------------------
|
|
37
|
-
--
|
|
38
|
-
-- Backfill in two steps:
|
|
39
|
-
-- a. Copy the most recent bind from agent_presence, if any.
|
|
40
|
-
-- b. For non-human agents with no bind history, attempt to pick the
|
|
41
|
-
-- first client in the agent's org. If none exists we intentionally
|
|
42
|
-
-- leave the row NULL; the service layer refuses runtime bind while
|
|
43
|
-
-- `client_id IS NULL` so operators notice and fix it.
|
|
46
|
+
-- agents.client_id — pin an agent to the physical client that runs it.
|
|
44
47
|
-- ---------------------------------------------------------------------------
|
|
45
48
|
ALTER TABLE "agents"
|
|
46
49
|
ADD COLUMN IF NOT EXISTS "client_id" text REFERENCES "clients"("id") ON DELETE RESTRICT;
|
|
50
|
+
--> statement-breakpoint
|
|
47
51
|
|
|
48
|
-
--
|
|
52
|
+
-- Copy last-bound client from agent_presence.
|
|
49
53
|
UPDATE "agents" a
|
|
50
54
|
SET "client_id" = ap."client_id"
|
|
51
55
|
FROM "agent_presence" ap
|
|
52
56
|
WHERE ap."agent_id" = a."uuid"
|
|
53
57
|
AND ap."client_id" IS NOT NULL
|
|
54
58
|
AND a."client_id" IS NULL;
|
|
59
|
+
--> statement-breakpoint
|
|
55
60
|
|
|
56
|
-
--
|
|
57
|
-
--
|
|
58
|
-
--
|
|
59
|
-
--
|
|
60
|
-
--
|
|
61
|
-
-- `first-tree-hub connect` and manually UPDATE before retrying.
|
|
61
|
+
-- Surface (do not block) any non-deleted non-human agent still missing a
|
|
62
|
+
-- client_id after the backfill. They will sit in an "unclaimed" state until
|
|
63
|
+
-- a client connects via WS and the first-bind path in
|
|
64
|
+
-- `api/agent/ws-client.ts` claims them. Runtime guards reject HTTP / WS
|
|
65
|
+
-- requests for those agents in the meantime.
|
|
62
66
|
DO $$
|
|
63
67
|
DECLARE
|
|
64
68
|
unpinned_count integer;
|
|
@@ -70,31 +74,33 @@ BEGIN
|
|
|
70
74
|
AND "status" <> 'deleted';
|
|
71
75
|
|
|
72
76
|
IF unpinned_count > 0 THEN
|
|
73
|
-
RAISE
|
|
77
|
+
RAISE NOTICE
|
|
74
78
|
'unified-user-token migration: % non-human agents have no client_id after backfill; '
|
|
75
|
-
'
|
|
76
|
-
'or soft-delete the orphan agents, then retry',
|
|
79
|
+
'they will be claimed on first WS bind (see ws-client.ts first-bind path)',
|
|
77
80
|
unpinned_count;
|
|
78
81
|
END IF;
|
|
79
82
|
END $$;
|
|
83
|
+
--> statement-breakpoint
|
|
80
84
|
|
|
81
85
|
CREATE INDEX IF NOT EXISTS "idx_agents_client" ON "agents" ("client_id");
|
|
86
|
+
--> statement-breakpoint
|
|
82
87
|
|
|
83
88
|
-- ---------------------------------------------------------------------------
|
|
84
|
-
--
|
|
85
|
-
--
|
|
86
|
-
--
|
|
89
|
+
-- agents.manager_id — backfill then enforce NOT NULL.
|
|
90
|
+
-- Human agents own their members row, so self-assign via members.
|
|
91
|
+
-- Non-human agents get the first admin member in their org.
|
|
87
92
|
-- ---------------------------------------------------------------------------
|
|
88
93
|
|
|
89
|
-
--
|
|
94
|
+
-- Human agents: self-assign to their own member row.
|
|
90
95
|
UPDATE "agents" a
|
|
91
96
|
SET "manager_id" = m."id"
|
|
92
97
|
FROM "members" m
|
|
93
98
|
WHERE m."agent_id" = a."uuid"
|
|
94
99
|
AND a."manager_id" IS NULL
|
|
95
100
|
AND a."type" = 'human';
|
|
101
|
+
--> statement-breakpoint
|
|
96
102
|
|
|
97
|
-
--
|
|
103
|
+
-- Non-human agents: first admin in org, ordered by created_at asc.
|
|
98
104
|
UPDATE "agents" a
|
|
99
105
|
SET "manager_id" = m."id"
|
|
100
106
|
FROM (
|
|
@@ -107,9 +113,9 @@ FROM (
|
|
|
107
113
|
) m
|
|
108
114
|
WHERE a."organization_id" = m."organization_id"
|
|
109
115
|
AND a."manager_id" IS NULL;
|
|
116
|
+
--> statement-breakpoint
|
|
110
117
|
|
|
111
|
-
--
|
|
112
|
-
-- before the migration can complete.
|
|
118
|
+
-- Fail loudly if any agent still lacks a manager.
|
|
113
119
|
DO $$
|
|
114
120
|
DECLARE
|
|
115
121
|
orphan_count integer;
|
|
@@ -126,23 +132,23 @@ BEGIN
|
|
|
126
132
|
orphan_count;
|
|
127
133
|
END IF;
|
|
128
134
|
END $$;
|
|
135
|
+
--> statement-breakpoint
|
|
129
136
|
|
|
130
137
|
ALTER TABLE "agents"
|
|
131
138
|
ALTER COLUMN "manager_id" SET NOT NULL;
|
|
139
|
+
--> statement-breakpoint
|
|
132
140
|
|
|
133
|
-
--
|
|
134
|
-
--
|
|
135
|
-
--
|
|
136
|
-
--
|
|
137
|
-
--
|
|
138
|
-
-- (member.ts::deleteMember) also benefits: we can move every agent in one
|
|
139
|
-
-- pass without fighting constraint order.
|
|
141
|
+
-- Recreate the manager_id FK as DEFERRABLE INITIALLY DEFERRED so the
|
|
142
|
+
-- bootstrap path for a new human agent + member row can run inside a
|
|
143
|
+
-- single transaction. The two rows reference each other (agents.manager_id
|
|
144
|
+
-- → members.id, members.agent_id → agents.uuid); without deferred FKs the
|
|
145
|
+
-- first INSERT always fails the sibling constraint.
|
|
140
146
|
ALTER TABLE "agents"
|
|
141
147
|
DROP CONSTRAINT IF EXISTS "agents_manager_id_fkey";
|
|
148
|
+
--> statement-breakpoint
|
|
149
|
+
|
|
142
150
|
ALTER TABLE "agents"
|
|
143
151
|
ADD CONSTRAINT "agents_manager_id_fkey"
|
|
144
152
|
FOREIGN KEY ("manager_id") REFERENCES "members"("id")
|
|
145
153
|
ON DELETE SET NULL
|
|
146
154
|
DEFERRABLE INITIALLY DEFERRED;
|
|
147
|
-
|
|
148
|
-
COMMIT;
|
|
@@ -173,7 +173,7 @@ const updateAgentSchema = z.object({
|
|
|
173
173
|
visibility: agentVisibilitySchema.optional(),
|
|
174
174
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
175
175
|
managerId: z.string().nullable().optional(),
|
|
176
|
-
clientId: z.string().optional()
|
|
176
|
+
clientId: z.string().min(1).max(100).nullable().optional()
|
|
177
177
|
});
|
|
178
178
|
z.object({
|
|
179
179
|
uuid: z.string(),
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as resolveAccessToken, n as ensureFreshAccessToken, o as resolveServerUrl, r as ensureFreshAdminToken } from "./bootstrap-
|
|
2
|
-
import { A as SdkError, C as ensurePostgres, D as createOwner, E as ClientRuntime, O as hasUser, S as status, T as stopPostgres, _ as checkServerHealth, a as formatCheckReport, b as printResults, c as onboardCreate, d as checkAgentConfigs, f as checkClientConfig, g as checkServerConfig, h as checkNodeVersion, i as promptMissingFields, k as FirstTreeHubSDK, m as checkDocker, n as isInteractive, p as checkDatabase, r as promptAddAgent, s as onboardCheck, t as startServer, u as runMigrations, v as checkServerReachable, w as isDockerAvailable, x as blank, y as checkWebSocket } from "./core-
|
|
3
|
-
import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-
|
|
1
|
+
import { a as resolveAccessToken, n as ensureFreshAccessToken, o as resolveServerUrl, r as ensureFreshAdminToken } from "./bootstrap-DNL1cEwv.mjs";
|
|
2
|
+
import { A as SdkError, C as ensurePostgres, D as createOwner, E as ClientRuntime, O as hasUser, S as status, T as stopPostgres, _ as checkServerHealth, a as formatCheckReport, b as printResults, c as onboardCreate, d as checkAgentConfigs, f as checkClientConfig, g as checkServerConfig, h as checkNodeVersion, i as promptMissingFields, k as FirstTreeHubSDK, m as checkDocker, n as isInteractive, p as checkDatabase, r as promptAddAgent, s as onboardCheck, t as startServer, u as runMigrations, v as checkServerReachable, w as isDockerAvailable, x as blank, y as checkWebSocket } from "./core-B10jgThe.mjs";
|
|
3
|
+
import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-BoMJHlOv.mjs";
|
|
4
4
|
export { ClientRuntime, FirstTreeHubSDK, SdkError, bindFeishuBot, bindFeishuUser, blank, checkAgentConfigs, checkClientConfig, checkDatabase, checkDocker, checkNodeVersion, checkServerConfig, checkServerHealth, checkServerReachable, checkWebSocket, createOwner, ensureFreshAccessToken, ensureFreshAdminToken, ensurePostgres, formatCheckReport, hasUser, isDockerAvailable, isInteractive, onboardCheck, onboardCreate, printResults, promptAddAgent, promptMissingFields, resolveAccessToken, resolveServerUrl, runMigrations, startServer, status, stopPostgres };
|