@agent-team-foundation/first-tree-hub 0.10.15 → 0.11.1

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.
Files changed (44) hide show
  1. package/dist/{bootstrap-DUeYbwm-.mjs → bootstrap-TJRy0B9m.mjs} +10 -2
  2. package/dist/chunk-BSw8zbkd.mjs +37 -0
  3. package/dist/cli/index.mjs +94 -35
  4. package/dist/client-BCaK653p-CZjDNcdM.mjs +516 -0
  5. package/dist/client-m1OM4Iag-HKWgB3Yk.mjs +4 -0
  6. package/dist/dist-BLY7Bu-l.mjs +430 -0
  7. package/dist/{dist-D6AOiyNg.mjs → dist-BkvrONSQ.mjs} +67 -23
  8. package/dist/drizzle/0030_chat_first_workspace.sql +129 -0
  9. package/dist/drizzle/0031_drop_system_configs.sql +11 -0
  10. package/dist/drizzle/meta/_journal.json +14 -0
  11. package/dist/errors-BmyRwN0Y-CIZZ_sDc.mjs +92 -0
  12. package/dist/{esm-CYu4tXXn.mjs → esm-iadMkGbV.mjs} +2 -37
  13. package/dist/execAsync-CCyouKZM.mjs +10 -0
  14. package/dist/{execAsync-XMc-nFn-.mjs → execAsync-pImxPKN5.mjs} +1 -1
  15. package/dist/{feishu-DQ1l18Ah.mjs → feishu-AEMHwT6L.mjs} +2 -2
  16. package/dist/from-CaD373S1.mjs +3840 -0
  17. package/dist/{getMachineId-bsd-D0w3uAZa.mjs → getMachineId-bsd-DR4-Dysy.mjs} +3 -2
  18. package/dist/getMachineId-bsd-DjLgZlll.mjs +27 -0
  19. package/dist/getMachineId-darwin-B6WCAhc4.mjs +24 -0
  20. package/dist/{getMachineId-darwin-DOoYFb2_.mjs → getMachineId-darwin-CaD2juTg.mjs} +3 -2
  21. package/dist/getMachineId-linux-BeWHG1gK.mjs +20 -0
  22. package/dist/{getMachineId-linux-MlY63Zsw.mjs → getMachineId-linux-Dk3gWdQK.mjs} +2 -1
  23. package/dist/getMachineId-unsupported-BMJQItvF.mjs +15 -0
  24. package/dist/{getMachineId-unsupported-BS652RIy.mjs → getMachineId-unsupported-Bgz_Je1J.mjs} +2 -1
  25. package/dist/getMachineId-win-CdgcrzCW.mjs +26 -0
  26. package/dist/{getMachineId-win-B6hY8edq.mjs → getMachineId-win-vJ6VfDRI.mjs} +3 -2
  27. package/dist/index.mjs +9 -6
  28. package/dist/invitation-DWlyNb8x-DZTW9I26.mjs +4 -0
  29. package/dist/{invitation-B1pjAyOz-BaCA9PII.mjs → invitation-Dnn5gGGX-Ce7zbZpn.mjs} +4 -90
  30. package/dist/multipart-parser-BIksYTkk.mjs +294 -0
  31. package/dist/observability-C3nY6Jcz-Dpsi3eFk.mjs +96006 -0
  32. package/dist/observability-Co8OO0og.mjs +5 -0
  33. package/dist/{saas-connect-CebXWFF-.mjs → saas-connect-Bd0g0v_b.mjs} +6543 -6043
  34. package/dist/src-DNBS5Yjj.mjs +735 -0
  35. package/dist/src-uVZSbShB.mjs +1176 -0
  36. package/dist/web/assets/{index-CKoTjI0J.css → index-7RvlJjJ9.css} +1 -1
  37. package/dist/web/assets/{index-BQda2sqe.js → index-Dbwa40_B.js} +1 -1
  38. package/dist/web/assets/index-cpdSFHAJ.js +383 -0
  39. package/dist/web/index.html +2 -2
  40. package/package.json +1 -1
  41. package/dist/invitation-CBnQyB7o-Bulf3Sl7.mjs +0 -3
  42. package/dist/observability-C08jUFsJ.mjs +0 -4
  43. package/dist/observability-DPyf745N-BSc8QNcR.mjs +0 -33897
  44. package/dist/web/assets/index-C7yW7sWI.js +0 -388
@@ -0,0 +1,129 @@
1
+ -- Chat-first workspace foundation. See docs/chat-first-workspace-product-design.md
2
+ -- for the contract this migration implements.
3
+ --
4
+ -- Three structural changes + one data backfill:
5
+ -- 1. chats: add last_message_at + last_message_preview projection columns
6
+ -- and (organization_id, last_message_at DESC) index. Powers GET /me/chats
7
+ -- cursor pagination + sort.
8
+ -- 2. chat_participants: add last_read_at + unread_mention_count columns.
9
+ -- The chat-first workspace per-user read cursor and red-dot counter
10
+ -- live with the participation row that owns them; no separate read-state
11
+ -- table.
12
+ -- 3. chat_subscriptions (NEW): non-speaking observers ("watchers"). Stays
13
+ -- strictly disjoint from chat_participants — invariant 1 in the design.
14
+ -- ON DELETE CASCADE so dropping a chat tears down its watchers too.
15
+ -- 4. Backfill (single statement each):
16
+ -- - chats projection from messages, using DISTINCT ON to avoid the
17
+ -- per-row correlated subquery that would lock messages for minutes
18
+ -- on large tables.
19
+ -- - chat_subscriptions for every active manager whose managed non-human
20
+ -- agent already participates in a chat the manager themselves does
21
+ -- not speak in. Exactly the rows recomputeChatWatchers would create
22
+ -- on first run, but in one bulk INSERT.
23
+ --
24
+ -- chat_participants.last_read_at + unread_mention_count default to NULL/0,
25
+ -- which is the desired "treat all existing chats as already read" behavior
26
+ -- on the workspace upgrade.
27
+
28
+ ALTER TABLE "chats"
29
+ ADD COLUMN IF NOT EXISTS "last_message_at" timestamp with time zone,
30
+ ADD COLUMN IF NOT EXISTS "last_message_preview" text;
31
+
32
+ --> statement-breakpoint
33
+ CREATE INDEX IF NOT EXISTS "idx_chats_org_last_message"
34
+ ON "chats" ("organization_id", "last_message_at" DESC);
35
+
36
+ --> statement-breakpoint
37
+ ALTER TABLE "chat_participants"
38
+ ADD COLUMN IF NOT EXISTS "last_read_at" timestamp with time zone,
39
+ ADD COLUMN IF NOT EXISTS "unread_mention_count" integer NOT NULL DEFAULT 0;
40
+
41
+ --> statement-breakpoint
42
+ CREATE TABLE IF NOT EXISTS "chat_subscriptions" (
43
+ "chat_id" text NOT NULL,
44
+ "agent_id" text NOT NULL,
45
+ "kind" text NOT NULL DEFAULT 'watching',
46
+ "last_read_at" timestamp with time zone,
47
+ "unread_mention_count" integer NOT NULL DEFAULT 0,
48
+ "created_at" timestamp with time zone NOT NULL DEFAULT now(),
49
+ CONSTRAINT "chat_subscriptions_chat_id_fkey"
50
+ FOREIGN KEY ("chat_id") REFERENCES "chats"("id") ON DELETE CASCADE,
51
+ -- Intentionally NO ON DELETE clause on the agent FK. `services/agent.ts:
52
+ -- deleteAgent` is soft-only (UPDATE status='deleted', name=NULL — never
53
+ -- DELETE), so the row stays. Adding CASCADE would be dead code today and
54
+ -- silently legitimise a future hard-delete path; default RESTRICT instead
55
+ -- pins the soft-delete convention at the schema layer — any future caller
56
+ -- that tries a hard DELETE FROM agents will be forced to clean up
57
+ -- subscriptions explicitly. (asymmetry with chat_id FK is intentional —
58
+ -- chats can be hard-deleted by admin, agents cannot.)
59
+ CONSTRAINT "chat_subscriptions_agent_id_fkey"
60
+ FOREIGN KEY ("agent_id") REFERENCES "agents"("uuid"),
61
+ CONSTRAINT "chat_subscriptions_pkey"
62
+ PRIMARY KEY ("chat_id", "agent_id")
63
+ );
64
+
65
+ --> statement-breakpoint
66
+ CREATE INDEX IF NOT EXISTS "idx_chat_subscriptions_agent"
67
+ ON "chat_subscriptions" ("agent_id");
68
+
69
+ --> statement-breakpoint
70
+ -- Backfill projection: one INSERT-shaped UPDATE driven by a DISTINCT ON
71
+ -- subquery so messages is touched once. Avoids the correlated subquery
72
+ -- variant that runs two scans per chat row.
73
+ --
74
+ -- Preview must match `chat-projection.ts:applyAfterFanOut`'s live-write
75
+ -- semantics: a clean unquoted string for text messages, NULL for
76
+ -- structured content (file / image / etc.). `content::text` would
77
+ -- serialize JSONB to wire form (with quotes for strings, `{...}` for
78
+ -- objects), producing visible inconsistency between backfilled and
79
+ -- live-written rows. `jsonb_typeof` + `#>> '{}'` extracts the bare
80
+ -- string only when content is a JSON string; otherwise NULL (B3 in
81
+ -- PR review). `trim()` mirrors `outboundContent.trim()` in
82
+ -- `chat-projection.ts:applyAfterFanOut` so leading whitespace doesn't
83
+ -- visually jump on the first live overwrite.
84
+ WITH last_msg AS (
85
+ SELECT DISTINCT ON ("chat_id")
86
+ "chat_id",
87
+ "created_at",
88
+ CASE
89
+ WHEN jsonb_typeof("content") = 'string'
90
+ THEN LEFT(trim("content" #>> '{}'), 200)
91
+ ELSE NULL
92
+ END AS "preview"
93
+ FROM "messages"
94
+ ORDER BY "chat_id", "created_at" DESC
95
+ )
96
+ UPDATE "chats" c
97
+ SET "last_message_at" = lm."created_at",
98
+ "last_message_preview" = lm."preview"
99
+ FROM last_msg lm
100
+ WHERE c."id" = lm."chat_id";
101
+
102
+ --> statement-breakpoint
103
+ -- Watcher backfill: every active member whose managed (non-human) agent
104
+ -- participates in a chat where the member's own human agent is NOT a
105
+ -- speaking participant. Idempotent via ON CONFLICT.
106
+ --
107
+ -- The explicit NULL casts are required: PostgreSQL infers a bare NULL in a
108
+ -- VALUES/SELECT list as `text`, which then fails to coerce to the target
109
+ -- columns (timestamptz / integer respectively).
110
+ INSERT INTO "chat_subscriptions"
111
+ ("chat_id", "agent_id", "kind", "last_read_at", "unread_mention_count", "created_at")
112
+ SELECT DISTINCT
113
+ cp."chat_id",
114
+ m."agent_id",
115
+ 'watching',
116
+ NULL::timestamp with time zone,
117
+ 0,
118
+ now()
119
+ FROM "chat_participants" cp
120
+ JOIN "agents" a ON a."uuid" = cp."agent_id"
121
+ JOIN "members" m ON m."id" = a."manager_id"
122
+ WHERE m."status" = 'active'
123
+ AND a."type" <> 'human'
124
+ AND NOT EXISTS (
125
+ SELECT 1 FROM "chat_participants" cp2
126
+ WHERE cp2."chat_id" = cp."chat_id"
127
+ AND cp2."agent_id" = m."agent_id"
128
+ )
129
+ ON CONFLICT ("chat_id", "agent_id") DO NOTHING;
@@ -0,0 +1,11 @@
1
+ -- Drop the `system_configs` table — replaced by deployment-level env
2
+ -- vars (FIRST_TREE_HUB_INBOX_TIMEOUT_SECONDS, FIRST_TREE_HUB_MAX_RETRY_COUNT,
3
+ -- FIRST_TREE_HUB_POLLING_INTERVAL_SECONDS, FIRST_TREE_HUB_PRESENCE_CLEANUP_SECONDS,
4
+ -- FIRST_TREE_HUB_NOTIFICATION_WEBHOOK_URL).
5
+ --
6
+ -- See proposals/hub-strip-jwt-ambient-scope.20260508.md §3.5 + §6.3.
7
+ -- The table held tunables that were never customer-configurable; promoting
8
+ -- them to env vars closes the multi-tenant security gap where any org admin
9
+ -- could mutate cross-org runtime behavior.
10
+
11
+ DROP TABLE IF EXISTS "system_configs";
@@ -211,6 +211,20 @@
211
211
  "when": 1777766400000,
212
212
  "tag": "0029_direct_agent_only_mention_only",
213
213
  "breakpoints": true
214
+ },
215
+ {
216
+ "idx": 30,
217
+ "version": "7",
218
+ "when": 1777852800000,
219
+ "tag": "0030_chat_first_workspace",
220
+ "breakpoints": true
221
+ },
222
+ {
223
+ "idx": 31,
224
+ "version": "7",
225
+ "when": 1777939200000,
226
+ "tag": "0031_drop_system_configs",
227
+ "breakpoints": true
214
228
  }
215
229
  ]
216
230
  }
@@ -0,0 +1,92 @@
1
+ import { integer, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
2
+ //#region ../server/dist/errors-BmyRwN0Y.mjs
3
+ /** Organization entity. Agents and chats belong to exactly one organization. */
4
+ const organizations = pgTable("organizations", {
5
+ id: text("id").primaryKey(),
6
+ name: text("name").unique().notNull(),
7
+ displayName: text("display_name").notNull(),
8
+ maxAgents: integer("max_agents").notNull().default(0),
9
+ maxMessagesPerMinute: integer("max_messages_per_minute").notNull().default(0),
10
+ features: jsonb("features").$type().notNull().default({}),
11
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
12
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
13
+ });
14
+ /** User accounts. Passwords are stored as bcrypt hashes. */
15
+ const users = pgTable("users", {
16
+ id: text("id").primaryKey(),
17
+ username: text("username").unique().notNull(),
18
+ passwordHash: text("password_hash").notNull(),
19
+ displayName: text("display_name").notNull(),
20
+ avatarUrl: text("avatar_url"),
21
+ status: text("status").notNull().default("active"),
22
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
23
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
24
+ });
25
+ var AppError = class extends Error {
26
+ constructor(statusCode, message, attrs) {
27
+ super(message);
28
+ this.statusCode = statusCode;
29
+ this.attrs = attrs;
30
+ this.name = "AppError";
31
+ }
32
+ };
33
+ var NotFoundError = class extends AppError {
34
+ constructor(message = "Not found", attrs) {
35
+ super(404, message, attrs);
36
+ this.name = "NotFoundError";
37
+ }
38
+ };
39
+ var UnauthorizedError = class extends AppError {
40
+ constructor(message = "Unauthorized", attrs) {
41
+ super(401, message, attrs);
42
+ this.name = "UnauthorizedError";
43
+ }
44
+ };
45
+ var ForbiddenError = class extends AppError {
46
+ constructor(message = "Forbidden", attrs) {
47
+ super(403, message, attrs);
48
+ this.name = "ForbiddenError";
49
+ }
50
+ };
51
+ var ConflictError = class extends AppError {
52
+ constructor(message = "Conflict", attrs) {
53
+ super(409, message, attrs);
54
+ this.name = "ConflictError";
55
+ }
56
+ };
57
+ var BadRequestError = class extends AppError {
58
+ constructor(message = "Bad request", attrs) {
59
+ super(400, message, attrs);
60
+ this.name = "BadRequestError";
61
+ }
62
+ };
63
+ /**
64
+ * Thrown when an operation targets a client whose organization does not match
65
+ * the caller's authenticated organization. Retained for wire compatibility:
66
+ * the read paths that produced this error were retired in
67
+ * decouple-client-from-identity §4.1, so the server itself no longer raises
68
+ * it. SDK consumers may still pattern-match the `code` field on legacy
69
+ * payloads.
70
+ */
71
+ var ClientOrgMismatchError = class extends AppError {
72
+ code = "CLIENT_ORG_MISMATCH";
73
+ constructor(message = "Client belongs to a different organization", attrs) {
74
+ super(403, message, attrs);
75
+ this.name = "ClientOrgMismatchError";
76
+ }
77
+ };
78
+ /**
79
+ * Thrown when a client.yaml is presented with a JWT whose user_id does not
80
+ * match the row's owner. The CLI responds by guiding the operator through
81
+ * `first-tree-hub client claim --confirm` to take over ownership, which
82
+ * unpins the previous owner's agents from this machine.
83
+ */
84
+ var ClientUserMismatchError = class extends AppError {
85
+ code = "CLIENT_USER_MISMATCH";
86
+ constructor(message = "Client belongs to a different user", attrs) {
87
+ super(403, message, attrs);
88
+ this.name = "ClientUserMismatchError";
89
+ }
90
+ };
91
+ //#endregion
92
+ export { ConflictError as a, UnauthorizedError as c, ClientUserMismatchError as i, organizations as l, BadRequestError as n, ForbiddenError as o, ClientOrgMismatchError as r, NotFoundError as s, AppError as t, users as u };
@@ -1,39 +1,4 @@
1
- import { createRequire } from "node:module";
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
10
- var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
11
- var __exportAll = (all, no_symbols) => {
12
- let target = {};
13
- for (var name in all) __defProp(target, name, {
14
- get: all[name],
15
- enumerable: true
16
- });
17
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
18
- return target;
19
- };
20
- var __copyProps = (to, from, except, desc) => {
21
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
22
- key = keys[i];
23
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
24
- get: ((k) => from[k]).bind(null, key),
25
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
26
- });
27
- }
28
- return to;
29
- };
30
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
31
- value: mod,
32
- enumerable: true
33
- }) : target, mod));
34
- var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
35
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
36
- //#endregion
1
+ import { n as __esmMin, r as __exportAll } from "./chunk-BSw8zbkd.mjs";
37
2
  //#region ../../node_modules/.pnpm/@opentelemetry+api@1.9.1/node_modules/@opentelemetry/api/build/esm/version.js
38
3
  var VERSION;
39
4
  var init_version = __esmMin((() => {
@@ -1548,4 +1513,4 @@ var init_esm = __esmMin((() => {
1548
1513
  };
1549
1514
  }));
1550
1515
  //#endregion
1551
- export { diag as a, SpanKind as c, __exportAll as d, __require as f, propagation as i, __commonJSMin as l, __toESM as m, init_esm as n, context as o, __toCommonJS as p, trace as r, SpanStatusCode as s, esm_exports as t, __esmMin as u };
1516
+ export { metrics as a, SpanStatusCode as c, createContextKey as d, DiagLogLevel as f, propagation as i, SpanKind as l, init_esm as n, diag as o, trace as r, context as s, esm_exports as t, DiagConsoleLogger as u };
@@ -0,0 +1,10 @@
1
+ import { i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
+ //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/execAsync.js
3
+ var require_execAsync = /* @__PURE__ */ __commonJSMin(((exports) => {
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.execAsync = void 0;
6
+ const child_process = __require("child_process");
7
+ exports.execAsync = __require("util").promisify(child_process.exec);
8
+ }));
9
+ //#endregion
10
+ export { require_execAsync as t };
@@ -1,4 +1,4 @@
1
- import { f as __require, l as __commonJSMin } from "./esm-CYu4tXXn.mjs";
1
+ import { i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
2
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.0_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/execAsync.js
3
3
  var require_execAsync = /* @__PURE__ */ __commonJSMin(((exports) => {
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,5 +1,5 @@
1
- import { d as __exportAll } from "./esm-CYu4tXXn.mjs";
2
- import { r as AGENT_SELECTOR_HEADER } from "./dist-D6AOiyNg.mjs";
1
+ import { r as __exportAll } from "./chunk-BSw8zbkd.mjs";
2
+ import { r as AGENT_SELECTOR_HEADER } from "./dist-BkvrONSQ.mjs";
3
3
  //#region src/core/feishu.ts
4
4
  var feishu_exports = /* @__PURE__ */ __exportAll({
5
5
  bindFeishuBot: () => bindFeishuBot,