@agent-team-foundation/first-tree-hub 0.11.4 → 0.12.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/{bootstrap-D-Yf8yOc.mjs → bootstrap-C_K2CKXC.mjs} +7 -0
- package/dist/cli/index.mjs +73 -11
- package/dist/client-BPUdUaZT-CyCrpCTP.mjs +2033 -0
- package/dist/client-BhCtO2df-BGOu-rRN.mjs +7 -0
- package/dist/{dist-BQtAQNRD.mjs → dist-LgF7LHpE.mjs} +1 -1
- package/dist/{dist-ClFs4WMj.mjs → dist-UOZ6vMUW.mjs} +372 -197
- package/dist/drizzle/0033_onboarding_dismissed_at.sql +13 -0
- package/dist/drizzle/0034_pending_questions.sql +34 -0
- package/dist/drizzle/meta/_journal.json +14 -0
- package/dist/{errors-BmyRwN0Y-Dad3eV8F.mjs → errors-CF5evtJt-B0NTIVPt.mjs} +2 -1
- package/dist/{feishu-AI3pwmqN.mjs → feishu-C6qlhju2.mjs} +1 -1
- package/dist/{getMachineId-bsd-c2VImogj.mjs → getMachineId-bsd-BmasEOJr.mjs} +1 -1
- package/dist/{getMachineId-bsd-DyySs8xz.mjs → getMachineId-bsd-Dh3h0DDE.mjs} +1 -1
- package/dist/{getMachineId-darwin-Cl7TSzgO.mjs → getMachineId-darwin-CuhM3hfZ.mjs} +1 -1
- package/dist/{getMachineId-darwin-DKgI8b1d.mjs → getMachineId-darwin-D9wR0SLj.mjs} +1 -1
- package/dist/{getMachineId-linux-1OIMWfdh.mjs → getMachineId-linux-CYfb0oxZ.mjs} +1 -1
- package/dist/{getMachineId-linux-cT7EbP10.mjs → getMachineId-linux-D8ZaSjAC.mjs} +1 -1
- package/dist/{getMachineId-unsupported-CkX-YOG1.mjs → getMachineId-unsupported-Cu3iisaD.mjs} +1 -1
- package/dist/{getMachineId-unsupported-CmVlhzIo.mjs → getMachineId-unsupported-DZqI4ZT5.mjs} +1 -1
- package/dist/{getMachineId-win-C2cM60YT.mjs → getMachineId-win-8ZJbtrdf.mjs} +1 -1
- package/dist/{getMachineId-win-Chl03TYe.mjs → getMachineId-win-DT-hqwVp.mjs} +1 -1
- package/dist/index.mjs +9 -9
- package/dist/{invitation-Dnn5gGGX-DXryyvRG.mjs → invitation-Bg0TRiyx-BsZH4GCS.mjs} +2 -2
- package/dist/invitation-C299fxkP-KyCNax4T.mjs +4 -0
- package/dist/{observability-BAScT_5S-gw1ODB_o.mjs → observability-BAScT_5S-BcW9HgkG.mjs} +13 -13
- package/dist/{observability-CYsdAcoF.mjs → observability-eLA9iNK_.mjs} +3 -3
- package/dist/{saas-connect-CVoRK0Ex.mjs → saas-connect-Drn9g6cR.mjs} +1195 -1685
- package/dist/web/assets/index-B_Tf2I6v.css +1 -0
- package/dist/web/assets/{index-Bm6hgcvt.js → index-Bnyz7inW.js} +1 -1
- package/dist/web/assets/index-Dy3jIUX5.js +391 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/client-By1K4VVT-DuI6EnSh.mjs +0 -4
- package/dist/client-CLdRbuml-svTO0Eat.mjs +0 -524
- package/dist/invitation-DWlyNb8x-BvXubk24.mjs +0 -4
- package/dist/web/assets/index-fNb_M0nL.css +0 -1
- package/dist/web/assets/index-k2bWRKc-.js +0 -388
- /package/dist/{esm-Ci8E1Gtj.mjs → esm-iadMkGbV.mjs} +0 -0
- /package/dist/{src-aJMV60mR.mjs → src-DNBS5Yjj.mjs} +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import "./observability-BAScT_5S-BcW9HgkG.mjs";
|
|
2
|
+
import "./logger-core-BTmvdflj-DjW8FM4T.mjs";
|
|
3
|
+
import "./dist-UOZ6vMUW.mjs";
|
|
4
|
+
import "./errors-CF5evtJt-B0NTIVPt.mjs";
|
|
5
|
+
import "./src-DNBS5Yjj.mjs";
|
|
6
|
+
import { q as listMyPinnedAgents } from "./client-BPUdUaZT-CyCrpCTP.mjs";
|
|
7
|
+
export { listMyPinnedAgents };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
|
|
2
|
-
import { t as require_src } from "./src-
|
|
2
|
+
import { t as require_src } from "./src-DNBS5Yjj.mjs";
|
|
3
3
|
//#region ../../node_modules/.pnpm/agent-base@7.1.4/node_modules/agent-base/dist/helpers.js
|
|
4
4
|
var require_helpers = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
5
5
|
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -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)
|
|
@@ -825,7 +846,9 @@ const messageFormatSchema = z.enum([
|
|
|
825
846
|
"card",
|
|
826
847
|
"reference",
|
|
827
848
|
"file",
|
|
828
|
-
"task"
|
|
849
|
+
"task",
|
|
850
|
+
"question",
|
|
851
|
+
"question_answer"
|
|
829
852
|
]);
|
|
830
853
|
const sendMessageSchema = z.object({
|
|
831
854
|
format: messageFormatSchema.default("text"),
|
|
@@ -1054,6 +1077,43 @@ const createOrgFromMeSchema = z.object({
|
|
|
1054
1077
|
name: z.string().min(2).max(50).regex(/^[a-z0-9][a-z0-9-]*$/),
|
|
1055
1078
|
displayName: z.string().min(1).max(200)
|
|
1056
1079
|
});
|
|
1080
|
+
/**
|
|
1081
|
+
* Body for `PATCH /me/onboarding`. v1 only mutates `dismissed` — true to
|
|
1082
|
+
* hide the onboarding stepper (server stamps `users.onboarding_dismissed_at
|
|
1083
|
+
* = NOW()`), false to restore it. See docs/new-user-onboarding-design.md
|
|
1084
|
+
* §8.4.
|
|
1085
|
+
*/
|
|
1086
|
+
const patchOnboardingSchema = z.object({ dismissed: z.boolean().optional() });
|
|
1087
|
+
/**
|
|
1088
|
+
* Body for `POST /me/onboarding/events`. The web SPA reports key
|
|
1089
|
+
* milestones so the server can log them as a single funnel-trackable
|
|
1090
|
+
* stream alongside server-emitted events (`team_created`, `dismissed`).
|
|
1091
|
+
*
|
|
1092
|
+
* Server emits:
|
|
1093
|
+
* - `team_created` — at OAuth callback when joinPath === "solo"
|
|
1094
|
+
* - `dismissed` — when PATCH /me/onboarding flips dismissed
|
|
1095
|
+
*
|
|
1096
|
+
* Web reports:
|
|
1097
|
+
* - `team_renamed` — Step 1 user changed the auto-named team
|
|
1098
|
+
* - `agent_created` — Step 2 successfully created the agent
|
|
1099
|
+
* - `tree_chat_started` — Step 3 [Yes, set it up] succeeded
|
|
1100
|
+
* - `tree_intro_dismissed` — Step 3 [I'll do it later] clicked
|
|
1101
|
+
*/
|
|
1102
|
+
const onboardingEventNameSchema = z.enum([
|
|
1103
|
+
"team_renamed",
|
|
1104
|
+
"agent_created",
|
|
1105
|
+
"tree_chat_started",
|
|
1106
|
+
"tree_intro_dismissed"
|
|
1107
|
+
]);
|
|
1108
|
+
const onboardingEventSchema = z.object({
|
|
1109
|
+
event: onboardingEventNameSchema,
|
|
1110
|
+
attrs: z.record(z.string(), z.union([
|
|
1111
|
+
z.string(),
|
|
1112
|
+
z.number(),
|
|
1113
|
+
z.boolean(),
|
|
1114
|
+
z.null()
|
|
1115
|
+
])).optional()
|
|
1116
|
+
});
|
|
1057
1117
|
z.object({
|
|
1058
1118
|
id: z.string(),
|
|
1059
1119
|
organizationId: z.string(),
|
|
@@ -1159,12 +1219,26 @@ const githubDevCallbackQuerySchema = z.object({
|
|
|
1159
1219
|
* 2. Add a key to `ORG_SETTINGS_NAMESPACES`.
|
|
1160
1220
|
* 3. Done. No DB migration, no new API route.
|
|
1161
1221
|
*/
|
|
1222
|
+
const httpsRepoUrlSchema = z.string().url().refine((value) => {
|
|
1223
|
+
try {
|
|
1224
|
+
return new URL(value).protocol === "https:";
|
|
1225
|
+
} catch {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
}, { message: "Repo URL must use HTTPS." }).refine((value) => {
|
|
1229
|
+
try {
|
|
1230
|
+
const url = new URL(value);
|
|
1231
|
+
return url.username.length === 0 && url.password.length === 0;
|
|
1232
|
+
} catch {
|
|
1233
|
+
return false;
|
|
1234
|
+
}
|
|
1235
|
+
}, { message: "Repo URL must not include credentials." });
|
|
1162
1236
|
const orgContextTreeStorageSchema = z.object({
|
|
1163
|
-
repo:
|
|
1237
|
+
repo: httpsRepoUrlSchema.optional(),
|
|
1164
1238
|
branch: z.string().default("main")
|
|
1165
1239
|
});
|
|
1166
1240
|
const orgContextTreeInputSchema = z.object({
|
|
1167
|
-
repo:
|
|
1241
|
+
repo: httpsRepoUrlSchema.nullish(),
|
|
1168
1242
|
branch: z.string().min(1).nullish()
|
|
1169
1243
|
});
|
|
1170
1244
|
const orgContextTreeOutputSchema = z.object({
|
|
@@ -1177,16 +1251,36 @@ const orgGithubIntegrationOutputSchema = z.object({
|
|
|
1177
1251
|
webhookSecretConfigured: z.boolean(),
|
|
1178
1252
|
webhookUrl: z.string()
|
|
1179
1253
|
});
|
|
1254
|
+
const orgSourceReposStorageSchema = z.object({ repos: z.array(z.object({
|
|
1255
|
+
url: httpsRepoUrlSchema,
|
|
1256
|
+
defaultBranch: z.string().optional()
|
|
1257
|
+
})).default([]) });
|
|
1258
|
+
const orgSourceReposInputSchema = z.object({ repos: z.array(z.object({
|
|
1259
|
+
url: httpsRepoUrlSchema,
|
|
1260
|
+
defaultBranch: z.string().min(1).optional()
|
|
1261
|
+
})).optional() });
|
|
1262
|
+
const orgSourceReposOutputSchema = z.object({ repos: z.array(z.object({
|
|
1263
|
+
url: z.string(),
|
|
1264
|
+
defaultBranch: z.string().optional()
|
|
1265
|
+
})) });
|
|
1180
1266
|
const ORG_SETTINGS_NAMESPACES = {
|
|
1181
1267
|
context_tree: {
|
|
1182
1268
|
storage: orgContextTreeStorageSchema,
|
|
1183
1269
|
input: orgContextTreeInputSchema,
|
|
1184
|
-
output: orgContextTreeOutputSchema
|
|
1270
|
+
output: orgContextTreeOutputSchema,
|
|
1271
|
+
readPolicy: "member"
|
|
1185
1272
|
},
|
|
1186
1273
|
github_integration: {
|
|
1187
1274
|
storage: orgGithubIntegrationStorageSchema,
|
|
1188
1275
|
input: orgGithubIntegrationInputSchema,
|
|
1189
|
-
output: orgGithubIntegrationOutputSchema
|
|
1276
|
+
output: orgGithubIntegrationOutputSchema,
|
|
1277
|
+
readPolicy: "admin"
|
|
1278
|
+
},
|
|
1279
|
+
source_repos: {
|
|
1280
|
+
storage: orgSourceReposStorageSchema,
|
|
1281
|
+
input: orgSourceReposInputSchema,
|
|
1282
|
+
output: orgSourceReposOutputSchema,
|
|
1283
|
+
readPolicy: "member"
|
|
1190
1284
|
}
|
|
1191
1285
|
};
|
|
1192
1286
|
const ORG_SETTINGS_NAMESPACE_KEYS = Object.keys(ORG_SETTINGS_NAMESPACES);
|
|
@@ -1228,6 +1322,87 @@ z.object({
|
|
|
1228
1322
|
organizationId: z.string(),
|
|
1229
1323
|
agents: z.record(z.string(), z.array(pulseBucketSchema).length(32))
|
|
1230
1324
|
});
|
|
1325
|
+
/**
|
|
1326
|
+
* Structured ask-user payloads bridged from the Claude Agent SDK
|
|
1327
|
+
* `AskUserQuestion` tool to Hub messages.
|
|
1328
|
+
*
|
|
1329
|
+
* Shape mirrors the SDK 0.2.84 input/output verbatim so the client
|
|
1330
|
+
* runtime adapter can pass `updatedInput` straight through. See
|
|
1331
|
+
* verify scripts under `packages/client/tmp-verify/` for the live
|
|
1332
|
+
* matrix this was validated against.
|
|
1333
|
+
*
|
|
1334
|
+
* Lifecycle:
|
|
1335
|
+
* 1. Agent emits a `format: "question"` message — its `content` is a
|
|
1336
|
+
* `QuestionMessageContent` carrying `correlationId` + `questions[]`.
|
|
1337
|
+
* 2. User picks options in the Web UI and POSTs answers; server writes
|
|
1338
|
+
* a `format: "question_answer"` message — its `content` is a
|
|
1339
|
+
* `QuestionAnswerMessageContent` referencing the same `correlationId`.
|
|
1340
|
+
* 3. Client runtime resolves the in-flight `canUseTool` promise with the
|
|
1341
|
+
* answers, and the SDK feeds them back to the model.
|
|
1342
|
+
*
|
|
1343
|
+
* `pending → answered → superseded` runtime status lives in a separate
|
|
1344
|
+
* server table (`pending_questions`) and is not part of the message —
|
|
1345
|
+
* messages are immutable once written.
|
|
1346
|
+
*/
|
|
1347
|
+
/**
|
|
1348
|
+
* Single option inside a question. `preview` is rich content rendered above
|
|
1349
|
+
* the label — the SDK's tool input emits it as `string | undefined` (the
|
|
1350
|
+
* field is omitted when the model didn't generate any preview content), so
|
|
1351
|
+
* we accept undefined / null / string and normalise downstream renderers
|
|
1352
|
+
* to treat all three the same way.
|
|
1353
|
+
*/
|
|
1354
|
+
const questionOptionSchema = z.object({
|
|
1355
|
+
label: z.string().min(1),
|
|
1356
|
+
description: z.string(),
|
|
1357
|
+
preview: z.string().nullable().optional()
|
|
1358
|
+
});
|
|
1359
|
+
/**
|
|
1360
|
+
* One question. `header` is a chip-style short tag. The SDK schema docs
|
|
1361
|
+
* describe ≤12 chars but in practice the model occasionally emits
|
|
1362
|
+
* slightly longer headers; we keep the rule loose (≤24) so a stylistic
|
|
1363
|
+
* regression doesn't fail-closed at canUseTool and abandon the entire
|
|
1364
|
+
* tool call. The UI truncates visually if needed.
|
|
1365
|
+
*/
|
|
1366
|
+
const questionItemSchema = z.object({
|
|
1367
|
+
question: z.string().min(1),
|
|
1368
|
+
header: z.string().min(1).max(24),
|
|
1369
|
+
options: z.array(questionOptionSchema).min(2).max(4),
|
|
1370
|
+
multiSelect: z.boolean()
|
|
1371
|
+
});
|
|
1372
|
+
/** Session-level preview format hint. Mirrors `toolConfig.askUserQuestion.previewFormat`. */
|
|
1373
|
+
const questionPreviewFormatSchema = z.enum(["html", "markdown"]).nullable();
|
|
1374
|
+
/**
|
|
1375
|
+
* Content payload for a message whose `format === "question"`.
|
|
1376
|
+
*
|
|
1377
|
+
* `correlationId` ties the question to its eventual answer message AND to the
|
|
1378
|
+
* server-side `pending_questions` row; client runtime uses it to resolve the
|
|
1379
|
+
* waiting `canUseTool` promise. Reuse the SDK `tool_use_id` when available so
|
|
1380
|
+
* a single id flows end-to-end.
|
|
1381
|
+
*/
|
|
1382
|
+
const questionMessageContentSchema = z.object({
|
|
1383
|
+
correlationId: z.string().min(1),
|
|
1384
|
+
questions: z.array(questionItemSchema).min(1).max(4),
|
|
1385
|
+
previewFormat: questionPreviewFormatSchema,
|
|
1386
|
+
allowFreeText: z.boolean()
|
|
1387
|
+
});
|
|
1388
|
+
/**
|
|
1389
|
+
* Content payload for a message whose `format === "question_answer"`.
|
|
1390
|
+
*
|
|
1391
|
+
* `answers` is keyed by `QuestionItem.question` text. For `multiSelect` questions
|
|
1392
|
+
* the value is a `, `-joined string of selected labels (matches SDK convention).
|
|
1393
|
+
* For free-text answers the value is the user's raw input.
|
|
1394
|
+
*/
|
|
1395
|
+
const questionAnswerMessageContentSchema = z.object({
|
|
1396
|
+
correlationId: z.string().min(1),
|
|
1397
|
+
answers: z.record(z.string().min(1), z.string())
|
|
1398
|
+
});
|
|
1399
|
+
/** Submit-answer request body for `POST /api/admin/questions/:correlationId/answer`. */
|
|
1400
|
+
const submitQuestionAnswerSchema = z.object({ answers: z.record(z.string().min(1), z.string()) });
|
|
1401
|
+
z.enum([
|
|
1402
|
+
"pending",
|
|
1403
|
+
"answered",
|
|
1404
|
+
"superseded"
|
|
1405
|
+
]);
|
|
1231
1406
|
const sessionEventKind = z.enum([
|
|
1232
1407
|
"tool_call",
|
|
1233
1408
|
"error",
|
|
@@ -1521,4 +1696,4 @@ z.object({
|
|
|
1521
1696
|
capabilities: serverCapabilitiesSchema.optional()
|
|
1522
1697
|
}).passthrough();
|
|
1523
1698
|
//#endregion
|
|
1524
|
-
export { loginSchema as $, createAgentSchema as A, githubCallbackQuerySchema as B, agentTypeSchema as C,
|
|
1699
|
+
export { loginSchema as $, createAgentSchema as A, updateTaskStatusSchema as At, githubCallbackQuerySchema as B, agentTypeSchema as C, updateAdapterConfigSchema as Ct, contextTreeSnapshotSchema as D, updateClientCapabilitiesSchema as Dt, connectTokenExchangeSchema as E, updateChatSchema 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, updateMemberSchema as Ot, createOrgFromMeSchema as P, listMeChatsQuerySchema as Q, dryRunAgentRuntimeConfigSchema as R, agentRuntimeConfigPayloadSchema as S, taskListQuerySchema as St, clientRegisterSchema as T, updateAgentSchema as Tt, imageInlineContentSchema as U, githubDevCallbackQuerySchema as V, inboxAckFrameSchema as W, joinByInvitationSchema as X, isReservedAgentName as Y, linkTaskChatSchema as Z, addParticipantSchema as _, sessionEventSchema as _t, AGENT_STATUSES as a, questionAnswerMessageContentSchema as at, agentBindRequestSchema as b, stripCode as bt, DEFAULT_RUNTIME_PROVIDER as c, refreshTokenSchema as ct, TASK_CREATOR_TYPES as d, scanMentionTokens as dt, messageSourceSchema as et, TASK_HEALTH_SIGNALS as f, selfServiceFeishuBotSchema as ft, addMeChatParticipantsSchema as g, sessionEventMessageSchema as gt, WS_AUTH_FRAME_TIMEOUT_MS as h, sessionCompletionMessageSchema as ht, AGENT_SOURCES as i, patchOnboardingSchema as it, createChatSchema as j, wsAuthFrameSchema as jt, createAdapterMappingSchema as k, updateOrganizationSchema as kt, MENTION_REGEX as l, runtimeStateMessageSchema as lt, TASK_TERMINAL_STATUSES as m, sendToAgentSchema as mt, AGENT_NAME_REGEX as n, onboardingEventSchema as nt, AGENT_TYPES as o, questionMessageContentSchema as ot, TASK_STATUSES as p, sendMessageSchema as pt, isOrgSettingNamespace as q, AGENT_SELECTOR_HEADER as r, paginationQuerySchema as rt, AGENT_VISIBILITY as s, rebindAgentSchema as st, AGENT_BIND_REJECT_REASONS as t, notificationQuerySchema as tt, ORG_SETTINGS_NAMESPACES as u, safeRedirectPath as ut, adminCreateTaskSchema as v, sessionReconcileRequestSchema as vt, clientCapabilitiesSchema as w, updateAgentRuntimeConfigSchema as wt, agentPinnedMessageSchema as x, submitQuestionAnswerSchema as xt, adminUpdateTaskSchema as y, sessionStateMessageSchema as yt, extractMentions as z };
|
|
@@ -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;
|