@crossworks/client-types 0.232.127 → 0.232.140
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/package.json +1 -1
- package/src/dto/agent-graph.ts +229 -0
- package/src/dto/agents.ts +190 -0
- package/src/dto/comms.ts +114 -0
- package/src/dto/heartbeats.ts +60 -0
- package/src/dto/recall.ts +21 -0
- package/src/dto/rows.ts +882 -0
- package/src/dto/turns.ts +192 -0
- package/src/dto/views.ts +936 -0
- package/src/index.ts +15 -2534
- package/src/model-pools-data.json +2194 -0
- package/src/model-pools-data.ts +10 -2194
- package/src/model-pools-template.test.ts +77 -0
package/src/dto/views.ts
ADDED
|
@@ -0,0 +1,936 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mantle/client-types · views
|
|
3
|
+
*
|
|
4
|
+
* Server-lib view/query DTOs — the shapes screens read, kept here so a
|
|
5
|
+
* client component never reaches into @server/*.
|
|
6
|
+
*
|
|
7
|
+
* Split out of the 2548-line index.ts on 2026-09-02 (audit, tier 3) with the
|
|
8
|
+
* contents unchanged. index.ts re-exports every one of these, so the package's
|
|
9
|
+
* public surface is byte-identical — only the file a symbol lives in moved.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { AuditSeverity, SystemReport } from '../types/integrity';
|
|
13
|
+
import type { TraceDetail } from '../traces-format';
|
|
14
|
+
import type { PersonaNoteDTO } from './agents';
|
|
15
|
+
import type { ContextSnapshot, ConversationAttachment } from './rows';
|
|
16
|
+
|
|
17
|
+
// ── Server-lib view/query DTOs (jackdaw split P0 follow-up: @server/* purge) ──
|
|
18
|
+
// Moved from server/web/lib/* and @mantle/content; the originals re-export
|
|
19
|
+
// these names so server import paths are unchanged.
|
|
20
|
+
|
|
21
|
+
/** Sort order for the pages list. 'edited' (last updated) is the default. */
|
|
22
|
+
export type PageSort = 'edited' | 'newest' | 'oldest' | 'title';
|
|
23
|
+
|
|
24
|
+
/** A node that links TO a given page — one inbound `references` edge, resolved
|
|
25
|
+
* to its source node. Powers the "Referenced by" panel. */
|
|
26
|
+
export type Backlink = {
|
|
27
|
+
id: string;
|
|
28
|
+
title: string;
|
|
29
|
+
/** The source node's type (wire truth: the db enum widens to string here). */
|
|
30
|
+
type: string;
|
|
31
|
+
icon: string | null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type CapacityZone = 'green' | 'watch' | 'split';
|
|
35
|
+
|
|
36
|
+
export type CapacityMetric = {
|
|
37
|
+
count: number;
|
|
38
|
+
watch: number;
|
|
39
|
+
split: number;
|
|
40
|
+
/** count / split — may exceed 1 when the split point is passed. */
|
|
41
|
+
ratio: number;
|
|
42
|
+
zone: CapacityZone;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type BrainCapacity = {
|
|
46
|
+
docs: CapacityMetric;
|
|
47
|
+
chunkVectors: CapacityMetric;
|
|
48
|
+
/** Worst zone across both axes — the brain's headline state. */
|
|
49
|
+
zone: CapacityZone;
|
|
50
|
+
/** Worst-axis fill as an integer percentage of the split budget (may exceed 100). */
|
|
51
|
+
pctOfSplit: number;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type AgentContext = {
|
|
55
|
+
agentId: string;
|
|
56
|
+
agentName: string | null;
|
|
57
|
+
agentSlug: string | null;
|
|
58
|
+
modelSlug: string;
|
|
59
|
+
lastTokensIn: number;
|
|
60
|
+
contextLimit: number | null;
|
|
61
|
+
/** Where contextLimit came from: live OpenRouter data, the static
|
|
62
|
+
* fallback, or unknown (slug not in either). Surfaced in the UI. */
|
|
63
|
+
contextSource: ContextSource;
|
|
64
|
+
pct: number | null;
|
|
65
|
+
lastRunAt: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type SpendRange = 'day' | 'week' | 'month';
|
|
69
|
+
|
|
70
|
+
export type AgentSpend = {
|
|
71
|
+
agentId: string | null;
|
|
72
|
+
agentName: string | null;
|
|
73
|
+
agentSlug: string | null;
|
|
74
|
+
costMicroUsd: number;
|
|
75
|
+
tokensIn: number;
|
|
76
|
+
tokensOut: number;
|
|
77
|
+
cacheReadTokens: number;
|
|
78
|
+
runs: number;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type ModelSpend = {
|
|
82
|
+
/** The OpenRouter model slug captured in trace_steps.meta.model. */
|
|
83
|
+
model: string;
|
|
84
|
+
costMicroUsd: number;
|
|
85
|
+
tokensIn: number;
|
|
86
|
+
tokensOut: number;
|
|
87
|
+
cacheReadTokens: number;
|
|
88
|
+
calls: number;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type DailySpend = {
|
|
92
|
+
/** ISO date (YYYY-MM-DD) in the server's local timezone. */
|
|
93
|
+
day: string;
|
|
94
|
+
costMicroUsd: number;
|
|
95
|
+
tokensIn: number;
|
|
96
|
+
tokensOut: number;
|
|
97
|
+
cacheReadTokens: number;
|
|
98
|
+
runs: number;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export type RecentFailure = {
|
|
102
|
+
id: string;
|
|
103
|
+
kind: string;
|
|
104
|
+
startedAt: string;
|
|
105
|
+
error: string;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type TopError = {
|
|
109
|
+
message: string;
|
|
110
|
+
count: number;
|
|
111
|
+
lastAt: string;
|
|
112
|
+
lastTraceId: string;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/** Per-tool tallies of calls the central validator flagged. Clean calls
|
|
116
|
+
* write no `arg_validation` meta at all, so these are problem counts,
|
|
117
|
+
* not rates — an empty result means nothing was flagged, not no calls. */
|
|
118
|
+
export type ToolValidationAgg = {
|
|
119
|
+
tool: string;
|
|
120
|
+
flaggedCalls: number;
|
|
121
|
+
withRepairs: number;
|
|
122
|
+
withUnknownKeys: number;
|
|
123
|
+
withViolations: number;
|
|
124
|
+
lastAt: string;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export type ToolValidationEvent = {
|
|
128
|
+
stepId: string;
|
|
129
|
+
traceId: string;
|
|
130
|
+
tool: string;
|
|
131
|
+
mode: string;
|
|
132
|
+
repairs: Array<{ key: string; kind: string; note: string }>;
|
|
133
|
+
unknownKeys: Array<{ key: string; suggestion: string | null }>;
|
|
134
|
+
violations: string[];
|
|
135
|
+
startedAt: string;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export type AgentActivityRow = {
|
|
139
|
+
id: string;
|
|
140
|
+
slug: string;
|
|
141
|
+
name: string;
|
|
142
|
+
role: string;
|
|
143
|
+
model: string;
|
|
144
|
+
priority: number;
|
|
145
|
+
enabled: boolean;
|
|
146
|
+
lastUsedAt: string | null;
|
|
147
|
+
usageCount: number;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export type ChatRow = {
|
|
151
|
+
id: string;
|
|
152
|
+
title: string | null;
|
|
153
|
+
username: string | null;
|
|
154
|
+
telegramChatId: string;
|
|
155
|
+
allowlistStatus: string;
|
|
156
|
+
totalTurns: number;
|
|
157
|
+
digested: number;
|
|
158
|
+
undigested: number;
|
|
159
|
+
lastActivity: string | null;
|
|
160
|
+
responderAgentId: string | null;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export type PersonaNotesRow = {
|
|
164
|
+
agentId: string;
|
|
165
|
+
agentName: string;
|
|
166
|
+
agentSlug: string;
|
|
167
|
+
notes: PersonaNoteDTO[];
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export type ContentIndexCoverage = {
|
|
171
|
+
total: number;
|
|
172
|
+
indexed: number;
|
|
173
|
+
byType: Array<{ type: string; total: number; indexed: number }>;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Awareness of duplicate graph edges. Going forward the extractor rebuilds
|
|
178
|
+
* edges per node (idempotent), but content re-edited *before* that fix may
|
|
179
|
+
* carry historical duplicate `mentioned_in` / `references` rows. This surfaces
|
|
180
|
+
* the count + a few labelled samples so the operator knows to run
|
|
181
|
+
* `pnpm dedupe:edges`. Read-only — cleaning stays the deliberate CLI tool.
|
|
182
|
+
*/
|
|
183
|
+
export type DuplicateEdgeStats = {
|
|
184
|
+
groups: number; // logical edges with >1 row
|
|
185
|
+
redundant: number; // rows that could be removed (sum of count-1)
|
|
186
|
+
samples: { relation: string; label: string; count: number }[];
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/** One responder turn: the question, the retrieval snapshot the turn's
|
|
190
|
+
* 'load_context' trace step persisted (null for pre-instrumentation turns),
|
|
191
|
+
* and the outbound reply. See ContextSnapshot in @mantle/runtime/agent. */
|
|
192
|
+
/** Mirrors @mantle/tracing `ContextSource`. */
|
|
193
|
+
export type ContextSource = 'live' | 'fallback' | 'unknown';
|
|
194
|
+
|
|
195
|
+
export type ContextTurnRow = {
|
|
196
|
+
traceId: string;
|
|
197
|
+
startedAt: string;
|
|
198
|
+
status: string;
|
|
199
|
+
surface: string | null;
|
|
200
|
+
agentSlug: string | null;
|
|
201
|
+
model: string | null;
|
|
202
|
+
durationMs: number | null;
|
|
203
|
+
question: string | null;
|
|
204
|
+
snapshot: ContextSnapshot | null;
|
|
205
|
+
response: string | null;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export type DigestRow = {
|
|
209
|
+
id: string;
|
|
210
|
+
title: string;
|
|
211
|
+
createdAt: string;
|
|
212
|
+
/** All fields below are pulled out of nodes.data (jsonb). */
|
|
213
|
+
chatId: string;
|
|
214
|
+
telegramChatId: string | null;
|
|
215
|
+
periodStart: string;
|
|
216
|
+
periodEnd: string;
|
|
217
|
+
sourceTurnCount: number;
|
|
218
|
+
model: string;
|
|
219
|
+
agent: string;
|
|
220
|
+
summary: string;
|
|
221
|
+
topic: string | null;
|
|
222
|
+
topicSlug: string | null;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export type FactRow = {
|
|
226
|
+
id: string;
|
|
227
|
+
content: string;
|
|
228
|
+
kind: string;
|
|
229
|
+
confidence: number;
|
|
230
|
+
entityName: string | null;
|
|
231
|
+
entityKind: string | null;
|
|
232
|
+
sourceNodeId: string | null;
|
|
233
|
+
sourceTitle: string | null;
|
|
234
|
+
createdAt: string;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export type TopicRow = {
|
|
238
|
+
topic: string;
|
|
239
|
+
topicSlug: string;
|
|
240
|
+
digestCount: number;
|
|
241
|
+
turnCount: number;
|
|
242
|
+
firstSeen: string;
|
|
243
|
+
lastSeen: string;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
/** One key/count bucket in the corpus histograms below. */
|
|
247
|
+
export type Bucket = { key: string; count: number };
|
|
248
|
+
|
|
249
|
+
export type BrainCounts = {
|
|
250
|
+
nodesTotal: number;
|
|
251
|
+
nodesByType: Bucket[];
|
|
252
|
+
factsTotal: number;
|
|
253
|
+
factsByKind: Bucket[];
|
|
254
|
+
entitiesTotal: number;
|
|
255
|
+
entitiesByKind: Bucket[];
|
|
256
|
+
edgesTotal: number;
|
|
257
|
+
edgesByRelation: Bucket[];
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/** A health check, not a fixer. Counts active edges that share the same
|
|
261
|
+
* (source, target, relation) — i.e. duplicates. The extractor's
|
|
262
|
+
* delete-then-rebuild discipline (see architecture §9k) means this should
|
|
263
|
+
* stay 0; a non-zero value flags a regression in edge writing. The remedy is
|
|
264
|
+
* the one-shot `pnpm dedupe:edges --apply`, NOT a recurring auto-clean (which
|
|
265
|
+
* would mask the regression). */
|
|
266
|
+
export type GraphIntegrity = {
|
|
267
|
+
/** Distinct (source, target, relation) groups with more than one row. */
|
|
268
|
+
duplicateEdgeGroups: number;
|
|
269
|
+
/** Total redundant rows across those groups (Σ count-1) — how many
|
|
270
|
+
* `dedupe:edges --apply` would remove. */
|
|
271
|
+
redundantEdgeRows: number;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export type VectorCounts = {
|
|
275
|
+
nodesIndexed: number;
|
|
276
|
+
nodesTotal: number;
|
|
277
|
+
factsIndexed: number;
|
|
278
|
+
factsTotal: number;
|
|
279
|
+
entitiesIndexed: number;
|
|
280
|
+
entitiesTotal: number;
|
|
281
|
+
/** The headline: total embedded vectors across nodes + facts + entities. */
|
|
282
|
+
vectorsTotal: number;
|
|
283
|
+
/** Global content-addressed embedding cache (not owner-scoped). */
|
|
284
|
+
embeddingCacheRows: number;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export type EmailStats = {
|
|
288
|
+
total: number;
|
|
289
|
+
unread: number;
|
|
290
|
+
withAttachments: number;
|
|
291
|
+
byAccount: { accountId: string; address: string; total: number; unread: number }[];
|
|
292
|
+
latestSync: {
|
|
293
|
+
accountId: string;
|
|
294
|
+
address: string;
|
|
295
|
+
status: string;
|
|
296
|
+
finishedAt: string | null;
|
|
297
|
+
ingested: number;
|
|
298
|
+
scanned: number;
|
|
299
|
+
error: string | null;
|
|
300
|
+
}[];
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export type HeartbeatStats = {
|
|
304
|
+
byStatus: Bucket[];
|
|
305
|
+
recentFiresByDisposition: Bucket[];
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
export type TelegramStats = {
|
|
309
|
+
messagesTotal: number;
|
|
310
|
+
unprocessed: number;
|
|
311
|
+
chatsByStatus: Bucket[];
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export type IngestDay = {
|
|
315
|
+
day: string; // YYYY-MM-DD
|
|
316
|
+
total: number;
|
|
317
|
+
byType: Record<string, number>;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/** One model as shown in the explorer. Normalised fields are best-effort
|
|
321
|
+
* (absent when the provider's API doesn't return them); `raw` is always the
|
|
322
|
+
* untouched object the API gave us. */
|
|
323
|
+
export type ExplorerModel = {
|
|
324
|
+
/** Provider model id / slug (e.g. 'anthropic/claude-sonnet-4.6'). */
|
|
325
|
+
id: string;
|
|
326
|
+
/** Friendly display name if the API provides one. */
|
|
327
|
+
name?: string;
|
|
328
|
+
description?: string;
|
|
329
|
+
/** Total context window in tokens. */
|
|
330
|
+
contextTokens?: number;
|
|
331
|
+
/** Max output/completion tokens, when stated separately. */
|
|
332
|
+
maxOutputTokens?: number;
|
|
333
|
+
/** USD per 1M input (prompt) tokens. 0 means free; undefined means unknown. */
|
|
334
|
+
inputPricePerM?: number;
|
|
335
|
+
/** USD per 1M output (completion) tokens. */
|
|
336
|
+
outputPricePerM?: number;
|
|
337
|
+
/** Other priced dimensions the API exposes, surfaced verbatim. */
|
|
338
|
+
extraPricing?: { label: string; value: string }[];
|
|
339
|
+
/** e.g. 'text+image→text'. */
|
|
340
|
+
modality?: string;
|
|
341
|
+
/** Coarse type: chat | embedding | image | tts | stt | rerank | other. */
|
|
342
|
+
kind?: string;
|
|
343
|
+
/** Release/creation time as ISO, when provided. */
|
|
344
|
+
created?: string;
|
|
345
|
+
/** The provider's untouched model object. */
|
|
346
|
+
raw: unknown;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
export type ModelSort = 'name' | 'context' | 'input' | 'output' | 'created';
|
|
350
|
+
|
|
351
|
+
export type StudioNode = {
|
|
352
|
+
/** Stable canvas id, namespaced by kind: `agent:<slug>` / `skill:<slug>`. */
|
|
353
|
+
id: string;
|
|
354
|
+
kind: StudioNodeKind;
|
|
355
|
+
slug: string;
|
|
356
|
+
label: string;
|
|
357
|
+
/** Secondary line — model for agents, tool-count for skills. */
|
|
358
|
+
sublabel: string;
|
|
359
|
+
enabled: boolean;
|
|
360
|
+
isPersona: boolean;
|
|
361
|
+
/** Node-local referential problems (dangling tool/skill/delegate, disabled). */
|
|
362
|
+
issues: string[];
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
export type StudioEdge = {
|
|
366
|
+
id: string;
|
|
367
|
+
source: string;
|
|
368
|
+
target: string;
|
|
369
|
+
kind: 'skill' | 'delegate' | 'group';
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export type NodeBiographyView = {
|
|
373
|
+
node: {
|
|
374
|
+
id: string;
|
|
375
|
+
type: string;
|
|
376
|
+
title: string;
|
|
377
|
+
path: string;
|
|
378
|
+
tags: string[];
|
|
379
|
+
createdAt: string;
|
|
380
|
+
updatedAt: string;
|
|
381
|
+
/** First N chars of the summary the extractor wrote — null if
|
|
382
|
+
* the extractor hasn't run (or refused to). */
|
|
383
|
+
summary: string | null;
|
|
384
|
+
/** True when the node has an embedding vector — second half of
|
|
385
|
+
* the "is this node ready for retrieval?" check. */
|
|
386
|
+
hasEmbedding: boolean;
|
|
387
|
+
/** Bytes of the content field (text-shaped nodes) or 0
|
|
388
|
+
* otherwise. Useful for "did extractor skip because body too
|
|
389
|
+
* short?" debugging. */
|
|
390
|
+
contentChars: number;
|
|
391
|
+
/** First 4KB of content. Lets the biography page show a quick
|
|
392
|
+
* preview of what was actually saved. */
|
|
393
|
+
contentPreview: string | null;
|
|
394
|
+
/** The data jsonb truncated and key-summarised so we don't blow
|
|
395
|
+
* up the page rendering a 1MB blob inline. */
|
|
396
|
+
dataKeys: string[];
|
|
397
|
+
};
|
|
398
|
+
/** Traces in chronological order (oldest first). Operators read
|
|
399
|
+
* these top-to-bottom as a story: ingest → extractor → ... */
|
|
400
|
+
traces: TraceDetail[];
|
|
401
|
+
stats: {
|
|
402
|
+
totalTraces: number;
|
|
403
|
+
totalCostMicroUsd: number;
|
|
404
|
+
totalTokensIn: number;
|
|
405
|
+
totalTokensOut: number;
|
|
406
|
+
/** ISO timestamp of the earliest trace touching this node, or
|
|
407
|
+
* the node's own createdAt if there are no traces. */
|
|
408
|
+
firstSeen: string;
|
|
409
|
+
/** ISO timestamp of the most recent trace. Equal to firstSeen
|
|
410
|
+
* when there's only one. */
|
|
411
|
+
lastTouched: string;
|
|
412
|
+
/** Counts by kind + status for the header chips. */
|
|
413
|
+
byKind: Record<string, number>;
|
|
414
|
+
byStatus: Record<string, number>;
|
|
415
|
+
};
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
export type AssistantAgentOption = {
|
|
419
|
+
id: string;
|
|
420
|
+
slug: string;
|
|
421
|
+
name: string;
|
|
422
|
+
role: string;
|
|
423
|
+
model: string;
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
export type AssistantTimelineRow = {
|
|
427
|
+
id: string;
|
|
428
|
+
direction: 'inbound' | 'outbound';
|
|
429
|
+
text: string;
|
|
430
|
+
model: string | null;
|
|
431
|
+
/** Transport the turn arrived/left on — drives the channel badge in the UI.
|
|
432
|
+
* 'web' for native /assistant turns; 'telegram' (etc.) for turns that came
|
|
433
|
+
* in on another surface and now show in the unified stream. */
|
|
434
|
+
channel: string;
|
|
435
|
+
/** Execution state (migration 0105). 'complete' for every historical/inbound
|
|
436
|
+
* row; an outbound row is 'pending' while the durable runner works and
|
|
437
|
+
* 'failed' if it errored — so a reload mid-turn renders a live "thinking…"
|
|
438
|
+
* bubble (or the error) instead of nothing. See docs/live-turn-streaming.md. */
|
|
439
|
+
status: 'pending' | 'complete' | 'failed';
|
|
440
|
+
/** Human-readable failure reason for a 'failed' turn; null otherwise. */
|
|
441
|
+
error: string | null;
|
|
442
|
+
/** Persisted media (images, voice notes, docs) so the turn renders its
|
|
443
|
+
* attachments on load — no bytes, just node/file references. */
|
|
444
|
+
attachments: ConversationAttachment[];
|
|
445
|
+
/** Persisted thought trail (grounded action labels), present on an outbound
|
|
446
|
+
* row when the brain has trail-persistence on — lets the "Thought process"
|
|
447
|
+
* record survive a reload. Undefined when not persisted. */
|
|
448
|
+
thoughts?: Array<{ kind: string; label: string; elapsedMs?: number }>;
|
|
449
|
+
/** Deterministic tool-outcome tally for the turn — the runtime's own
|
|
450
|
+
* ledger, persisted at finalize. Drives the "N tool calls · M failed"
|
|
451
|
+
* footer so the record is independent of the reply's claims. */
|
|
452
|
+
toolStats?: ToolOutcomeStatsRow;
|
|
453
|
+
/** True when this row belongs to a superseded (replaced) turn pair — the
|
|
454
|
+
* user cancelled the turn mid-stream and re-sent original + correction as
|
|
455
|
+
* one combined turn (data.superseded_by). The pair stays in the transcript,
|
|
456
|
+
* rendered dimmed with a "replaced" tag; prompt history and digests skip it. */
|
|
457
|
+
superseded?: boolean;
|
|
458
|
+
createdAt: string;
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
export type TestApiKeyResult = {
|
|
462
|
+
ok: boolean;
|
|
463
|
+
/** One-line summary for the UI — e.g. '13 models accessible' or
|
|
464
|
+
* 'OpenAI rejected the key (401)'. */
|
|
465
|
+
message: string;
|
|
466
|
+
/** Provider label for the result line. Empty when we can't resolve the
|
|
467
|
+
* provider from the key's service. */
|
|
468
|
+
provider: string;
|
|
469
|
+
/** Which adapter ran the probe ('openai-tts', 'anthropic-chat', …). */
|
|
470
|
+
adapter: string;
|
|
471
|
+
/** Number of models accessible to this key, if discovery succeeded. */
|
|
472
|
+
modelsFound?: number;
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
export type ComposeStatus = {
|
|
476
|
+
state: ComposeState;
|
|
477
|
+
/** The updater's last refresh outcome verbatim (e.g. 'refreshed',
|
|
478
|
+
* 'modified', 'no-baseline', 'unavailable'), for the details view. */
|
|
479
|
+
refresh: string | null;
|
|
480
|
+
/** The CLIENT stack's compose (v0.200 split). 'absent' state = a
|
|
481
|
+
* server-only box (no docker-compose.client.yml — nothing to drift). */
|
|
482
|
+
client: { state: ComposeState | 'absent'; refresh: string | null };
|
|
483
|
+
/** The updater sidecar's own script (v0.206+). Before the self-refresh
|
|
484
|
+
* landed this was the silent failure: a stale script rolled the server
|
|
485
|
+
* stack, reported ok, and skipped the client stack with no error anywhere.
|
|
486
|
+
* 'unknown' on any box still running that script — it reports no sha. */
|
|
487
|
+
updater: { state: UpdaterScriptState; refresh: string | null };
|
|
488
|
+
/** The front door (v0.232.126+): infra/caddy/Caddyfile is release-owned and
|
|
489
|
+
* refreshed by the updater like compose. 'unknown' on a box whose updater
|
|
490
|
+
* predates the field. A 'modified' Caddyfile means release-level front-door
|
|
491
|
+
* changes are not arriving there; box routes belong in conf.d/. */
|
|
492
|
+
caddy: { state: ComposeState; refresh: string | null };
|
|
493
|
+
/** The operator scripts (v0.232.137+): db-dump, db-restore, sanity,
|
|
494
|
+
* compose-adopt, uninstall and the install.sh configurator, fingerprinted
|
|
495
|
+
* as ONE set. Nothing refreshed these before, so a box ran the copies it
|
|
496
|
+
* was installed with forever — jason-prod applied a compose binding
|
|
497
|
+
* infra/caddy/{shapes,conf.d} with a compose-adopt.sh that knew about
|
|
498
|
+
* neither. Classified like the updater script rather than like compose: an
|
|
499
|
+
* absent baseline reads 'stale' because the refresh adopts it by itself,
|
|
500
|
+
* and only a baseline that EXISTS and disagrees ('modified') needs a
|
|
501
|
+
* human. 'unknown' on a box whose updater predates the field. */
|
|
502
|
+
scripts: { state: UpdaterScriptState; refresh: string | null };
|
|
503
|
+
checkedAt: string | null;
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
export type UpdateCheck = {
|
|
507
|
+
currentVersion: string;
|
|
508
|
+
latest: ReleaseInfo | null;
|
|
509
|
+
updateAvailable: boolean;
|
|
510
|
+
checkedAt: string;
|
|
511
|
+
/** Set when the check itself failed (network, rate limit, no releases yet). */
|
|
512
|
+
error: string | null;
|
|
513
|
+
/** The owner-UI (jackdaw) release stream — versioned separately since the
|
|
514
|
+
* repo split. `latest` is jackdaw's newest release; `pairedTag` is the
|
|
515
|
+
* client tag THIS server release was tested with (from the release-pair
|
|
516
|
+
* file baked into the image). The server cannot know which client build a
|
|
517
|
+
* browser is running, so "is an interface update available" is computed by
|
|
518
|
+
* the client itself against its own APP_VERSION. Absent on servers that
|
|
519
|
+
* predate the field. */
|
|
520
|
+
client?: {
|
|
521
|
+
latest: ReleaseInfo | null;
|
|
522
|
+
pairedTag: string | null;
|
|
523
|
+
error: string | null;
|
|
524
|
+
} | null;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
export type UpdaterStatus = {
|
|
528
|
+
phase: UpdaterPhase;
|
|
529
|
+
target: string;
|
|
530
|
+
startedAt: string | null;
|
|
531
|
+
finishedAt: string | null;
|
|
532
|
+
ok: boolean | null;
|
|
533
|
+
error: string | null;
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
export interface TailnetStatus {
|
|
537
|
+
available: true;
|
|
538
|
+
/** tailscaled backend state: "Running" when connected; "NeedsLogin",
|
|
539
|
+
* "Stopped", "Starting" otherwise. */
|
|
540
|
+
backendState: string;
|
|
541
|
+
/** This node's MagicDNS name + hostname (how peers reach US). */
|
|
542
|
+
self: { dnsName: string; hostName: string; online: boolean } | null;
|
|
543
|
+
/** The tailnet domain, e.g. "tail1234.ts.net". */
|
|
544
|
+
magicDNSSuffix: string | null;
|
|
545
|
+
peers: TailnetPeer[];
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export interface TailnetUnavailable {
|
|
549
|
+
available: false;
|
|
550
|
+
/** Human-readable why — shown in the status tile. */
|
|
551
|
+
reason: string;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
export type TailnetResult = TailnetStatus | TailnetUnavailable;
|
|
555
|
+
|
|
556
|
+
export type TailscaleConfigSummary = {
|
|
557
|
+
hostname: string;
|
|
558
|
+
masked: string;
|
|
559
|
+
lastActivatedAt: Date | null;
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
export type SystemHealth = {
|
|
563
|
+
ts: string;
|
|
564
|
+
scope: 'container' | 'host';
|
|
565
|
+
host: {
|
|
566
|
+
cpuLoadPct: number | null;
|
|
567
|
+
mem: { usedBytes: number; totalBytes: number; usedPct: number } | null;
|
|
568
|
+
disk: DiskInfo | null;
|
|
569
|
+
uptimeSec: number;
|
|
570
|
+
heapUsedBytes: number;
|
|
571
|
+
rssBytes: number;
|
|
572
|
+
loadAvg: number[];
|
|
573
|
+
cpuCores: number;
|
|
574
|
+
};
|
|
575
|
+
postgres: {
|
|
576
|
+
up: boolean;
|
|
577
|
+
dbSizeBytes: number | null;
|
|
578
|
+
connections: number | null;
|
|
579
|
+
cacheHitPct: number | null;
|
|
580
|
+
topTables: { name: string; bytes: number }[];
|
|
581
|
+
};
|
|
582
|
+
storage: {
|
|
583
|
+
minioUp: boolean | null;
|
|
584
|
+
attachmentBytes: number | null;
|
|
585
|
+
filesDisk: DiskInfo | null;
|
|
586
|
+
};
|
|
587
|
+
/** Tier-2 document parser fallback (.odt / .pptx / .doc / .rtf / .epub /
|
|
588
|
+
* …) — sibling docker service. `up: false` means the fallback path
|
|
589
|
+
* degrades cleanly to `no_text_layer` on every new ingest of those
|
|
590
|
+
* formats; in-process parsers (pdf/docx/xlsx/text) keep working. */
|
|
591
|
+
tika: {
|
|
592
|
+
up: boolean;
|
|
593
|
+
version: string | null;
|
|
594
|
+
};
|
|
595
|
+
/** The browser sidecar (browserless/chromium) — the Pages → PDF export
|
|
596
|
+
* engine, a sibling docker service like Tika. `up: false` means PDF
|
|
597
|
+
* downloads 503 until it's back (Markdown/Word unaffected); `up: null`
|
|
598
|
+
* means BROWSER_WS_ENDPOINT isn't configured (e.g. detached dev). */
|
|
599
|
+
browser: {
|
|
600
|
+
up: boolean | null;
|
|
601
|
+
version: string | null;
|
|
602
|
+
};
|
|
603
|
+
/** The configured embedding server. For the `local` provider this is the
|
|
604
|
+
* self-hosted Ollama/LM Studio/TEI on MANTLE_LOCAL_EMBEDDING_URL (the
|
|
605
|
+
* bundled `ollama` compose service in prod). `up: true` means it's
|
|
606
|
+
* reachable AND the configured model is loaded — the only state in which
|
|
607
|
+
* ingest can actually embed. `up: null` = a remote/cloud embedder
|
|
608
|
+
* (openrouter/openai/google), which isn't pingable from here without a key,
|
|
609
|
+
* so it's surfaced as "remote" rather than a misleading red dot. */
|
|
610
|
+
embedder: {
|
|
611
|
+
up: boolean | null;
|
|
612
|
+
provider: string | null;
|
|
613
|
+
model: string | null;
|
|
614
|
+
detail: string | null;
|
|
615
|
+
/** Where the embedder runs: a self-hosted server ('local') or a cloud
|
|
616
|
+
* provider ('remote'). Shown on the dashboard pill label. */
|
|
617
|
+
scope: 'remote' | 'local' | null;
|
|
618
|
+
};
|
|
619
|
+
/** CLI sandboxes supervisor (sandboxd) — profile-gated like the tailnet,
|
|
620
|
+
* so `up: null` (muted pill) is the resting state on a box without the
|
|
621
|
+
* `sandboxes` compose profile. `up: true` requires sandboxd answering
|
|
622
|
+
* (its own /healthz additionally verifies docker); counts and the disk
|
|
623
|
+
* budget come from its live listing. */
|
|
624
|
+
sandboxes: {
|
|
625
|
+
up: boolean | null;
|
|
626
|
+
total: number | null;
|
|
627
|
+
running: number | null;
|
|
628
|
+
disk: { usedBytes: number | null; budgetBytes: number } | null;
|
|
629
|
+
};
|
|
630
|
+
/** Media sidecar (yt-dlp + ffmpeg) — the video_ingest fetch/transcode
|
|
631
|
+
* engine, profile-gated like sandboxes, so `up: null` (muted pill) is the
|
|
632
|
+
* resting state on a box without the `media` compose profile. Versions
|
|
633
|
+
* come from its /healthz — that is what makes a stale or failed yt-dlp
|
|
634
|
+
* self-update VISIBLE instead of silently breaking downloads. */
|
|
635
|
+
media: {
|
|
636
|
+
up: boolean | null;
|
|
637
|
+
ytDlpVersion: string | null;
|
|
638
|
+
ffmpegVersion: string | null;
|
|
639
|
+
/** null on images built before the CAD tier (v0.232.92) — DWF renders
|
|
640
|
+
* fall back to embedded thumbnails without it. */
|
|
641
|
+
ezdwfVersion: string | null;
|
|
642
|
+
/** null on images built before the DWG tier (v0.232.99) — with either of
|
|
643
|
+
* these missing the UI should show "DWG tier missing" (DWG parsing and
|
|
644
|
+
* rendering both need the sidecar; absence means the whole format). */
|
|
645
|
+
dwg2dxfVersion: string | null;
|
|
646
|
+
ezdxfVersion: string | null;
|
|
647
|
+
};
|
|
648
|
+
/** Tailscale / local network — the optional tailnet that lets a cloud VPS
|
|
649
|
+
* reach a LAN model box by MagicDNS name. Profile-gated and off by default
|
|
650
|
+
* in dev, so `up: null` (a muted/disabled pill) is the normal resting state;
|
|
651
|
+
* `up: true` only when tailscaled reports backendState 'Running'. */
|
|
652
|
+
network: {
|
|
653
|
+
up: boolean | null;
|
|
654
|
+
detail: string | null;
|
|
655
|
+
};
|
|
656
|
+
degraded: string[];
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
export type ProvisionResult = {
|
|
660
|
+
createdWorkers: { kind: string; name: string; provider: string; model: string }[];
|
|
661
|
+
createdAgent: { slug: string; name: string } | null;
|
|
662
|
+
/** Capabilities skipped because the optional key wasn't provided. */
|
|
663
|
+
skipped: string[];
|
|
664
|
+
/** Specialist agents seeded alongside the persona (Pages, Ledger, Remy,
|
|
665
|
+
* Researcher, Coder) and wired into the assistant's delegate_to. Names of the
|
|
666
|
+
* ones that seeded successfully; a seed that throws is logged + omitted (it
|
|
667
|
+
* never aborts onboarding — the persona is what matters). */
|
|
668
|
+
seededSpecialists: string[];
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
export type HeartbeatFireSummary = {
|
|
672
|
+
id: string;
|
|
673
|
+
firedAt: string;
|
|
674
|
+
traceId: string | null;
|
|
675
|
+
disposition: string;
|
|
676
|
+
stateBefore: Record<string, unknown> | null;
|
|
677
|
+
stateAfter: Record<string, unknown> | null;
|
|
678
|
+
replyText: string | null;
|
|
679
|
+
replySurfaceRef: Record<string, unknown> | null;
|
|
680
|
+
errorMessage: string | null;
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
export type AgentTelegramBinding = {
|
|
684
|
+
accountId: string;
|
|
685
|
+
botUsername: string;
|
|
686
|
+
enabled: boolean;
|
|
687
|
+
lastPollAt: string | null;
|
|
688
|
+
lastPollError: string | null;
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
export type AgentTelegramChat = {
|
|
692
|
+
id: string;
|
|
693
|
+
telegramChatId: string;
|
|
694
|
+
label: string;
|
|
695
|
+
status: 'pending' | 'allowed' | 'denied';
|
|
696
|
+
lastMessageAt: string | null;
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
export type DiffStatus =
|
|
700
|
+
/** Live matches the template (for tracked fields). */
|
|
701
|
+
| 'ok'
|
|
702
|
+
/** In the template, absent (or disabled) in the brain — a capability not landed. */
|
|
703
|
+
| 'missing'
|
|
704
|
+
/** In the brain, not in the template — operator-added, informational. */
|
|
705
|
+
| 'extra'
|
|
706
|
+
/** Present in both, but a tracked field diverges. */
|
|
707
|
+
| 'modified';
|
|
708
|
+
|
|
709
|
+
export type FieldDiff = {
|
|
710
|
+
/** 'toolGroupSlugs' | 'skillSlugs' | 'delegate_to' | 'instructions' |
|
|
711
|
+
* 'toolSlugs' | 'model' | 'systemPrompt' | 'enabled' */
|
|
712
|
+
field: string;
|
|
713
|
+
/** The template value — what an "adopt" would write. */
|
|
714
|
+
manifest: string | string[] | null;
|
|
715
|
+
/** The live value in the brain. */
|
|
716
|
+
live: string | string[] | null;
|
|
717
|
+
/** Set fields only: members in `live` but not `manifest` (operator-added). */
|
|
718
|
+
added?: string[];
|
|
719
|
+
/** Set fields only: members in `manifest` but not `live` (not landed). */
|
|
720
|
+
removed?: string[];
|
|
721
|
+
/** Informational-only diff (e.g. a specialist prompt) — shown, not weighted. */
|
|
722
|
+
info?: boolean;
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
export type EntityDiff = {
|
|
726
|
+
kind: EntityKind;
|
|
727
|
+
/** Agent/skill/group slug, or the worker kind. */
|
|
728
|
+
slug: string;
|
|
729
|
+
name: string;
|
|
730
|
+
status: DiffStatus;
|
|
731
|
+
severity: AuditSeverity;
|
|
732
|
+
/** One-line human summary of the difference. */
|
|
733
|
+
summary: string;
|
|
734
|
+
/** Tracked fields that differ (empty when status is 'ok'). */
|
|
735
|
+
fields: FieldDiff[];
|
|
736
|
+
/** Can the operator "Adopt from template" this item? True for missing/modified
|
|
737
|
+
* (apply the manifest version); false for ok (nothing to do) and extra
|
|
738
|
+
* (operator-added — adopting would mean deleting, which we never do). */
|
|
739
|
+
adoptable: boolean;
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
export type ConfigDiffReport = {
|
|
743
|
+
generatedAt: string;
|
|
744
|
+
/** The shipped template version (APP_VERSION). */
|
|
745
|
+
appVersion: string;
|
|
746
|
+
/** The version the brain was last auto-reconciled to (null if never). */
|
|
747
|
+
lastReconciledVersion: string | null;
|
|
748
|
+
entities: EntityDiff[];
|
|
749
|
+
counts: { ok: number; missing: number; extra: number; modified: number };
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
export type AdoptKind = 'persona' | 'agent' | 'skill' | 'tool-group' | 'worker';
|
|
753
|
+
|
|
754
|
+
// ── Server-lib view/query DTOs (jackdaw split P0 follow-up: @server/* purge) ──
|
|
755
|
+
// Moved from server/web/lib/* and @mantle/content; the originals re-export
|
|
756
|
+
// these names so server import paths are unchanged.
|
|
757
|
+
|
|
758
|
+
export type UpdaterPhase =
|
|
759
|
+
'idle' | 'pulling' | 'rolling' | 'done' | 'error' | 'unconfigured' | 'requested';
|
|
760
|
+
|
|
761
|
+
/** The updater SCRIPT's own currency. Deliberately not `ComposeState`: the
|
|
762
|
+
* script has no `no-baseline` standoff (it self-adopts, having no supported
|
|
763
|
+
* box-local variation), so a missing baseline is not a state an operator can
|
|
764
|
+
* act on — the only actionable state is `modified`. */
|
|
765
|
+
export type UpdaterScriptState =
|
|
766
|
+
| 'in-sync' // box script == this release's canonical
|
|
767
|
+
| 'stale' // differs — self-refreshes on the next successful update
|
|
768
|
+
| 'modified' // differs from its baseline: hand-edited, refresh refused
|
|
769
|
+
| 'unknown'; // no stack.json, or a pre-v0.206 updater that reports no sha
|
|
770
|
+
|
|
771
|
+
export type ComposeState =
|
|
772
|
+
| 'in-sync' // box compose == this release's canonical
|
|
773
|
+
| 'stale' // pristine (== baseline) but not this release's — refresh hasn't run
|
|
774
|
+
| 'modified' // hand-edited canonical file — auto-refresh disabled, needs adoption
|
|
775
|
+
| 'no-baseline' // pre-adoption box — run scripts/compose-adopt.sh once
|
|
776
|
+
| 'unknown'; // no stack.json (old updater.sh / no sidecar / dev)
|
|
777
|
+
|
|
778
|
+
export type ReleaseInfo = {
|
|
779
|
+
/** Tag as published, e.g. "v0.20.67". */
|
|
780
|
+
tag: string;
|
|
781
|
+
/** Bare version, e.g. "0.20.67". */
|
|
782
|
+
version: string;
|
|
783
|
+
name: string;
|
|
784
|
+
url: string;
|
|
785
|
+
publishedAt: string | null;
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
export type DiskInfo = { usedBytes: number; totalBytes: number; usedPct: number; mount: string };
|
|
789
|
+
|
|
790
|
+
export type EntityKind = 'persona' | 'agent' | 'skill' | 'tool-group' | 'worker';
|
|
791
|
+
|
|
792
|
+
export type StudioNodeKind = 'agent' | 'skill' | 'group';
|
|
793
|
+
|
|
794
|
+
/** One peer on the tailnet (another device sharing your tailnet). */
|
|
795
|
+
export interface TailnetPeer {
|
|
796
|
+
/** MagicDNS name, trailing dot stripped — e.g. "gemma-box.tail1234.ts.net".
|
|
797
|
+
* This is what you'd put in a route base URL: http://<dnsName>:<port>/v1 */
|
|
798
|
+
dnsName: string;
|
|
799
|
+
/** Short hostname — e.g. "gemma-box". */
|
|
800
|
+
hostName: string;
|
|
801
|
+
/** Tailscale IPs (100.x.y.z / fd7a:…). Surfaced for reference; prefer names. */
|
|
802
|
+
ips: string[];
|
|
803
|
+
online: boolean;
|
|
804
|
+
/** OS string tailscaled reports (linux / windows / macOS …), best-effort. */
|
|
805
|
+
os: string | null;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
export type ToolOutcomeStatsRow = {
|
|
809
|
+
calls: number;
|
|
810
|
+
succeeded: number;
|
|
811
|
+
failed: number;
|
|
812
|
+
skipped: number;
|
|
813
|
+
/** Confirm-gated calls parked behind operator approval — not yet run. */
|
|
814
|
+
queued: number;
|
|
815
|
+
failures: Array<{ slug: string; error: string }>;
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
// ── Server-lib view/query DTOs (jackdaw split P0 follow-up: @server/* purge) ──
|
|
819
|
+
// Moved from server/web/lib/* and @mantle/content; the originals re-export
|
|
820
|
+
// these names so server import paths are unchanged.
|
|
821
|
+
|
|
822
|
+
export type CacheHitStats = {
|
|
823
|
+
hits: number;
|
|
824
|
+
misses: number;
|
|
825
|
+
apiCalls: number;
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
export type DuplicateSuppression = {
|
|
829
|
+
/** Model slug captured in trace_steps.meta.model at suppression time. */
|
|
830
|
+
model: string;
|
|
831
|
+
/** How many duplicate tool_use blocks were suppressed in the window. */
|
|
832
|
+
count: number;
|
|
833
|
+
/** Distinct tool slugs the duplicates targeted (top 5, comma-separated). */
|
|
834
|
+
topSlugs: string;
|
|
835
|
+
/** Most recent suppression, ISO string. */
|
|
836
|
+
lastAt: string;
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
export type FactCostCapStats = {
|
|
840
|
+
/** Extractor model slug captured in trace_steps.meta.model. */
|
|
841
|
+
model: string;
|
|
842
|
+
/** How many process_facts steps dropped facts to the cap in the window. */
|
|
843
|
+
runs: number;
|
|
844
|
+
/** Total facts discarded across those runs (sum of meta.dropped). */
|
|
845
|
+
factsDropped: number;
|
|
846
|
+
/** Most recent occurrence, ISO string. */
|
|
847
|
+
lastAt: string;
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
export type Traffic = {
|
|
851
|
+
count: number;
|
|
852
|
+
errorCount: number;
|
|
853
|
+
avgMs: number | null;
|
|
854
|
+
costMicroUsd: number;
|
|
855
|
+
tokensIn: number;
|
|
856
|
+
tokensOut: number;
|
|
857
|
+
tokensCacheRead: number;
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
export type StudioGraph = {
|
|
861
|
+
generatedAt: string;
|
|
862
|
+
nodes: StudioNode[];
|
|
863
|
+
edges: StudioEdge[];
|
|
864
|
+
agents: StudioAgentDetail[];
|
|
865
|
+
skills: StudioSkillDetail[];
|
|
866
|
+
toolGroups: StudioToolGroupDetail[];
|
|
867
|
+
workers: StudioWorkerDetail[];
|
|
868
|
+
/** Live config-integrity report (the same checker behind /debug/integrity). */
|
|
869
|
+
report: SystemReport;
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
export type StudioAgentDetail = {
|
|
873
|
+
id: string;
|
|
874
|
+
slug: string;
|
|
875
|
+
name: string;
|
|
876
|
+
model: string;
|
|
877
|
+
role: string;
|
|
878
|
+
enabled: boolean;
|
|
879
|
+
isPersona: boolean;
|
|
880
|
+
skillSlugs: string[];
|
|
881
|
+
/** Skills attached but NOT resolved (missing or disabled) — surfaced honestly. */
|
|
882
|
+
missingSkillSlugs: string[];
|
|
883
|
+
delegateSlugs: string[];
|
|
884
|
+
/** Tool groups granted to this agent. */
|
|
885
|
+
toolGroupSlugs: string[];
|
|
886
|
+
/** Granted groups that are missing or disabled — surfaced honestly. */
|
|
887
|
+
missingToolGroupSlugs: string[];
|
|
888
|
+
toolCount: number;
|
|
889
|
+
params: { temperature?: number; max_tokens?: number };
|
|
890
|
+
maxIterations?: number;
|
|
891
|
+
/** Whether this is a manifest agent that can be reset to its canonical default. */
|
|
892
|
+
resettable: boolean;
|
|
893
|
+
/** The base system prompt (editable prose in Phase 2). */
|
|
894
|
+
systemPrompt: string;
|
|
895
|
+
/** The enabled, attached skills in composition order. */
|
|
896
|
+
skillBlocks: ComposedSkillBlock[];
|
|
897
|
+
/** The full assembled system prompt the model receives (base + skill blocks),
|
|
898
|
+
* exactly as `composeSystemPromptWithSkills` builds it on a real turn. */
|
|
899
|
+
composedPrompt: string;
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
export type ComposedSkillBlock = { slug: string; name: string; instructions: string };
|
|
903
|
+
|
|
904
|
+
export type StudioSkillDetail = {
|
|
905
|
+
id: string;
|
|
906
|
+
slug: string;
|
|
907
|
+
name: string;
|
|
908
|
+
enabled: boolean;
|
|
909
|
+
instructions: string;
|
|
910
|
+
/** Fan-out: every agent that attaches this skill (the many-to-many). */
|
|
911
|
+
usedByAgentSlugs: string[];
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
export type StudioToolGroupDetail = {
|
|
915
|
+
id: string;
|
|
916
|
+
slug: string;
|
|
917
|
+
name: string;
|
|
918
|
+
enabled: boolean;
|
|
919
|
+
toolSlugs: string[];
|
|
920
|
+
/** Fan-out: every agent that grants this group. */
|
|
921
|
+
usedByAgentSlugs: string[];
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
export type StudioWorkerDetail = {
|
|
925
|
+
id: string;
|
|
926
|
+
kind: string;
|
|
927
|
+
name: string;
|
|
928
|
+
model: string;
|
|
929
|
+
enabled: boolean;
|
|
930
|
+
isDefault: boolean;
|
|
931
|
+
/** Worker prose (registry): the chat-worker system prompt + the vision/document
|
|
932
|
+
* extraction prompt, when present. */
|
|
933
|
+
systemPrompt: string | null;
|
|
934
|
+
extractionPrompt: string | null;
|
|
935
|
+
issues: string[];
|
|
936
|
+
};
|