@agent-team-foundation/first-tree-hub 0.11.3 → 0.11.5
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-D4rdqM2F.mjs → bootstrap-C_K2CKXC.mjs} +5 -13
- package/dist/cli/index.mjs +71 -9
- package/dist/client-D_TRJFZY-LbgJF47t.mjs +4 -0
- package/dist/{client-CLdRbuml-BRtalKpQ.mjs → client-DqdGiggm-NQoGZ2vM.mjs} +3 -3
- package/dist/{dist-BAqGZkco.mjs → dist-CfvCT4E0.mjs} +318 -192
- package/dist/drizzle/0032_organization_settings.sql +36 -0
- package/dist/drizzle/0033_onboarding_dismissed_at.sql +13 -0
- package/dist/drizzle/meta/_journal.json +15 -1
- package/dist/{errors-BmyRwN0Y-Dad3eV8F.mjs → errors-CF5evtJt-B0NTIVPt.mjs} +2 -1
- package/dist/{feishu-Th_-ivJ7.mjs → feishu-DbSvp9UH.mjs} +1 -1
- package/dist/index.mjs +7 -7
- package/dist/{invitation-Dnn5gGGX-DXryyvRG.mjs → invitation-Bg0TRiyx-BsZH4GCS.mjs} +2 -2
- package/dist/invitation-C299fxkP-BR-niZyp.mjs +4 -0
- package/dist/{saas-connect-gcT6Q10z.mjs → saas-connect-CO554S-V.mjs} +1121 -310
- package/dist/web/assets/{index-CD7rTdqm.js → index-B7noAoV-.js} +1 -1
- package/dist/web/assets/index-DPLa60vJ.css +1 -0
- package/dist/web/assets/index-DvGkka4N.js +390 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/client-By1K4VVT-C5K7WZo6.mjs +0 -4
- package/dist/invitation-DWlyNb8x-D3zjZSwI.mjs +0 -4
- package/dist/web/assets/index-43trJLR8.js +0 -388
- package/dist/web/assets/index-fNb_M0nL.css +0 -1
|
@@ -137,195 +137,6 @@ z.object({
|
|
|
137
137
|
connected: z.boolean(),
|
|
138
138
|
lastActiveAt: z.string().nullable()
|
|
139
139
|
});
|
|
140
|
-
const presenceStatusSchema = z.enum(["online", "offline"]);
|
|
141
|
-
const runtimeStateSchema = z.enum([
|
|
142
|
-
"idle",
|
|
143
|
-
"working",
|
|
144
|
-
"blocked",
|
|
145
|
-
"error"
|
|
146
|
-
]);
|
|
147
|
-
z.enum([
|
|
148
|
-
"active",
|
|
149
|
-
"suspended",
|
|
150
|
-
"evicted"
|
|
151
|
-
]);
|
|
152
|
-
/** Wire-level states a client may report. `evicted` from a stale client is rejected. */
|
|
153
|
-
const clientSessionStateSchema = z.enum(["active", "suspended"]);
|
|
154
|
-
const sessionStateMessageSchema = z.object({
|
|
155
|
-
chatId: z.string().min(1),
|
|
156
|
-
state: clientSessionStateSchema
|
|
157
|
-
});
|
|
158
|
-
/** Client-reported runtime state override (client → server, per-agent). */
|
|
159
|
-
const runtimeStateMessageSchema = z.object({ runtimeState: runtimeStateSchema });
|
|
160
|
-
const agentBindRequestSchema = z.object({
|
|
161
|
-
agentId: z.string().min(1),
|
|
162
|
-
runtimeType: z.string().max(50),
|
|
163
|
-
runtimeVersion: z.string().max(50).optional()
|
|
164
|
-
});
|
|
165
|
-
const AGENT_BIND_REJECT_REASONS = {
|
|
166
|
-
WRONG_CLIENT: "wrong_client",
|
|
167
|
-
NOT_OWNED: "not_owned",
|
|
168
|
-
AGENT_SUSPENDED: "agent_suspended",
|
|
169
|
-
WRONG_ORG: "wrong_org",
|
|
170
|
-
UNKNOWN_AGENT: "unknown_agent",
|
|
171
|
-
RUNTIME_PROVIDER_MISMATCH: "runtime_provider_mismatch"
|
|
172
|
-
};
|
|
173
|
-
z.enum([
|
|
174
|
-
"wrong_client",
|
|
175
|
-
"not_owned",
|
|
176
|
-
"agent_suspended",
|
|
177
|
-
"wrong_org",
|
|
178
|
-
"unknown_agent",
|
|
179
|
-
"runtime_provider_mismatch"
|
|
180
|
-
]);
|
|
181
|
-
/** Header used on agent-scoped HTTP calls to select which managed agent the JWT acts as. */
|
|
182
|
-
const AGENT_SELECTOR_HEADER = "x-agent-id";
|
|
183
|
-
z.object({
|
|
184
|
-
agentId: z.string(),
|
|
185
|
-
status: presenceStatusSchema,
|
|
186
|
-
connectedAt: z.string().nullable(),
|
|
187
|
-
lastSeenAt: z.string(),
|
|
188
|
-
clientId: z.string().nullable().optional(),
|
|
189
|
-
runtimeType: z.string().nullable().optional(),
|
|
190
|
-
runtimeVersion: z.string().nullable().optional(),
|
|
191
|
-
runtimeState: runtimeStateSchema.nullable().optional(),
|
|
192
|
-
activeSessions: z.number().int().nullable().optional(),
|
|
193
|
-
totalSessions: z.number().int().nullable().optional(),
|
|
194
|
-
runtimeUpdatedAt: z.string().nullable().optional()
|
|
195
|
-
});
|
|
196
|
-
z.object({
|
|
197
|
-
total: z.number().int(),
|
|
198
|
-
running: z.number().int(),
|
|
199
|
-
byState: z.object({
|
|
200
|
-
idle: z.number().int(),
|
|
201
|
-
working: z.number().int(),
|
|
202
|
-
blocked: z.number().int(),
|
|
203
|
-
error: z.number().int()
|
|
204
|
-
}),
|
|
205
|
-
clients: z.number().int()
|
|
206
|
-
});
|
|
207
|
-
const runtimeProviderSchema = z.enum(["claude-code", "codex"]);
|
|
208
|
-
const DEFAULT_RUNTIME_PROVIDER = "claude-code";
|
|
209
|
-
const AGENT_TYPES = {
|
|
210
|
-
HUMAN: "human",
|
|
211
|
-
PERSONAL_ASSISTANT: "personal_assistant",
|
|
212
|
-
AUTONOMOUS_AGENT: "autonomous_agent"
|
|
213
|
-
};
|
|
214
|
-
const agentTypeSchema = z.enum([
|
|
215
|
-
"human",
|
|
216
|
-
"personal_assistant",
|
|
217
|
-
"autonomous_agent"
|
|
218
|
-
]);
|
|
219
|
-
const AGENT_VISIBILITY = {
|
|
220
|
-
PRIVATE: "private",
|
|
221
|
-
ORGANIZATION: "organization"
|
|
222
|
-
};
|
|
223
|
-
const agentVisibilitySchema = z.enum(["private", "organization"]);
|
|
224
|
-
const AGENT_STATUSES = {
|
|
225
|
-
ACTIVE: "active",
|
|
226
|
-
SUSPENDED: "suspended",
|
|
227
|
-
DELETED: "deleted"
|
|
228
|
-
};
|
|
229
|
-
const AGENT_SOURCES = {
|
|
230
|
-
ADMIN_API: "admin-api",
|
|
231
|
-
PORTAL: "portal"
|
|
232
|
-
};
|
|
233
|
-
const agentSourceSchema = z.enum(["admin-api", "portal"]);
|
|
234
|
-
z.enum(["active", "suspended"]);
|
|
235
|
-
/**
|
|
236
|
-
* Agent-name rules (see docs/agent-naming-design.md §3.1):
|
|
237
|
-
* - Lowercase ASCII slug, hyphens + underscores allowed.
|
|
238
|
-
* - Must start with alphanumeric: `-` / `_` as first char collide with
|
|
239
|
-
* CLI flag parsing and markdown list syntax.
|
|
240
|
-
* - 1–64 chars — aligned with `MENTION_REGEX` so any valid name can be
|
|
241
|
-
* @-mentioned in chat. Older rows created under the previous 1–100
|
|
242
|
-
* regex are grandfathered; the tight rule only gates new creates.
|
|
243
|
-
*/
|
|
244
|
-
const AGENT_NAME_REGEX = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
245
|
-
const RESERVED_AGENT_NAMES_SET = new Set([
|
|
246
|
-
"admin",
|
|
247
|
-
"agent",
|
|
248
|
-
"first-tree",
|
|
249
|
-
"hub",
|
|
250
|
-
"me",
|
|
251
|
-
"null",
|
|
252
|
-
"system",
|
|
253
|
-
"undefined"
|
|
254
|
-
]);
|
|
255
|
-
function isReservedAgentName(name) {
|
|
256
|
-
return RESERVED_AGENT_NAMES_SET.has(name);
|
|
257
|
-
}
|
|
258
|
-
const createAgentSchema = z.object({
|
|
259
|
-
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(),
|
|
260
|
-
type: agentTypeSchema,
|
|
261
|
-
displayName: z.string().min(1).max(200).optional(),
|
|
262
|
-
delegateMention: z.string().max(100).optional(),
|
|
263
|
-
organizationId: z.string().min(1).max(100).optional(),
|
|
264
|
-
source: agentSourceSchema.optional(),
|
|
265
|
-
visibility: agentVisibilitySchema.optional(),
|
|
266
|
-
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
267
|
-
managerId: z.string().optional(),
|
|
268
|
-
clientId: z.string().min(1).max(100).optional(),
|
|
269
|
-
runtimeProvider: runtimeProviderSchema.optional()
|
|
270
|
-
});
|
|
271
|
-
const updateAgentSchema = z.object({
|
|
272
|
-
type: agentTypeSchema.optional(),
|
|
273
|
-
displayName: z.string().min(1).max(200).optional(),
|
|
274
|
-
delegateMention: z.string().max(100).nullable().optional(),
|
|
275
|
-
visibility: agentVisibilitySchema.optional(),
|
|
276
|
-
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
277
|
-
managerId: z.string().nullable().optional(),
|
|
278
|
-
clientId: z.string().min(1).max(100).nullable().optional()
|
|
279
|
-
});
|
|
280
|
-
/**
|
|
281
|
-
* Service-level rebind input. Admin / owner re-binds an agent to a new
|
|
282
|
-
* client and/or a new runtime provider in one atomic operation.
|
|
283
|
-
*
|
|
284
|
-
* `force` bypasses the capability-match check (e.g. when the client is
|
|
285
|
-
* offline and capabilities are stale).
|
|
286
|
-
*/
|
|
287
|
-
const rebindAgentSchema = z.object({
|
|
288
|
-
clientId: z.string().min(1).max(100),
|
|
289
|
-
runtimeProvider: runtimeProviderSchema,
|
|
290
|
-
force: z.boolean().optional()
|
|
291
|
-
});
|
|
292
|
-
z.object({
|
|
293
|
-
uuid: z.string(),
|
|
294
|
-
name: z.string().nullable(),
|
|
295
|
-
organizationId: z.string(),
|
|
296
|
-
type: agentTypeSchema,
|
|
297
|
-
displayName: z.string(),
|
|
298
|
-
delegateMention: z.string().nullable(),
|
|
299
|
-
inboxId: z.string(),
|
|
300
|
-
status: z.string(),
|
|
301
|
-
source: z.string().nullable().optional(),
|
|
302
|
-
visibility: agentVisibilitySchema,
|
|
303
|
-
metadata: z.record(z.string(), z.unknown()),
|
|
304
|
-
managerId: z.string().nullable(),
|
|
305
|
-
clientId: z.string().nullable(),
|
|
306
|
-
runtimeProvider: runtimeProviderSchema,
|
|
307
|
-
presenceStatus: presenceStatusSchema.optional(),
|
|
308
|
-
createdAt: z.string(),
|
|
309
|
-
updatedAt: z.string()
|
|
310
|
-
});
|
|
311
|
-
z.object({
|
|
312
|
-
repo: z.string().nullable(),
|
|
313
|
-
branch: z.string().nullable()
|
|
314
|
-
});
|
|
315
|
-
/**
|
|
316
|
-
* Server → client WebSocket frame announcing that an agent has just been
|
|
317
|
-
* pinned to the connected client (either created with `clientId` or bound via
|
|
318
|
-
* PATCH NULL → ID). The client can auto-register a local config from this so
|
|
319
|
-
* the operator doesn't have to run `first-tree-hub agent add` manually.
|
|
320
|
-
*/
|
|
321
|
-
const agentPinnedMessageSchema = z.object({
|
|
322
|
-
type: z.literal("agent:pinned"),
|
|
323
|
-
agentId: z.string(),
|
|
324
|
-
name: z.string().nullable(),
|
|
325
|
-
displayName: z.string(),
|
|
326
|
-
agentType: agentTypeSchema,
|
|
327
|
-
runtimeProvider: runtimeProviderSchema
|
|
328
|
-
});
|
|
329
140
|
/**
|
|
330
141
|
* Agent runtime configuration.
|
|
331
142
|
*
|
|
@@ -500,6 +311,26 @@ const agentRuntimeConfigSchema = z.object({
|
|
|
500
311
|
updatedBy: z.string()
|
|
501
312
|
});
|
|
502
313
|
/**
|
|
314
|
+
* Write-side shape with no `.default()` per field.
|
|
315
|
+
*
|
|
316
|
+
* `agentRuntimeConfigPayloadShape` carries `.default()` on every field for the
|
|
317
|
+
* read path (so legacy DB rows parse cleanly). On the PATCH side those defaults
|
|
318
|
+
* are actively harmful: Zod 4's `.partial()` makes a field optional but keeps
|
|
319
|
+
* the inner `ZodDefault`, so a body like `{ mcpServers: [...] }` parses to a
|
|
320
|
+
* fully-populated patch where the omitted fields are filled with their
|
|
321
|
+
* defaults — the service layer's `patch.x ?? current.x` then sees a truthy
|
|
322
|
+
* default and *replaces* the user's saved value with empty. Mirroring the 5
|
|
323
|
+
* fields here without defaults keeps "field absent" → `undefined` in the
|
|
324
|
+
* parsed patch, which is what the merge logic expects.
|
|
325
|
+
*/
|
|
326
|
+
const agentRuntimeConfigPatchShape = z.object({
|
|
327
|
+
prompt: promptConfigSchema,
|
|
328
|
+
model: z.string(),
|
|
329
|
+
mcpServers: z.array(mcpServerSchema),
|
|
330
|
+
env: z.array(envEntrySchema),
|
|
331
|
+
gitRepos: z.array(gitRepoSchema)
|
|
332
|
+
}).partial();
|
|
333
|
+
/**
|
|
503
334
|
* Patch payload for PATCH /api/v1/admin/agents/:uuid/config.
|
|
504
335
|
*
|
|
505
336
|
* - `expectedVersion` enforces optimistic locking; mismatch → 409.
|
|
@@ -507,9 +338,9 @@ const agentRuntimeConfigSchema = z.object({
|
|
|
507
338
|
*/
|
|
508
339
|
const updateAgentRuntimeConfigSchema = z.object({
|
|
509
340
|
expectedVersion: z.number().int().positive(),
|
|
510
|
-
payload:
|
|
341
|
+
payload: agentRuntimeConfigPatchShape
|
|
511
342
|
});
|
|
512
|
-
const dryRunAgentRuntimeConfigSchema = z.object({ payload:
|
|
343
|
+
const dryRunAgentRuntimeConfigSchema = z.object({ payload: agentRuntimeConfigPatchShape });
|
|
513
344
|
z.object({
|
|
514
345
|
current: agentRuntimeConfigSchema,
|
|
515
346
|
next: agentRuntimeConfigPayloadSchema,
|
|
@@ -538,6 +369,196 @@ function deriveRepoLocalPath(url) {
|
|
|
538
369
|
if (!trimmed) return "";
|
|
539
370
|
return ((trimmed.split(/[?#]/)[0] ?? "").split(/[/:]/).filter(Boolean).pop() ?? "").replace(/\.git$/i, "");
|
|
540
371
|
}
|
|
372
|
+
const presenceStatusSchema = z.enum(["online", "offline"]);
|
|
373
|
+
const runtimeStateSchema = z.enum([
|
|
374
|
+
"idle",
|
|
375
|
+
"working",
|
|
376
|
+
"blocked",
|
|
377
|
+
"error"
|
|
378
|
+
]);
|
|
379
|
+
z.enum([
|
|
380
|
+
"active",
|
|
381
|
+
"suspended",
|
|
382
|
+
"evicted"
|
|
383
|
+
]);
|
|
384
|
+
/** Wire-level states a client may report. `evicted` from a stale client is rejected. */
|
|
385
|
+
const clientSessionStateSchema = z.enum(["active", "suspended"]);
|
|
386
|
+
const sessionStateMessageSchema = z.object({
|
|
387
|
+
chatId: z.string().min(1),
|
|
388
|
+
state: clientSessionStateSchema
|
|
389
|
+
});
|
|
390
|
+
/** Client-reported runtime state override (client → server, per-agent). */
|
|
391
|
+
const runtimeStateMessageSchema = z.object({ runtimeState: runtimeStateSchema });
|
|
392
|
+
const agentBindRequestSchema = z.object({
|
|
393
|
+
agentId: z.string().min(1),
|
|
394
|
+
runtimeType: z.string().max(50),
|
|
395
|
+
runtimeVersion: z.string().max(50).optional()
|
|
396
|
+
});
|
|
397
|
+
const AGENT_BIND_REJECT_REASONS = {
|
|
398
|
+
WRONG_CLIENT: "wrong_client",
|
|
399
|
+
NOT_OWNED: "not_owned",
|
|
400
|
+
AGENT_SUSPENDED: "agent_suspended",
|
|
401
|
+
WRONG_ORG: "wrong_org",
|
|
402
|
+
UNKNOWN_AGENT: "unknown_agent",
|
|
403
|
+
RUNTIME_PROVIDER_MISMATCH: "runtime_provider_mismatch"
|
|
404
|
+
};
|
|
405
|
+
z.enum([
|
|
406
|
+
"wrong_client",
|
|
407
|
+
"not_owned",
|
|
408
|
+
"agent_suspended",
|
|
409
|
+
"wrong_org",
|
|
410
|
+
"unknown_agent",
|
|
411
|
+
"runtime_provider_mismatch"
|
|
412
|
+
]);
|
|
413
|
+
/** Header used on agent-scoped HTTP calls to select which managed agent the JWT acts as. */
|
|
414
|
+
const AGENT_SELECTOR_HEADER = "x-agent-id";
|
|
415
|
+
z.object({
|
|
416
|
+
agentId: z.string(),
|
|
417
|
+
status: presenceStatusSchema,
|
|
418
|
+
connectedAt: z.string().nullable(),
|
|
419
|
+
lastSeenAt: z.string(),
|
|
420
|
+
clientId: z.string().nullable().optional(),
|
|
421
|
+
runtimeType: z.string().nullable().optional(),
|
|
422
|
+
runtimeVersion: z.string().nullable().optional(),
|
|
423
|
+
runtimeState: runtimeStateSchema.nullable().optional(),
|
|
424
|
+
activeSessions: z.number().int().nullable().optional(),
|
|
425
|
+
totalSessions: z.number().int().nullable().optional(),
|
|
426
|
+
runtimeUpdatedAt: z.string().nullable().optional()
|
|
427
|
+
});
|
|
428
|
+
z.object({
|
|
429
|
+
total: z.number().int(),
|
|
430
|
+
running: z.number().int(),
|
|
431
|
+
byState: z.object({
|
|
432
|
+
idle: z.number().int(),
|
|
433
|
+
working: z.number().int(),
|
|
434
|
+
blocked: z.number().int(),
|
|
435
|
+
error: z.number().int()
|
|
436
|
+
}),
|
|
437
|
+
clients: z.number().int()
|
|
438
|
+
});
|
|
439
|
+
const runtimeProviderSchema = z.enum(["claude-code", "codex"]);
|
|
440
|
+
const DEFAULT_RUNTIME_PROVIDER = "claude-code";
|
|
441
|
+
const AGENT_TYPES = {
|
|
442
|
+
HUMAN: "human",
|
|
443
|
+
PERSONAL_ASSISTANT: "personal_assistant",
|
|
444
|
+
AUTONOMOUS_AGENT: "autonomous_agent"
|
|
445
|
+
};
|
|
446
|
+
const agentTypeSchema = z.enum([
|
|
447
|
+
"human",
|
|
448
|
+
"personal_assistant",
|
|
449
|
+
"autonomous_agent"
|
|
450
|
+
]);
|
|
451
|
+
const AGENT_VISIBILITY = {
|
|
452
|
+
PRIVATE: "private",
|
|
453
|
+
ORGANIZATION: "organization"
|
|
454
|
+
};
|
|
455
|
+
const agentVisibilitySchema = z.enum(["private", "organization"]);
|
|
456
|
+
const AGENT_STATUSES = {
|
|
457
|
+
ACTIVE: "active",
|
|
458
|
+
SUSPENDED: "suspended",
|
|
459
|
+
DELETED: "deleted"
|
|
460
|
+
};
|
|
461
|
+
const AGENT_SOURCES = {
|
|
462
|
+
ADMIN_API: "admin-api",
|
|
463
|
+
PORTAL: "portal"
|
|
464
|
+
};
|
|
465
|
+
const agentSourceSchema = z.enum(["admin-api", "portal"]);
|
|
466
|
+
z.enum(["active", "suspended"]);
|
|
467
|
+
/**
|
|
468
|
+
* Agent-name rules (see docs/agent-naming-design.md §3.1):
|
|
469
|
+
* - Lowercase ASCII slug, hyphens + underscores allowed.
|
|
470
|
+
* - Must start with alphanumeric: `-` / `_` as first char collide with
|
|
471
|
+
* CLI flag parsing and markdown list syntax.
|
|
472
|
+
* - 1–64 chars — aligned with `MENTION_REGEX` so any valid name can be
|
|
473
|
+
* @-mentioned in chat. Older rows created under the previous 1–100
|
|
474
|
+
* regex are grandfathered; the tight rule only gates new creates.
|
|
475
|
+
*/
|
|
476
|
+
const AGENT_NAME_REGEX = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
477
|
+
const RESERVED_AGENT_NAMES_SET = new Set([
|
|
478
|
+
"admin",
|
|
479
|
+
"agent",
|
|
480
|
+
"first-tree",
|
|
481
|
+
"hub",
|
|
482
|
+
"me",
|
|
483
|
+
"null",
|
|
484
|
+
"system",
|
|
485
|
+
"undefined"
|
|
486
|
+
]);
|
|
487
|
+
function isReservedAgentName(name) {
|
|
488
|
+
return RESERVED_AGENT_NAMES_SET.has(name);
|
|
489
|
+
}
|
|
490
|
+
const createAgentSchema = z.object({
|
|
491
|
+
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(),
|
|
492
|
+
type: agentTypeSchema,
|
|
493
|
+
displayName: z.string().min(1).max(200).optional(),
|
|
494
|
+
delegateMention: z.string().max(100).optional(),
|
|
495
|
+
organizationId: z.string().min(1).max(100).optional(),
|
|
496
|
+
source: agentSourceSchema.optional(),
|
|
497
|
+
visibility: agentVisibilitySchema.optional(),
|
|
498
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
499
|
+
managerId: z.string().optional(),
|
|
500
|
+
clientId: z.string().min(1).max(100).optional(),
|
|
501
|
+
runtimeProvider: runtimeProviderSchema.optional(),
|
|
502
|
+
gitRepos: z.array(gitRepoSchema).optional()
|
|
503
|
+
});
|
|
504
|
+
const updateAgentSchema = z.object({
|
|
505
|
+
type: agentTypeSchema.optional(),
|
|
506
|
+
displayName: z.string().min(1).max(200).optional(),
|
|
507
|
+
delegateMention: z.string().max(100).nullable().optional(),
|
|
508
|
+
visibility: agentVisibilitySchema.optional(),
|
|
509
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
510
|
+
managerId: z.string().nullable().optional(),
|
|
511
|
+
clientId: z.string().min(1).max(100).nullable().optional()
|
|
512
|
+
});
|
|
513
|
+
/**
|
|
514
|
+
* Service-level rebind input. Admin / owner re-binds an agent to a new
|
|
515
|
+
* client and/or a new runtime provider in one atomic operation.
|
|
516
|
+
*
|
|
517
|
+
* `force` bypasses the capability-match check (e.g. when the client is
|
|
518
|
+
* offline and capabilities are stale).
|
|
519
|
+
*/
|
|
520
|
+
const rebindAgentSchema = z.object({
|
|
521
|
+
clientId: z.string().min(1).max(100),
|
|
522
|
+
runtimeProvider: runtimeProviderSchema,
|
|
523
|
+
force: z.boolean().optional()
|
|
524
|
+
});
|
|
525
|
+
z.object({
|
|
526
|
+
uuid: z.string(),
|
|
527
|
+
name: z.string().nullable(),
|
|
528
|
+
organizationId: z.string(),
|
|
529
|
+
type: agentTypeSchema,
|
|
530
|
+
displayName: z.string(),
|
|
531
|
+
delegateMention: z.string().nullable(),
|
|
532
|
+
inboxId: z.string(),
|
|
533
|
+
status: z.string(),
|
|
534
|
+
source: z.string().nullable().optional(),
|
|
535
|
+
visibility: agentVisibilitySchema,
|
|
536
|
+
metadata: z.record(z.string(), z.unknown()),
|
|
537
|
+
managerId: z.string().nullable(),
|
|
538
|
+
clientId: z.string().nullable(),
|
|
539
|
+
runtimeProvider: runtimeProviderSchema,
|
|
540
|
+
presenceStatus: presenceStatusSchema.optional(),
|
|
541
|
+
createdAt: z.string(),
|
|
542
|
+
updatedAt: z.string()
|
|
543
|
+
});
|
|
544
|
+
z.object({
|
|
545
|
+
repo: z.string().nullable(),
|
|
546
|
+
branch: z.string().nullable()
|
|
547
|
+
});
|
|
548
|
+
/**
|
|
549
|
+
* Server → client WebSocket frame announcing that an agent has just been
|
|
550
|
+
* pinned to the connected client (either created with `clientId` or bound via
|
|
551
|
+
* PATCH NULL → ID). The client can auto-register a local config from this so
|
|
552
|
+
* the operator doesn't have to run `first-tree-hub agent add` manually.
|
|
553
|
+
*/
|
|
554
|
+
const agentPinnedMessageSchema = z.object({
|
|
555
|
+
type: z.literal("agent:pinned"),
|
|
556
|
+
agentId: z.string(),
|
|
557
|
+
name: z.string().nullable(),
|
|
558
|
+
displayName: z.string(),
|
|
559
|
+
agentType: agentTypeSchema,
|
|
560
|
+
runtimeProvider: runtimeProviderSchema
|
|
561
|
+
});
|
|
541
562
|
const loginSchema = z.object({
|
|
542
563
|
username: z.string().min(1),
|
|
543
564
|
password: z.string().min(1)
|
|
@@ -1054,6 +1075,43 @@ const createOrgFromMeSchema = z.object({
|
|
|
1054
1075
|
name: z.string().min(2).max(50).regex(/^[a-z0-9][a-z0-9-]*$/),
|
|
1055
1076
|
displayName: z.string().min(1).max(200)
|
|
1056
1077
|
});
|
|
1078
|
+
/**
|
|
1079
|
+
* Body for `PATCH /me/onboarding`. v1 only mutates `dismissed` — true to
|
|
1080
|
+
* hide the onboarding stepper (server stamps `users.onboarding_dismissed_at
|
|
1081
|
+
* = NOW()`), false to restore it. See docs/new-user-onboarding-design.md
|
|
1082
|
+
* §8.4.
|
|
1083
|
+
*/
|
|
1084
|
+
const patchOnboardingSchema = z.object({ dismissed: z.boolean().optional() });
|
|
1085
|
+
/**
|
|
1086
|
+
* Body for `POST /me/onboarding/events`. The web SPA reports key
|
|
1087
|
+
* milestones so the server can log them as a single funnel-trackable
|
|
1088
|
+
* stream alongside server-emitted events (`team_created`, `dismissed`).
|
|
1089
|
+
*
|
|
1090
|
+
* Server emits:
|
|
1091
|
+
* - `team_created` — at OAuth callback when joinPath === "solo"
|
|
1092
|
+
* - `dismissed` — when PATCH /me/onboarding flips dismissed
|
|
1093
|
+
*
|
|
1094
|
+
* Web reports:
|
|
1095
|
+
* - `team_renamed` — Step 1 user changed the auto-named team
|
|
1096
|
+
* - `agent_created` — Step 2 successfully created the agent
|
|
1097
|
+
* - `tree_chat_started` — Step 3 [Yes, set it up] succeeded
|
|
1098
|
+
* - `tree_intro_dismissed` — Step 3 [I'll do it later] clicked
|
|
1099
|
+
*/
|
|
1100
|
+
const onboardingEventNameSchema = z.enum([
|
|
1101
|
+
"team_renamed",
|
|
1102
|
+
"agent_created",
|
|
1103
|
+
"tree_chat_started",
|
|
1104
|
+
"tree_intro_dismissed"
|
|
1105
|
+
]);
|
|
1106
|
+
const onboardingEventSchema = z.object({
|
|
1107
|
+
event: onboardingEventNameSchema,
|
|
1108
|
+
attrs: z.record(z.string(), z.union([
|
|
1109
|
+
z.string(),
|
|
1110
|
+
z.number(),
|
|
1111
|
+
z.boolean(),
|
|
1112
|
+
z.null()
|
|
1113
|
+
])).optional()
|
|
1114
|
+
});
|
|
1057
1115
|
z.object({
|
|
1058
1116
|
id: z.string(),
|
|
1059
1117
|
organizationId: z.string(),
|
|
@@ -1140,6 +1198,74 @@ const githubDevCallbackQuerySchema = z.object({
|
|
|
1140
1198
|
displayName: z.string().optional(),
|
|
1141
1199
|
next: z.string().max(256).optional()
|
|
1142
1200
|
});
|
|
1201
|
+
/**
|
|
1202
|
+
* Per-organization settings — schemas, namespaces, and the registry that
|
|
1203
|
+
* dispatches `(orgId, namespace)` lookups to the right validator.
|
|
1204
|
+
*
|
|
1205
|
+
* Each namespace has three schemas:
|
|
1206
|
+
* - `storage` — what is persisted in `organization_settings.value`. For
|
|
1207
|
+
* namespaces with secrets, the storage schema names the *cipher* field
|
|
1208
|
+
* (e.g. `webhookSecretCipher`); plaintext never touches the row.
|
|
1209
|
+
* - `input` — what the admin API accepts in PUT bodies. For namespaces
|
|
1210
|
+
* with secrets, `webhookSecret` is plaintext; the service layer
|
|
1211
|
+
* encrypts it before merging into storage.
|
|
1212
|
+
* - `output` — what GET returns. Secrets are replaced by a boolean
|
|
1213
|
+
* `…Configured` flag — plaintext is never echoed.
|
|
1214
|
+
*
|
|
1215
|
+
* Adding a new per-org config group:
|
|
1216
|
+
* 1. Define three schemas (storage / input / output).
|
|
1217
|
+
* 2. Add a key to `ORG_SETTINGS_NAMESPACES`.
|
|
1218
|
+
* 3. Done. No DB migration, no new API route.
|
|
1219
|
+
*/
|
|
1220
|
+
const orgContextTreeRepoUrlSchema = z.string().url().refine((value) => {
|
|
1221
|
+
try {
|
|
1222
|
+
return new URL(value).protocol === "https:";
|
|
1223
|
+
} catch {
|
|
1224
|
+
return false;
|
|
1225
|
+
}
|
|
1226
|
+
}, { message: "Context Tree repo URL must use HTTPS." }).refine((value) => {
|
|
1227
|
+
try {
|
|
1228
|
+
const url = new URL(value);
|
|
1229
|
+
return url.username.length === 0 && url.password.length === 0;
|
|
1230
|
+
} catch {
|
|
1231
|
+
return false;
|
|
1232
|
+
}
|
|
1233
|
+
}, { message: "Context Tree repo URL must not include credentials." });
|
|
1234
|
+
const orgContextTreeStorageSchema = z.object({
|
|
1235
|
+
repo: orgContextTreeRepoUrlSchema.optional(),
|
|
1236
|
+
branch: z.string().default("main")
|
|
1237
|
+
});
|
|
1238
|
+
const orgContextTreeInputSchema = z.object({
|
|
1239
|
+
repo: orgContextTreeRepoUrlSchema.nullish(),
|
|
1240
|
+
branch: z.string().min(1).nullish()
|
|
1241
|
+
});
|
|
1242
|
+
const orgContextTreeOutputSchema = z.object({
|
|
1243
|
+
repo: z.string().optional(),
|
|
1244
|
+
branch: z.string().optional()
|
|
1245
|
+
});
|
|
1246
|
+
const orgGithubIntegrationStorageSchema = z.object({ webhookSecretCipher: z.string().optional() });
|
|
1247
|
+
const orgGithubIntegrationInputSchema = z.object({ webhookSecret: z.string().min(1).nullish() });
|
|
1248
|
+
const orgGithubIntegrationOutputSchema = z.object({
|
|
1249
|
+
webhookSecretConfigured: z.boolean(),
|
|
1250
|
+
webhookUrl: z.string()
|
|
1251
|
+
});
|
|
1252
|
+
const ORG_SETTINGS_NAMESPACES = {
|
|
1253
|
+
context_tree: {
|
|
1254
|
+
storage: orgContextTreeStorageSchema,
|
|
1255
|
+
input: orgContextTreeInputSchema,
|
|
1256
|
+
output: orgContextTreeOutputSchema
|
|
1257
|
+
},
|
|
1258
|
+
github_integration: {
|
|
1259
|
+
storage: orgGithubIntegrationStorageSchema,
|
|
1260
|
+
input: orgGithubIntegrationInputSchema,
|
|
1261
|
+
output: orgGithubIntegrationOutputSchema
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
const ORG_SETTINGS_NAMESPACE_KEYS = Object.keys(ORG_SETTINGS_NAMESPACES);
|
|
1265
|
+
z.enum(ORG_SETTINGS_NAMESPACE_KEYS);
|
|
1266
|
+
function isOrgSettingNamespace(value) {
|
|
1267
|
+
return typeof value === "string" && ORG_SETTINGS_NAMESPACE_KEYS.includes(value);
|
|
1268
|
+
}
|
|
1143
1269
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1144
1270
|
z.object({
|
|
1145
1271
|
name: z.string().min(2).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Must start with a letter or digit and contain only lowercase alphanumeric and hyphens").refine((v) => !UUID_PATTERN.test(v), "Name must not be a UUID format"),
|
|
@@ -1467,4 +1593,4 @@ z.object({
|
|
|
1467
1593
|
capabilities: serverCapabilitiesSchema.optional()
|
|
1468
1594
|
}).passthrough();
|
|
1469
1595
|
//#endregion
|
|
1470
|
-
export {
|
|
1596
|
+
export { loginSchema as $, createAgentSchema as A, githubCallbackQuerySchema as B, agentTypeSchema as C, updateChatSchema as Ct, contextTreeSnapshotSchema as D, updateTaskStatusSchema as Dt, connectTokenExchangeSchema as E, updateOrganizationSchema as Et, createTaskSchema as F, inboxDeliverFrameSchema as G, githubStartQuerySchema as H, defaultRuntimeConfigPayload as I, isRedactedEnvValue as J, inboxPollQuerySchema as K, delegateFeishuUserSchema as L, createMeChatSchema as M, createMemberSchema as N, createAdapterConfigSchema as O, wsAuthFrameSchema as Ot, createOrgFromMeSchema as P, listMeChatsQuerySchema as Q, dryRunAgentRuntimeConfigSchema as R, agentRuntimeConfigPayloadSchema as S, updateAgentSchema as St, clientRegisterSchema as T, updateMemberSchema as Tt, imageInlineContentSchema as U, githubDevCallbackQuerySchema as V, inboxAckFrameSchema as W, joinByInvitationSchema as X, isReservedAgentName as Y, linkTaskChatSchema as Z, addParticipantSchema as _, sessionStateMessageSchema as _t, AGENT_STATUSES as a, rebindAgentSchema as at, agentBindRequestSchema as b, updateAdapterConfigSchema as bt, DEFAULT_RUNTIME_PROVIDER as c, safeRedirectPath as ct, TASK_CREATOR_TYPES as d, sendMessageSchema as dt, messageSourceSchema as et, TASK_HEALTH_SIGNALS as f, sendToAgentSchema as ft, addMeChatParticipantsSchema as g, sessionReconcileRequestSchema as gt, WS_AUTH_FRAME_TIMEOUT_MS as h, sessionEventSchema as ht, AGENT_SOURCES as i, patchOnboardingSchema as it, createChatSchema as j, createAdapterMappingSchema as k, MENTION_REGEX as l, scanMentionTokens as lt, TASK_TERMINAL_STATUSES as m, sessionEventMessageSchema as mt, AGENT_NAME_REGEX as n, onboardingEventSchema as nt, AGENT_TYPES as o, refreshTokenSchema as ot, TASK_STATUSES as p, sessionCompletionMessageSchema as pt, isOrgSettingNamespace as q, AGENT_SELECTOR_HEADER as r, paginationQuerySchema as rt, AGENT_VISIBILITY as s, runtimeStateMessageSchema as st, AGENT_BIND_REJECT_REASONS as t, notificationQuerySchema as tt, ORG_SETTINGS_NAMESPACES as u, selfServiceFeishuBotSchema as ut, adminCreateTaskSchema as v, stripCode as vt, clientCapabilitiesSchema as w, updateClientCapabilitiesSchema as wt, agentPinnedMessageSchema as x, updateAgentRuntimeConfigSchema as xt, adminUpdateTaskSchema as y, taskListQuerySchema as yt, extractMentions as z };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Per-organization settings, keyed by (organization_id, namespace).
|
|
2
|
+
--
|
|
3
|
+
-- Each row holds an entire group of related config as a JSONB blob; the
|
|
4
|
+
-- schema for each namespace lives in @agent-team-foundation/first-tree-hub-shared
|
|
5
|
+
-- (ORG_SETTINGS_NAMESPACES) and is enforced by the service layer on every
|
|
6
|
+
-- read/write. Adding a new config group means registering a new namespace +
|
|
7
|
+
-- Zod schema in shared — the DB does not change.
|
|
8
|
+
--
|
|
9
|
+
-- `version` is reserved for future optimistic locking (PUT with If-Match).
|
|
10
|
+
-- We keep the column from day one so tightening to compare-and-swap later
|
|
11
|
+
-- is a code-only change with no migration.
|
|
12
|
+
--
|
|
13
|
+
-- Sensitive fields inside `value` (e.g. github_integration.webhookSecret)
|
|
14
|
+
-- are AES-256-GCM-encrypted at the service layer using crypto.ts's
|
|
15
|
+
-- encryptValue / decryptValue — same pattern as adapter_configs.
|
|
16
|
+
--
|
|
17
|
+
-- ON DELETE CASCADE on organization_id: settings have no independent
|
|
18
|
+
-- lifecycle, deleting an org must drop them. updated_by is SET NULL so a
|
|
19
|
+
-- user deletion does not cascade-clobber unrelated config rows.
|
|
20
|
+
|
|
21
|
+
CREATE TABLE IF NOT EXISTS "organization_settings" (
|
|
22
|
+
"organization_id" text NOT NULL,
|
|
23
|
+
"namespace" text NOT NULL,
|
|
24
|
+
"value" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
25
|
+
"version" integer NOT NULL DEFAULT 0,
|
|
26
|
+
"updated_by" text,
|
|
27
|
+
"updated_at" timestamp with time zone NOT NULL DEFAULT now(),
|
|
28
|
+
CONSTRAINT "organization_settings_pkey" PRIMARY KEY ("organization_id", "namespace"),
|
|
29
|
+
CONSTRAINT "organization_settings_organization_id_organizations_id_fk"
|
|
30
|
+
FOREIGN KEY ("organization_id") REFERENCES "organizations"("id") ON DELETE CASCADE,
|
|
31
|
+
CONSTRAINT "organization_settings_updated_by_users_id_fk"
|
|
32
|
+
FOREIGN KEY ("updated_by") REFERENCES "users"("id") ON DELETE SET NULL
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
CREATE INDEX IF NOT EXISTS "idx_org_settings_namespace"
|
|
36
|
+
ON "organization_settings" ("namespace");
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- Onboarding stepper dismissal flag. Decoupled from the server-side
|
|
2
|
+
-- `onboardingStep` enum so the stepper keeps rendering across all three
|
|
3
|
+
-- UI steps (server-side onboardingStep flips to `completed` at the end of
|
|
4
|
+
-- Step 2; Step 3 is purely client-driven and the stepper must keep
|
|
5
|
+
-- showing during the tree-init chat).
|
|
6
|
+
--
|
|
7
|
+
-- See docs/new-user-onboarding-design.md §8.
|
|
8
|
+
--
|
|
9
|
+
-- NULL → stepper renders
|
|
10
|
+
-- value → user clicked the `✕`; stepper unmounts. Irreversible from UI v1.
|
|
11
|
+
|
|
12
|
+
ALTER TABLE "users"
|
|
13
|
+
ADD COLUMN IF NOT EXISTS "onboarding_dismissed_at" timestamp with time zone;
|
|
@@ -225,6 +225,20 @@
|
|
|
225
225
|
"when": 1777939200000,
|
|
226
226
|
"tag": "0031_drop_system_configs",
|
|
227
227
|
"breakpoints": true
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"idx": 32,
|
|
231
|
+
"version": "7",
|
|
232
|
+
"when": 1778198400000,
|
|
233
|
+
"tag": "0032_organization_settings",
|
|
234
|
+
"breakpoints": true
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"idx": 33,
|
|
238
|
+
"version": "7",
|
|
239
|
+
"when": 1778284800000,
|
|
240
|
+
"tag": "0033_onboarding_dismissed_at",
|
|
241
|
+
"breakpoints": true
|
|
228
242
|
}
|
|
229
243
|
]
|
|
230
|
-
}
|
|
244
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { integer, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
2
|
-
//#region ../server/dist/errors-
|
|
2
|
+
//#region ../server/dist/errors-CF5evtJt.mjs
|
|
3
3
|
/** Organization entity. Agents and chats belong to exactly one organization. */
|
|
4
4
|
const organizations = pgTable("organizations", {
|
|
5
5
|
id: text("id").primaryKey(),
|
|
@@ -19,6 +19,7 @@ const users = pgTable("users", {
|
|
|
19
19
|
displayName: text("display_name").notNull(),
|
|
20
20
|
avatarUrl: text("avatar_url"),
|
|
21
21
|
status: text("status").notNull().default("active"),
|
|
22
|
+
onboardingDismissedAt: timestamp("onboarding_dismissed_at", { withTimezone: true }),
|
|
22
23
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
23
24
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
24
25
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as __exportAll } from "./chunk-BSw8zbkd.mjs";
|
|
2
2
|
import { t as cliFetch } from "./cli-fetch--tiwKm5S.mjs";
|
|
3
|
-
import { r as AGENT_SELECTOR_HEADER } from "./dist-
|
|
3
|
+
import { r as AGENT_SELECTOR_HEADER } from "./dist-CfvCT4E0.mjs";
|
|
4
4
|
//#region src/core/feishu.ts
|
|
5
5
|
var feishu_exports = /* @__PURE__ */ __exportAll({
|
|
6
6
|
bindFeishuBot: () => bindFeishuBot,
|