@agent-team-foundation/first-tree-hub 0.6.1 → 0.6.2

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.
@@ -0,0 +1,30 @@
1
+ -- M1 agent configuration: Hub-managed runtime config table.
2
+ -- Creates `agent_configs` and back-fills one row per non-deleted agent so
3
+ -- Step 6 can flip Claude Code handler over to read prompt from config without
4
+ -- silently dropping the existing `agents.profile` text.
5
+
6
+ CREATE TABLE IF NOT EXISTS "agent_configs" (
7
+ "agent_id" text PRIMARY KEY NOT NULL,
8
+ "version" integer NOT NULL DEFAULT 1,
9
+ "payload" jsonb NOT NULL,
10
+ "updated_by" text NOT NULL,
11
+ "updated_at" timestamp with time zone NOT NULL DEFAULT now()
12
+ );
13
+
14
+ --> statement-breakpoint
15
+ INSERT INTO "agent_configs" ("agent_id", "version", "payload", "updated_by", "updated_at")
16
+ SELECT
17
+ "uuid",
18
+ 1,
19
+ jsonb_build_object(
20
+ 'prompt', jsonb_build_object('append', COALESCE("profile", '')),
21
+ 'model', '',
22
+ 'mcpServers', '[]'::jsonb,
23
+ 'env', '[]'::jsonb,
24
+ 'gitRepos', '[]'::jsonb
25
+ ),
26
+ 'system',
27
+ now()
28
+ FROM "agents"
29
+ WHERE "status" != 'deleted'
30
+ ON CONFLICT ("agent_id") DO NOTHING;
@@ -0,0 +1,148 @@
1
+ -- Unified user token milestone — retire agent tokens, authenticate every
2
+ -- caller as a user.
3
+ --
4
+ -- This migration collapses the two-track auth (member JWT + `aghub_*` agent
5
+ -- token) into a single member-JWT credential. Three structural changes:
6
+ -- 1. Drop `agent_tokens` (table + FK cascade).
7
+ -- 2. Add `clients.user_id` (nullable) — owning user of the physical client.
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` and is
10
+ -- required for every non-human agent (enforced in the service layer per
11
+ -- CLAUDE.md "integrity in service layer"; no DB CHECK/trigger).
12
+ -- 4. Make `agents.manager_id` NOT NULL after backfilling the first admin
13
+ -- member of each org onto the unmanaged rows.
14
+ --
15
+ -- There is no compatibility layer: operators stop SDK/CLI processes, run
16
+ -- `db:migrate`, then re-login via `first-tree-hub connect`. See the proposal
17
+ -- "unified-user-token.20260417" for the full upgrade runbook.
18
+
19
+ BEGIN;
20
+
21
+ -- ---------------------------------------------------------------------------
22
+ -- 1. Drop agent_tokens (FK cascade handles row removal).
23
+ -- ---------------------------------------------------------------------------
24
+ DROP TABLE IF EXISTS "agent_tokens";
25
+
26
+ -- ---------------------------------------------------------------------------
27
+ -- 2. clients.user_id — nullable FK to users(id).
28
+ -- Nullable so legacy rows (created before handshake auth) keep existing;
29
+ -- the WS handshake claims them on first re-register under a JWT.
30
+ -- ---------------------------------------------------------------------------
31
+ ALTER TABLE "clients"
32
+ ADD COLUMN IF NOT EXISTS "user_id" text REFERENCES "users"("id") ON DELETE SET NULL;
33
+
34
+ CREATE INDEX IF NOT EXISTS "idx_clients_user" ON "clients" ("user_id");
35
+
36
+ -- ---------------------------------------------------------------------------
37
+ -- 3. agents.client_id — pin an agent to the physical client that runs it.
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.
44
+ -- ---------------------------------------------------------------------------
45
+ ALTER TABLE "agents"
46
+ ADD COLUMN IF NOT EXISTS "client_id" text REFERENCES "clients"("id") ON DELETE RESTRICT;
47
+
48
+ -- 3a. Copy last-bound client from agent_presence.
49
+ UPDATE "agents" a
50
+ SET "client_id" = ap."client_id"
51
+ FROM "agent_presence" ap
52
+ WHERE ap."agent_id" = a."uuid"
53
+ AND ap."client_id" IS NOT NULL
54
+ AND a."client_id" IS NULL;
55
+
56
+ -- 3b. Fail loudly if any non-deleted non-human agent is missing a client_id
57
+ -- after 3a. These rows would pass migration but surface at runtime as
58
+ -- `WRONG_CLIENT` / `assertClientOwner` 404 — which looks like "installed
59
+ -- but broken" to the operator. Mirrors 4c's orphan-count pattern. Admins
60
+ -- must either soft-delete the orphans or re-register their client via
61
+ -- `first-tree-hub connect` and manually UPDATE before retrying.
62
+ DO $$
63
+ DECLARE
64
+ unpinned_count integer;
65
+ BEGIN
66
+ SELECT COUNT(*) INTO unpinned_count
67
+ FROM "agents"
68
+ WHERE "client_id" IS NULL
69
+ AND "type" <> 'human'
70
+ AND "status" <> 'deleted';
71
+
72
+ IF unpinned_count > 0 THEN
73
+ RAISE EXCEPTION
74
+ 'unified-user-token migration: % non-human agents have no client_id after backfill; '
75
+ 're-register their client via `first-tree-hub connect` (which will update agent_presence) '
76
+ 'or soft-delete the orphan agents, then retry',
77
+ unpinned_count;
78
+ END IF;
79
+ END $$;
80
+
81
+ CREATE INDEX IF NOT EXISTS "idx_agents_client" ON "agents" ("client_id");
82
+
83
+ -- ---------------------------------------------------------------------------
84
+ -- 4. agents.manager_id — backfill then enforce NOT NULL.
85
+ -- Human agents own their members row, so self-assign via members.
86
+ -- Non-human agents get the first admin member in their org.
87
+ -- ---------------------------------------------------------------------------
88
+
89
+ -- 4a. Human agents: self-assign to their own member row.
90
+ UPDATE "agents" a
91
+ SET "manager_id" = m."id"
92
+ FROM "members" m
93
+ WHERE m."agent_id" = a."uuid"
94
+ AND a."manager_id" IS NULL
95
+ AND a."type" = 'human';
96
+
97
+ -- 4b. Non-human agents: first admin in org, ordered by created_at asc.
98
+ UPDATE "agents" a
99
+ SET "manager_id" = m."id"
100
+ FROM (
101
+ SELECT DISTINCT ON (m."organization_id")
102
+ m."id" AS id,
103
+ m."organization_id" AS organization_id
104
+ FROM "members" m
105
+ WHERE m."role" = 'admin'
106
+ ORDER BY m."organization_id", m."created_at" ASC
107
+ ) m
108
+ WHERE a."organization_id" = m."organization_id"
109
+ AND a."manager_id" IS NULL;
110
+
111
+ -- 4c. Fail loudly if any agent still lacks a manager. Admin must intervene
112
+ -- before the migration can complete.
113
+ DO $$
114
+ DECLARE
115
+ orphan_count integer;
116
+ BEGIN
117
+ SELECT COUNT(*) INTO orphan_count
118
+ FROM "agents"
119
+ WHERE "manager_id" IS NULL
120
+ AND "status" <> 'deleted';
121
+
122
+ IF orphan_count > 0 THEN
123
+ RAISE EXCEPTION
124
+ 'unified-user-token migration: % non-deleted agents have no manager_id after backfill; '
125
+ 'create at least one admin member per org, or set agents.manager_id manually, then retry',
126
+ orphan_count;
127
+ END IF;
128
+ END $$;
129
+
130
+ ALTER TABLE "agents"
131
+ ALTER COLUMN "manager_id" SET NOT NULL;
132
+
133
+ -- 4d. Recreate the manager_id FK as DEFERRABLE INITIALLY DEFERRED so the
134
+ -- bootstrap path for a new human agent + member row can run inside a
135
+ -- single transaction. The two rows reference each other (agents.manager_id
136
+ -- → members.id, members.agent_id → agents.uuid); without deferred FKs the
137
+ -- first INSERT always fails the sibling constraint. Manager reassignment
138
+ -- (member.ts::deleteMember) also benefits: we can move every agent in one
139
+ -- pass without fighting constraint order.
140
+ ALTER TABLE "agents"
141
+ DROP CONSTRAINT IF EXISTS "agents_manager_id_fkey";
142
+ ALTER TABLE "agents"
143
+ ADD CONSTRAINT "agents_manager_id_fkey"
144
+ FOREIGN KEY ("manager_id") REFERENCES "members"("id")
145
+ ON DELETE SET NULL
146
+ DEFERRABLE INITIALLY DEFERRED;
147
+
148
+ COMMIT;
@@ -0,0 +1,10 @@
1
+ -- PRD D7: remove `agents.profile` column and all dependent code paths.
2
+ -- The column was backfilled into `agent_configs.payload.prompt.append` by
3
+ -- migration 0018. After this migration, agent behavior instructions live
4
+ -- exclusively in `agent_configs`; the `profile` column is dead weight.
5
+ --
6
+ -- Callers that used to read/write `profile` have been updated in the same
7
+ -- release; operators must deploy this migration together with the matching
8
+ -- code change — no compatibility layer.
9
+
10
+ ALTER TABLE "agents" DROP COLUMN IF EXISTS "profile";
@@ -134,6 +134,27 @@
134
134
  "when": 1776816000000,
135
135
  "tag": "0018_agent_visibility",
136
136
  "breakpoints": true
137
+ },
138
+ {
139
+ "idx": 19,
140
+ "version": "7",
141
+ "when": 1776902400000,
142
+ "tag": "0019_agent_configs",
143
+ "breakpoints": true
144
+ },
145
+ {
146
+ "idx": 20,
147
+ "version": "7",
148
+ "when": 1776988800000,
149
+ "tag": "0020_unified_user_token",
150
+ "breakpoints": true
151
+ },
152
+ {
153
+ "idx": 21,
154
+ "version": "7",
155
+ "when": 1777075200000,
156
+ "tag": "0021_drop_agents_profile",
157
+ "breakpoints": true
137
158
  }
138
159
  ]
139
160
  }