lumiverse-spindle-types 0.6.24 → 0.6.26

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/api.d.ts ADDED
@@ -0,0 +1,4682 @@
1
+ import type { SpindleManifest } from "./manifest.js";
2
+ import type { SpindleHostDescriptorV1 } from "./host.js";
3
+ import type { CouncilMemberContext } from "./council.js";
4
+ import type { ChatLinkAttachDTO, CortexQueryDTO, MemoryCortexConfigDTO, MemoryEntityStatusUpdateDTO, MemoryEntityUpsertDTO, MemoryRelationUpsertDTO, VaultCreateDTO } from "./memories.js";
5
+ export type LlmMessagePartDTO = {
6
+ type: "text";
7
+ text: string;
8
+ cache_control?: Record<string, unknown>;
9
+ } | {
10
+ type: "image";
11
+ data: string;
12
+ mime_type: string;
13
+ cache_control?: Record<string, unknown>;
14
+ } | {
15
+ type: "audio";
16
+ data: string;
17
+ mime_type: string;
18
+ cache_control?: Record<string, unknown>;
19
+ } | {
20
+ type: "tool_use";
21
+ id: string;
22
+ name: string;
23
+ input: Record<string, unknown>;
24
+ cache_control?: Record<string, unknown>;
25
+ thought_signature?: string;
26
+ } | {
27
+ type: "tool_result";
28
+ tool_use_id: string;
29
+ content: string;
30
+ is_error?: boolean;
31
+ cache_control?: Record<string, unknown>;
32
+ };
33
+ export interface LlmThinkingBlockDTO {
34
+ type: "thinking" | "redacted_thinking";
35
+ thinking?: string;
36
+ signature?: string;
37
+ data?: string;
38
+ }
39
+ export interface LlmMessageDTO {
40
+ role: "system" | "user" | "assistant";
41
+ content: string | LlmMessagePartDTO[];
42
+ name?: string;
43
+ cache_control?: Record<string, unknown>;
44
+ /**
45
+ * Thinking-mode reasoning content from the previous assistant turn, echoed
46
+ * back on the next request. Required by DeepSeek's thinking-mode models
47
+ * (`deepseek-reasoner`, `deepseek-chat` with thinking enabled) **on
48
+ * tool-call continuations** — DeepSeek's API rejects a continuation when
49
+ * an assistant turn invoked a tool call and the echo doesn't carry its
50
+ * reasoning_content back. Plain-text continuations don't need this; nor
51
+ * do non-thinking models. Other openai-compatible providers that route
52
+ * DeepSeek (NanoGPT, OpenRouter, etc.) inherit the same requirement;
53
+ * providers without a reasoning_content notion ignore the field. Set
54
+ * only on `role: 'assistant'` messages.
55
+ */
56
+ reasoning_content?: string;
57
+ thinking_blocks?: LlmThinkingBlockDTO[];
58
+ reasoning_details?: Record<string, unknown>[];
59
+ /**
60
+ * True when this message is a chat-history turn (as opposed to a depth-injected
61
+ * world-info/preset/author's-note block that was spliced into the chat-history
62
+ * range). Set by the host only on the messages passed to the interceptor pipeline,
63
+ * so an extension applying prompt-target regex inline can reproduce the host's depth
64
+ * frame exactly.
65
+ */
66
+ __isChatHistory?: boolean;
67
+ /**
68
+ * Id of the originating chat message, set only on chat-history turns. Lets
69
+ * interceptors map back to the source message without matching on
70
+ * (macro/regex-mutated) content. Stripped before the LLM payload.
71
+ */
72
+ sourceMessageId?: string;
73
+ /**
74
+ * Source message's `index_in_chat`, paired with `sourceMessageId`.
75
+ */
76
+ sourceIndexInChat?: number;
77
+ sourceMessageMetadata?: Readonly<Record<string, unknown>>;
78
+ }
79
+ export type SpindleUserRoleDTO = "operator" | "admin" | "user";
80
+ export type InterceptorGenerationType = "normal" | "continue" | "regenerate" | "swipe" | "impersonate" | "quiet";
81
+ export type InterceptorMatchScalar = string | number | boolean | null;
82
+ export interface InterceptorMatchDTO {
83
+ /**
84
+ * Serializable filter domain. Terminal callbacks currently run only for
85
+ * live `normal` and `continue`; every other value is a fail-closed filter.
86
+ */
87
+ generationTypes?: InterceptorGenerationType[];
88
+ /** Terminal callbacks are never invoked for dry runs. */
89
+ isDryRun?: boolean;
90
+ presetField?: {
91
+ path: string[];
92
+ exists?: boolean;
93
+ oneOf?: InterceptorMatchScalar[];
94
+ notIn?: InterceptorMatchScalar[];
95
+ };
96
+ }
97
+ export interface InterceptorRegistrationMatchOptions {
98
+ match?: InterceptorMatchDTO;
99
+ }
100
+ export interface InterceptorRegistrationOptions {
101
+ priority?: number;
102
+ match?: InterceptorMatchDTO;
103
+ }
104
+ /**
105
+ * Host-owned, immutable context for one bound interceptor callback.
106
+ * `signal` is local to the worker invocation and is never serialized.
107
+ */
108
+ export interface InterceptorContextDTO {
109
+ readonly userId: string;
110
+ readonly chatId: string;
111
+ readonly generationId: string;
112
+ readonly generationType: InterceptorGenerationType;
113
+ readonly isDryRun: boolean;
114
+ readonly presetId: string | null;
115
+ /** Deep clone of only this extension's own preset metadata namespace. */
116
+ readonly presetMetadata: unknown;
117
+ readonly personaId: string | null;
118
+ readonly characterId: string | null;
119
+ readonly personaAddonStates: Readonly<Record<string, boolean>>;
120
+ readonly excludeMessageId?: string;
121
+ readonly activatedWorldInfo?: readonly ActivatedWorldInfoEntryDTO[];
122
+ readonly capturedWorldInfo?: readonly ActivatedWorldInfoEntryDTO[];
123
+ readonly rejectedSwipe?: string;
124
+ readonly regenFeedback?: string;
125
+ readonly regenFeedbackPosition?: "system" | "user";
126
+ readonly mainDispatch: {
127
+ readonly source: "main";
128
+ readonly descriptor: Readonly<ConnectionDispatchDescriptorDTO> | null;
129
+ readonly connectionDispatchRevision: string | null;
130
+ readonly dispatchKind: "concrete" | "roulette" | null;
131
+ };
132
+ readonly prefillCarrier: BoundPrefillAttachmentDTO;
133
+ readonly interceptorDeadlineAt: number;
134
+ readonly boundWorkDeadlineAt: number;
135
+ /** Worker-local cancellation signal; never serialized over the worker protocol. */
136
+ readonly signal: AbortSignal;
137
+ }
138
+ /**
139
+ * Deferred system guidance retained by the host terminal lease.
140
+ *
141
+ * A result may contain at most 128 entries. IDs must be unique canonical UUIDs
142
+ * (versions 1-5). Content must be non-empty, at most 1 MiB per entry when
143
+ * UTF-8 encoded, and at most 2 MiB across the result.
144
+ */
145
+ export interface DeferredGuidanceDTO {
146
+ /** Unique canonical UUID (versions 1-5) within this interceptor result. */
147
+ id: string;
148
+ /** Non-empty system guidance bounded by the limits above. */
149
+ content: string;
150
+ role: "system";
151
+ }
152
+ /**
153
+ * Optional metadata returned by an interceptor so Lumiverse can surface
154
+ * extension-injected prompt messages as first-class items in Prompt Breakdown.
155
+ *
156
+ * `messageIndex` points at the message inside the interceptor's returned
157
+ * `messages` array. The host resolves role/content/extension attribution from
158
+ * that message and from the installed extension manifest, so extensions only
159
+ * need to identify which injected messages should appear in the breakdown.
160
+ */
161
+ export interface InterceptorBreakdownEntryDTO {
162
+ messageIndex: number;
163
+ /** Optional human label for this injected prompt block. */
164
+ name?: string;
165
+ }
166
+ /** Privileged response override returned by an interceptor. */
167
+ export interface FinalResponseDTO {
168
+ content: string;
169
+ reasoning?: string;
170
+ fallbackMessageIndex: number;
171
+ }
172
+ /**
173
+ * Return type for interceptor handlers.
174
+ * Interceptors may return either a plain `LlmMessageDTO[]` (backwards-compatible)
175
+ * or this object to also inject generation parameters (requires `generation_parameters` permission).
176
+ */
177
+ export interface InterceptorResultDTO {
178
+ messages: LlmMessageDTO[];
179
+ /** Provider parameters merged into the outgoing LLM request. Requires `generation_parameters` permission. */
180
+ parameters?: Record<string, unknown>;
181
+ /** Optional prompt-breakdown entries for injected messages. */
182
+ breakdown?: InterceptorBreakdownEntryDTO[];
183
+ /** Host-owned system guidance retained for each authoritative Main attempt. */
184
+ deferredGuidance?: DeferredGuidanceDTO[];
185
+ /** Optional privileged response replacement. Requires the `final_response` permission. */
186
+ finalResponse?: FinalResponseDTO;
187
+ }
188
+ export type InterceptorDisposer = () => void;
189
+ export type InterceptorHandler = (messages: LlmMessageDTO[], context: InterceptorContextDTO) => Promise<LlmMessageDTO[] | InterceptorResultDTO>;
190
+ export interface MacroDefinitionDTO {
191
+ name: string;
192
+ category: string;
193
+ description: string;
194
+ returnType?: "string" | "integer" | "number" | "boolean";
195
+ args?: {
196
+ name: string;
197
+ description?: string;
198
+ required?: boolean;
199
+ }[];
200
+ handler: string;
201
+ /** Set true when the macro returns different output across calls with the same args (time, randomness, idle duration). The display-regex cache will not store resolutions that include this macro. */
202
+ volatile?: boolean;
203
+ }
204
+ /** Minimal shape exposed to extension macro handlers. Additional fields may be present. */
205
+ export interface MacroInvocationContextDTO {
206
+ /** False when the host is performing a dry / non-committing resolve. */
207
+ commit: boolean;
208
+ [key: string]: unknown;
209
+ }
210
+ export interface MacroResolveOptionsDTO {
211
+ chatId?: string;
212
+ characterId?: string;
213
+ /** For operator-scoped extensions only. */
214
+ userId?: string;
215
+ /** Defaults to true. Set false to request a dry / non-committing resolve. */
216
+ commit?: boolean;
217
+ }
218
+ export interface MacroResolveResultDTO {
219
+ text: string;
220
+ diagnostics: Array<{
221
+ message: string;
222
+ offset: number;
223
+ length: number;
224
+ }>;
225
+ }
226
+ /**
227
+ * Where a macro evaluation originated. Useful for interceptors that only
228
+ * want to fire for certain call sites (e.g. prompt assembly vs. response
229
+ * post-processing vs. display-time resolution).
230
+ */
231
+ export type MacroInterceptorPhase = "prompt" | "display" | "response" | "other";
232
+ /**
233
+ * Structured-clone snapshot of the live macro evaluation environment,
234
+ * passed to a macro interceptor. All values are read-only copies — mutating
235
+ * them has no effect on the real environment. Persist state via
236
+ * `spindle.variables.*` helpers instead.
237
+ */
238
+ export type MacroInterceptorCharacterEnvDTO = Readonly<Record<string, unknown>> & {
239
+ /** Selected greeting stored in the current chat, including edits. */
240
+ readonly firstMessage: string;
241
+ /** Card-defined alternatives, excluding the default greeting. */
242
+ readonly alternateGreetings: readonly string[];
243
+ };
244
+ export type MacroInterceptorChatEnvDTO = Readonly<Record<string, unknown>> & {
245
+ /** Selected index in [default greeting, ...alternate greetings]. */
246
+ readonly greetingIndex: number;
247
+ };
248
+ export interface MacroInterceptorEnvDTO {
249
+ readonly commit: boolean;
250
+ readonly names: Record<string, string>;
251
+ readonly character: MacroInterceptorCharacterEnvDTO;
252
+ readonly chat: MacroInterceptorChatEnvDTO;
253
+ readonly system: Record<string, unknown>;
254
+ readonly variables: {
255
+ readonly local: Record<string, string>;
256
+ readonly global: Record<string, string>;
257
+ readonly chat: Record<string, string>;
258
+ };
259
+ /**
260
+ * Per-call dynamic macros injected by the caller (e.g. display-regex
261
+ * pre-resolution passes `chat_index` here). Keys merge into the macro
262
+ * lookup table for the duration of one resolve() call.
263
+ */
264
+ readonly dynamicMacros: Record<string, string>;
265
+ readonly extra: Record<string, unknown>;
266
+ }
267
+ /**
268
+ * Context passed to a macro interceptor handler on every iteration of
269
+ * `MacroEvaluator.evaluate()`. The handler receives the current raw
270
+ * template (already transformed by any earlier interceptors in the chain)
271
+ * and returns either a transformed template string or `void` to pass through.
272
+ */
273
+ export interface MacroInterceptorCtxDTO {
274
+ readonly template: string;
275
+ readonly env: MacroInterceptorEnvDTO;
276
+ readonly commit: boolean;
277
+ readonly phase: MacroInterceptorPhase;
278
+ readonly sourceHint?: string;
279
+ /**
280
+ * User ID that initiated the macro resolution (when available). Relevant
281
+ * for operator-scoped extensions that need to route work through other
282
+ * Spindle APIs on that user's behalf.
283
+ */
284
+ readonly userId?: string;
285
+ }
286
+ /**
287
+ * Lets an interceptor that resolves the template report its real cache
288
+ * dependencies so the host's display-regex cache can store the result
289
+ * and invalidate it precisely.
290
+ */
291
+ export interface MacroInterceptorRichResultDTO {
292
+ text: string;
293
+ touchedVars?: readonly string[];
294
+ volatile?: boolean;
295
+ }
296
+ /**
297
+ * Return value of a macro interceptor handler.
298
+ * - `string` replaces the template for subsequent interceptors + parsing.
299
+ * (forces a non-cacheable resolution when it changes the template).
300
+ * - {@link MacroInterceptorRichResultDTO}
301
+ * - `void` / `undefined` passes the template through unchanged.
302
+ */
303
+ export type MacroInterceptorResultDTO = string | MacroInterceptorRichResultDTO | void;
304
+ /**
305
+ * Which content-write path triggered a message content processor run.
306
+ * `"create"` covers both user-initiated `POST .../messages` writes and
307
+ * auto-inserted greeting rows.
308
+ */
309
+ export type MessageContentProcessorOrigin = "create" | "update" | "swipe_add" | "swipe_update" | "render";
310
+ /**
311
+ * Context passed to a message content processor before a user-initiated
312
+ * message write reaches SQLite. Handlers can inspect this and return a
313
+ * patch (new `content` / merged `extra`) to transform what is stored and
314
+ * what WebSocket subscribers observe on first paint.
315
+ */
316
+ export interface MessageContentProcessorCtxDTO {
317
+ chatId: string;
318
+ /** Undefined for `"create"` origins (the row doesn't exist yet). */
319
+ messageId?: string;
320
+ content: string;
321
+ /** True when the message was authored by the user. */
322
+ isUser: boolean;
323
+ extra?: Record<string, unknown>;
324
+ origin: MessageContentProcessorOrigin;
325
+ /** Set for `"swipe_update"` only — the zero-based index of the swipe being rewritten. */
326
+ swipeIndex?: number;
327
+ /** Owning user for the write. Pass this through to operator-scoped Spindle calls. */
328
+ userId: string;
329
+ }
330
+ /**
331
+ * Return value for a message content processor handler. Return `undefined`
332
+ * / `void` to pass through, or a partial patch to modify the write:
333
+ * - `content` (if present) replaces the content for downstream processors
334
+ * and the DB write.
335
+ * - `extra` (if present) shallow-merges into the existing `extra` — keys
336
+ * you omit are preserved. Ignored on swipe origins (swipes share the
337
+ * parent message's `extra`).
338
+ */
339
+ export interface MessageContentProcessorResultDTO {
340
+ content?: string;
341
+ extra?: Record<string, unknown>;
342
+ }
343
+ export interface ToolRegistrationDTO {
344
+ name: string;
345
+ display_name: string;
346
+ description: string;
347
+ parameters: Record<string, unknown>;
348
+ council_eligible?: boolean;
349
+ /** Whether the tool is available for inline function calling during generation */
350
+ inline_available?: boolean;
351
+ }
352
+ /** Tool/function schema passed to LLM for inline function calling. */
353
+ export interface ToolSchemaDTO {
354
+ name: string;
355
+ description: string;
356
+ parameters: Record<string, unknown>;
357
+ }
358
+ export interface ToolDefinitionDTO {
359
+ name: string;
360
+ description: string;
361
+ parameters: Record<string, unknown>;
362
+ strict?: boolean;
363
+ inputExamples?: Array<Record<string, unknown>>;
364
+ cache_control?: Record<string, unknown>;
365
+ }
366
+ /** A single function call made by the LLM. */
367
+ export interface ToolCallDTO {
368
+ /** Tool name (as given in the schema). */
369
+ name: string;
370
+ /** Parsed JSON arguments as returned by the LLM. */
371
+ args: Record<string, unknown>;
372
+ /** Provider call ID (e.g. Anthropic `id`, OpenAI `id`). Synthetic UUID for providers that don't supply one (e.g. Google). */
373
+ call_id: string;
374
+ /** Opaque provider signature preserved for tool-call continuations. */
375
+ thought_signature?: string;
376
+ }
377
+ export interface GenerationUsageDTO {
378
+ prompt_tokens?: number;
379
+ completion_tokens?: number;
380
+ total_tokens?: number;
381
+ provider_raw?: Record<string, unknown>;
382
+ }
383
+ export interface GenerationResponseDTO {
384
+ content: string;
385
+ reasoning?: string;
386
+ finish_reason: string;
387
+ tool_calls?: ToolCallDTO[];
388
+ thinking_blocks?: LlmThinkingBlockDTO[];
389
+ reasoning_details?: Record<string, unknown>[];
390
+ usage?: GenerationUsageDTO;
391
+ }
392
+ export type GenerationDispatchSourceDTO = {
393
+ source: "main";
394
+ expectedConnectionDispatchRevision: string;
395
+ } | {
396
+ source: "slot";
397
+ connectionId: string;
398
+ expectedConnectionDispatchRevision: string;
399
+ };
400
+ export interface GenerationRequestDTO {
401
+ type: "raw" | "quiet" | "batch";
402
+ messages?: LlmMessageDTO[];
403
+ parameters?: Record<string, unknown>;
404
+ connection_id?: string;
405
+ /** Optional tool/function definitions for inline function calling (raw/quiet only). */
406
+ tools?: ToolSchemaDTO[];
407
+ /**
408
+ * Optional per-request override of the user's reasoning ("extended thinking")
409
+ * settings. When omitted (or `{ source: "inherit" }`) the backend resolves
410
+ * the effective settings the same way a normal chat generation does:
411
+ * the resolved connection's `reasoning_bindings` win, falling back to the
412
+ * user's global `reasoningSettings`.
413
+ *
414
+ * Use this to bypass that resolution for a single request — e.g. to force
415
+ * `"off"` for a quick, cheap call, or to dial the effort up/down with
416
+ * `source: "custom"`. The backend translates the high-level intent into
417
+ * the provider-specific knobs (`thinking`, `thinkingConfig`,
418
+ * `reasoning_effort`, `reasoning.effort`, etc.) so the extension doesn't
419
+ * need to know the per-provider quirks.
420
+ *
421
+ * Raw values supplied in `parameters` still take precedence at the field
422
+ * level — this override only fills in what hasn't already been set,
423
+ * except `source: "off"` which unconditionally strips reasoning fields.
424
+ */
425
+ reasoning?: GenerationReasoningOverrideDTO;
426
+ /**
427
+ * For operator-scoped extensions: the user ID whose connection profiles
428
+ * and generation context should be used. For user-scoped extensions this
429
+ * is inferred from the extension owner and can be omitted.
430
+ */
431
+ userId?: string;
432
+ /**
433
+ * Optional `AbortSignal` to cancel an in-flight generation. When the
434
+ * signal fires, the upstream LLM HTTP request is torn down and the
435
+ * returned promise rejects with an `AbortError` (`err.name === "AbortError"`).
436
+ *
437
+ * The signal is consumed inside the extension worker and never crosses
438
+ * the host boundary — it is stripped before the RPC message is posted.
439
+ * The worker notifies the host via an internal `cancel_generation`
440
+ * message so the host can abort the in-flight request.
441
+ *
442
+ * @example
443
+ * ```ts
444
+ * const controller = new AbortController()
445
+ * const timer = setTimeout(() => controller.abort(), 10_000)
446
+ * try {
447
+ * const result = await spindle.generate.raw({
448
+ * provider: "openai",
449
+ * model: "gpt-4o-mini",
450
+ * messages: [{ role: "user", content: "hello" }],
451
+ * signal: controller.signal,
452
+ * })
453
+ * } catch (err) {
454
+ * if (err instanceof Error && err.name === "AbortError") {
455
+ * // user/timeout cancelled — not an error condition
456
+ * }
457
+ * } finally {
458
+ * clearTimeout(timer)
459
+ * }
460
+ * ```
461
+ */
462
+ signal?: AbortSignal;
463
+ }
464
+ /** Options passed to chat generation when `spindle.chat.appendMessage()` starts a normal reply. */
465
+ export interface ChatAppendGenerationOptionsDTO {
466
+ /** Omit to use the user's default connection profile. */
467
+ connection_id?: string;
468
+ /** Omit to use the user's active persona setting. */
469
+ persona_id?: string;
470
+ persona_addon_states?: Record<string, boolean>;
471
+ /** Omit to use the user's active Loom preset; if unset, the connection preset is used. */
472
+ preset_id?: string;
473
+ force_preset_id?: boolean;
474
+ parameters?: Record<string, unknown>;
475
+ target_character_id?: string;
476
+ retain_council?: boolean;
477
+ }
478
+ /** Optional third argument for `spindle.chat.appendMessage()`. */
479
+ export type ChatAppendMessageOptionsDTO = boolean | {
480
+ triggerGeneration?: boolean;
481
+ generation?: ChatAppendGenerationOptionsDTO;
482
+ };
483
+ /**
484
+ * Streamed chunk yielded by `spindle.generate.rawStream()` and
485
+ * `spindle.generate.quietStream()`.
486
+ *
487
+ * The stream emits one or more `token` / `reasoning` chunks and then
488
+ * exactly one terminal `done` chunk carrying the aggregated response.
489
+ * If the stream fails or is aborted, the async generator rejects instead
490
+ * of emitting `done`.
491
+ */
492
+ export type StreamChunkDTO =
493
+ /** Incremental content token. */
494
+ {
495
+ type: "token";
496
+ token: string;
497
+ }
498
+ /** Incremental chain-of-thought / reasoning token. */
499
+ | {
500
+ type: "reasoning";
501
+ token: string;
502
+ }
503
+ /** Terminal chunk — emitted exactly once, on successful completion. */
504
+ | {
505
+ type: "done";
506
+ content: string;
507
+ reasoning?: string;
508
+ finish_reason: string;
509
+ tool_calls?: ToolCallDTO[];
510
+ usage?: {
511
+ prompt_tokens: number;
512
+ completion_tokens: number;
513
+ total_tokens: number;
514
+ };
515
+ };
516
+ export interface RequestInitDTO {
517
+ method?: string;
518
+ headers?: Record<string, string>;
519
+ body?: string;
520
+ /** When `"arraybuffer"`, the response body is returned as a base64-encoded string
521
+ * with `encoding: "base64"`. Used by the sandboxed-widget transparent proxy. */
522
+ responseType?: "text" | "arraybuffer";
523
+ /** Restricts transparent binary proxy responses to a browser-renderable media class. */
524
+ mediaType?: "image" | "audio";
525
+ }
526
+ /**
527
+ * Reasoning effort tier. Provider mapping:
528
+ * - Anthropic adaptive (Claude 4.6+): `low | medium | high | max` (+ `xhigh` on Opus 4.7) → `output_config.effort`.
529
+ * - Anthropic legacy: mapped to `thinking.budget_tokens` (low=2048, medium=8192, high=16384, max=32768).
530
+ * - Google (Gemini / Vertex): `minimal | low | medium | high` → `thinkingConfig.thinkingLevel`.
531
+ * - DeepSeek: `low | medium | high` → `"high"`, `max | xhigh` → `"max"` (`reasoning_effort`).
532
+ * - OpenRouter: `none | minimal | low | medium | high | xhigh` → `reasoning.effort`.
533
+ * - NanoGPT: `none | minimal | low | medium | high` → `reasoning.effort`.
534
+ * - Moonshot / Z.AI: toggle-only — effort ignored, just enables `thinking`.
535
+ * - Generic OpenAI-compatible: passed verbatim as `reasoning.effort`.
536
+ *
537
+ * `"auto"` defers to the user's preset/global setting or the provider's
538
+ * model-specific default and is the safest value to use when you don't
539
+ * have a specific tier in mind.
540
+ */
541
+ export type ReasoningEffortDTO = "auto" | "none" | "minimal" | "low" | "medium" | "high" | "max" | "xhigh";
542
+ /**
543
+ * Anthropic-only display mode for thinking blocks. Maps to `thinking.display`
544
+ * in the Messages API. `"auto"` omits the field so Anthropic applies its
545
+ * model-specific default (`"omitted"` on Opus 4.7 / Mythos Preview,
546
+ * `"summarized"` elsewhere). Ignored by every other provider.
547
+ */
548
+ export type ThinkingDisplayDTO = "auto" | "summarized" | "omitted";
549
+ /**
550
+ * Full reasoning settings snapshot. Mirrors the user-level setting that
551
+ * Lumiverse stores under `reasoningSettings`. Surfaced on
552
+ * `ConnectionProfileDTO.reasoning_bindings.settings` when a connection has
553
+ * a binding attached.
554
+ *
555
+ * Only `apiReasoning` / `reasoningEffort` / `thinkingDisplay` influence the
556
+ * outgoing provider request — the remaining fields drive delimited-reasoning
557
+ * parsing (`prefix`, `suffix`, `autoParse`) and chat-history pruning
558
+ * (`keepInHistory`) and are included for inspection / round-tripping.
559
+ */
560
+ export interface ReasoningSettingsDTO {
561
+ /** Master switch: whether the provider should produce thinking output. */
562
+ apiReasoning: boolean;
563
+ /** Effort tier — see {@link ReasoningEffortDTO}. */
564
+ reasoningEffort: ReasoningEffortDTO;
565
+ /** Anthropic-only. */
566
+ thinkingDisplay: ThinkingDisplayDTO;
567
+ /** Opening delimiter used by the delimited-reasoning parser (e.g. `"<think>\n"`). */
568
+ prefix: string;
569
+ /** Closing delimiter used by the delimited-reasoning parser (e.g. `"\n</think>"`). */
570
+ suffix: string;
571
+ /** Whether to auto-parse delimited reasoning out of the assistant content stream. */
572
+ autoParse: boolean;
573
+ /**
574
+ * How many recent reasoning blocks to retain in assembled prompt history.
575
+ * `0` strips all, `-1` keeps everything, `N` keeps the last N.
576
+ */
577
+ keepInHistory: number;
578
+ }
579
+ /**
580
+ * Reasoning settings bound to a specific connection profile. When present,
581
+ * these override the user's global `reasoningSettings` during normal chat
582
+ * generation on this connection.
583
+ */
584
+ export interface ConnectionReasoningBindingsDTO {
585
+ /** Reasoning settings snapshot captured at bind time. */
586
+ settings: ReasoningSettingsDTO;
587
+ /**
588
+ * Optional "Start Reply With" assistant prefill captured alongside the
589
+ * reasoning snapshot. When present, overrides the user's global
590
+ * `promptBias` setting for this connection.
591
+ */
592
+ promptBias?: string;
593
+ }
594
+ /**
595
+ * Per-request reasoning override for `spindle.generate.*` calls. Use the
596
+ * `source` discriminator to pick how the backend resolves the effective
597
+ * reasoning settings:
598
+ *
599
+ * - `"inherit"` (default if `source` is omitted): apply the connection's
600
+ * `reasoning_bindings` if any, else the user's global setting. Same as
601
+ * leaving the `reasoning` field off entirely. Useful when you want to
602
+ * document intent without changing behaviour.
603
+ * - `"off"`: short-circuit. The provider's no-reasoning off-switch is
604
+ * applied unconditionally — even if `parameters` already carry an
605
+ * explicit `thinking` / `reasoning` block from the caller.
606
+ * - `"custom"`: use the explicit `apiReasoning` / `effort` / `thinkingDisplay`
607
+ * fields below for this request only. Omitted fields use their defaults
608
+ * (`apiReasoning: true`, `effort: "auto"`, `thinkingDisplay: "auto"`).
609
+ * Raw values supplied via `parameters` still win at the field level —
610
+ * the override only fills in unset fields, exactly like the inherited
611
+ * settings would.
612
+ */
613
+ export interface GenerationReasoningOverrideDTO {
614
+ source?: "inherit" | "off" | "custom";
615
+ apiReasoning?: boolean;
616
+ effort?: ReasoningEffortDTO;
617
+ thinkingDisplay?: ThinkingDisplayDTO;
618
+ }
619
+ /**
620
+ * Safe representation of a user's connection profile exposed to extensions.
621
+ * Never contains the actual API key — only `has_api_key` boolean.
622
+ */
623
+ export interface ConnectionProfileDTO {
624
+ id: string;
625
+ name: string;
626
+ provider: string;
627
+ api_url: string;
628
+ model: string;
629
+ preset_id: string | null;
630
+ is_default: boolean;
631
+ has_api_key: boolean;
632
+ /**
633
+ * Raw provider-specific metadata bag stored on the connection. Includes
634
+ * provider-quirk flags (Anthropic prompt caching, Google thinking budget
635
+ * config, etc.) and the original `reasoningBindings` blob — `reasoning_bindings`
636
+ * below is the parsed, typed view of that same blob.
637
+ */
638
+ metadata: Record<string, unknown>;
639
+ /**
640
+ * Typed view of the connection's bound reasoning settings, parsed from
641
+ * `metadata.reasoningBindings`. `null` when the connection has no binding
642
+ * (in which case generation falls back to the user's global
643
+ * `reasoningSettings`).
644
+ */
645
+ reasoning_bindings: ConnectionReasoningBindingsDTO | null;
646
+ created_at: number;
647
+ updated_at: number;
648
+ }
649
+ export interface ConnectionDispatchDescriptorDTO {
650
+ connectionId: string;
651
+ connectionName: string;
652
+ provider: string;
653
+ model: string;
654
+ endpointOrigin: string;
655
+ dispatchKind: "concrete" | "roulette";
656
+ connectionDispatchRevision: string | null;
657
+ }
658
+ /**
659
+ * Safe representation of an image generation connection profile.
660
+ * Never contains the actual API key — only `has_api_key` boolean.
661
+ */
662
+ export interface ImageGenConnectionDTO {
663
+ id: string;
664
+ name: string;
665
+ provider: string;
666
+ api_url: string;
667
+ model: string;
668
+ is_default: boolean;
669
+ has_api_key: boolean;
670
+ default_parameters: Record<string, unknown>;
671
+ metadata: Record<string, unknown>;
672
+ created_at: number;
673
+ updated_at: number;
674
+ }
675
+ /** Parameter schema for a single image gen provider parameter. */
676
+ export interface ImageGenParameterSchemaDTO {
677
+ type: "number" | "integer" | "boolean" | "string" | "select" | "image_array";
678
+ default?: unknown;
679
+ min?: number;
680
+ max?: number;
681
+ step?: number;
682
+ description: string;
683
+ required?: boolean;
684
+ options?: Array<{
685
+ id: string;
686
+ label: string;
687
+ }>;
688
+ group?: string;
689
+ }
690
+ /** Capabilities exposed by an image generation provider. */
691
+ export interface ImageGenProviderDTO {
692
+ id: string;
693
+ name: string;
694
+ capabilities: {
695
+ parameters: Record<string, ImageGenParameterSchemaDTO>;
696
+ apiKeyRequired: boolean;
697
+ modelListStyle: "static" | "dynamic" | "google";
698
+ staticModels?: Array<{
699
+ id: string;
700
+ label: string;
701
+ }>;
702
+ defaultUrl: string;
703
+ /** Present only when the provider supports Lumiverse's WebSocket preview/status stream. */
704
+ websocketPreviewStreaming?: {
705
+ previews: true;
706
+ status: true;
707
+ };
708
+ };
709
+ }
710
+ /** Input for `spindle.imageGen.generate()` */
711
+ export interface ImageGenRequestDTO {
712
+ /** Connection profile ID to use. If omitted, uses the user's default image gen connection. */
713
+ connection_id?: string;
714
+ /** Text prompt for image generation. */
715
+ prompt: string;
716
+ /** Negative prompt (provider-dependent). */
717
+ negativePrompt?: string;
718
+ /** Model override. If omitted, uses the connection profile's model. */
719
+ model?: string;
720
+ /** Provider-specific parameters. Merged with the connection's default_parameters. */
721
+ parameters?: Record<string, unknown>;
722
+ /** Optional character ownership tag for the persisted result image. */
723
+ owner_character_id?: string;
724
+ /** Optional chat ownership tag for the persisted result image. */
725
+ owner_chat_id?: string;
726
+ /**
727
+ * Ask the host to omit the base64 `imageDataUrl` from the result.
728
+ *
729
+ * The host still needs the data URL to persist the image into the images
730
+ * table, so `imageId` / `imageUrl` remain populated. Only the
731
+ * extension-facing response drops the base64 field, which is the largest
732
+ * per-image RPC payload. The default (`includeDataUrl` omitted or `true`)
733
+ * keeps the data URL for backward compatibility.
734
+ */
735
+ includeDataUrl?: boolean;
736
+ /** For operator-scoped extensions. */
737
+ userId?: string;
738
+ }
739
+ /** Result from `spindle.imageGen.generate()` */
740
+ export interface ImageGenResultDTO {
741
+ imageDataUrl: string;
742
+ model: string;
743
+ provider: string;
744
+ /** Persisted image ID in the images table (for gallery, backgrounds, etc.) */
745
+ imageId?: string;
746
+ /** Public URL for the image — works without authentication. Suitable for push notification `image` field. */
747
+ imageUrl?: string;
748
+ }
749
+ /** Input for {@link SpindleAPI.imageGen.generateStream}. */
750
+ export interface ImageGenStreamRequestDTO extends ImageGenRequestDTO {
751
+ /** Abort the upstream generation and close its WebSocket stream. */
752
+ signal?: AbortSignal;
753
+ }
754
+ /** A progress/status update received from the provider WebSocket. */
755
+ export interface ImageGenStreamStatusDTO {
756
+ type: "status";
757
+ /** Current step when the provider reports numerical progress. */
758
+ step?: number;
759
+ /** Total steps when the provider reports numerical progress. */
760
+ totalSteps?: number;
761
+ /** Current workflow node, when supplied by the provider. */
762
+ nodeId?: string;
763
+ }
764
+ /** A preview image received from the provider WebSocket. */
765
+ export interface ImageGenStreamPreviewDTO {
766
+ type: "preview";
767
+ /** Preview image as a base64 data URL. */
768
+ imageDataUrl: string;
769
+ /** Status reported with this preview, when available. */
770
+ step?: number;
771
+ totalSteps?: number;
772
+ nodeId?: string;
773
+ }
774
+ /** Terminal success event for an image generation stream. */
775
+ export interface ImageGenStreamDoneDTO {
776
+ type: "done";
777
+ /** Final image and its persisted asset identifiers. */
778
+ result: ImageGenResultDTO;
779
+ }
780
+ /** Events yielded by {@link SpindleAPI.imageGen.generateStream}. */
781
+ export type ImageGenStreamEventDTO = ImageGenStreamStatusDTO | ImageGenStreamPreviewDTO | ImageGenStreamDoneDTO;
782
+ export type ImageSpecificityDTO = "full" | "sm" | "lg";
783
+ export type ImageVideoCodecDTO = "h264" | "hevc";
784
+ export interface ImageListOptionsDTO {
785
+ limit?: number;
786
+ offset?: number;
787
+ /** Which image URL size should be returned in each DTO. */
788
+ specificity?: ImageSpecificityDTO;
789
+ /** Restrict results to images created by the current extension. */
790
+ onlyOwned?: boolean;
791
+ /** Restrict results to images tagged to a specific character. */
792
+ characterId?: string;
793
+ /** Restrict results to images tagged to a specific chat. */
794
+ chatId?: string;
795
+ /** For operator-scoped extensions. */
796
+ userId?: string;
797
+ }
798
+ export interface ImageGetOptionsDTO {
799
+ /** Which image URL size should be returned in the DTO. */
800
+ specificity?: ImageSpecificityDTO;
801
+ /** Restrict lookup to images created by the current extension. */
802
+ onlyOwned?: boolean;
803
+ /** Restrict lookup to images tagged to a specific character. */
804
+ characterId?: string;
805
+ /** Restrict lookup to images tagged to a specific chat. */
806
+ chatId?: string;
807
+ /** For operator-scoped extensions. */
808
+ userId?: string;
809
+ }
810
+ /** Safe representation of an image/video asset exposed to extensions. */
811
+ export interface ImageDTO {
812
+ id: string;
813
+ original_filename: string;
814
+ mime_type: string;
815
+ width: number | null;
816
+ height: number | null;
817
+ has_thumbnail: boolean;
818
+ /** Relative authenticated URL for this image, already sized to `specificity`. */
819
+ url: string;
820
+ specificity: ImageSpecificityDTO;
821
+ owner_extension_identifier: string | null;
822
+ owner_character_id: string | null;
823
+ owner_chat_id: string | null;
824
+ created_at: number;
825
+ }
826
+ /** Upload payload for `spindle.images.upload()` */
827
+ export interface ImageUploadDTO {
828
+ /** Raw image or video bytes. */
829
+ data: Uint8Array;
830
+ /** Optional filename used to preserve the extension/MIME when storing the image. */
831
+ filename?: string;
832
+ /** Optional content type. Defaults to image/png when not inferable. */
833
+ mime_type?: string;
834
+ /** Optional character ownership tag for the persisted image. */
835
+ owner_character_id?: string;
836
+ /** Optional chat ownership tag for the persisted image. */
837
+ owner_chat_id?: string;
838
+ /** Persist the original asset without generating thumbnail derivatives. */
839
+ skip_thumbnail_processing?: boolean;
840
+ /** For video uploads, strip any audio tracks from the stored output when possible. */
841
+ strip_audio?: boolean;
842
+ /** For video uploads, transcode the primary stored asset to this codec. */
843
+ transcode_video_codec?: ImageVideoCodecDTO;
844
+ /** Optional extra video variants to generate alongside the primary stored asset. */
845
+ sidecar_video_codecs?: ImageVideoCodecDTO[];
846
+ }
847
+ export interface ImageUploadFromDataUrlOptionsDTO {
848
+ originalFilename?: string;
849
+ /** Optional character ownership tag for the persisted image. */
850
+ owner_character_id?: string;
851
+ /** Optional chat ownership tag for the persisted image. */
852
+ owner_chat_id?: string;
853
+ /** Persist the original asset without generating thumbnail derivatives. */
854
+ skip_thumbnail_processing?: boolean;
855
+ /** For operator-scoped extensions. */
856
+ userId?: string;
857
+ }
858
+ export type MediaSourceDTO = {
859
+ kind: "inline";
860
+ data: Uint8Array;
861
+ filename?: string;
862
+ mime_type?: string;
863
+ } | {
864
+ kind: "upload";
865
+ upload_id: string;
866
+ filename?: string;
867
+ mime_type?: string;
868
+ } | {
869
+ /**
870
+ * Image or video asset already stored in Lumiverse's images table.
871
+ * This can point at either a still image or a video upload.
872
+ */
873
+ kind: "image";
874
+ image_id: string;
875
+ } | {
876
+ /** Audio asset already stored in Lumiverse's audio_files table. */
877
+ kind: "audio";
878
+ audio_id: string;
879
+ };
880
+ export type MediaAudioFormatDTO = "mp3" | "wav" | "ogg" | "aac" | "flac" | "m4a" | "webm";
881
+ export type MediaVideoFormatDTO = "mp4" | "webm" | "mov" | "mkv";
882
+ export type MediaVideoCodecDTO = "h264" | "hevc" | "vp9" | "av1" | "copy";
883
+ export type MediaAudioCodecDTO = "aac" | "mp3" | "opus" | "vorbis" | "flac" | "pcm_s16le" | "copy";
884
+ export type MediaFitModeDTO = "contain" | "cover" | "stretch";
885
+ export interface MediaTransformResultDTO {
886
+ data: Uint8Array;
887
+ filename: string;
888
+ mime_type: string;
889
+ byte_size: number;
890
+ duration_ms?: number | null;
891
+ width?: number | null;
892
+ height?: number | null;
893
+ }
894
+ export interface MediaConvertAudioRequestDTO {
895
+ source: MediaSourceDTO;
896
+ output_format: MediaAudioFormatDTO;
897
+ audio_codec?: MediaAudioCodecDTO;
898
+ bitrate_kbps?: number;
899
+ sample_rate?: number;
900
+ channels?: number;
901
+ filename?: string;
902
+ /** For operator-scoped extensions. */
903
+ userId?: string;
904
+ }
905
+ export interface MediaConvertVideoRequestDTO {
906
+ source: MediaSourceDTO;
907
+ output_format: MediaVideoFormatDTO;
908
+ filename?: string;
909
+ /** For operator-scoped extensions. */
910
+ userId?: string;
911
+ }
912
+ export interface MediaTranscodeVideoRequestDTO {
913
+ source: MediaSourceDTO;
914
+ output_format?: MediaVideoFormatDTO;
915
+ video_codec?: MediaVideoCodecDTO;
916
+ audio_codec?: MediaAudioCodecDTO | "none";
917
+ video_bitrate_kbps?: number;
918
+ audio_bitrate_kbps?: number;
919
+ crf?: number;
920
+ preset?: string;
921
+ width?: number;
922
+ height?: number;
923
+ fps?: number;
924
+ pixel_format?: string;
925
+ faststart?: boolean;
926
+ filename?: string;
927
+ /** For operator-scoped extensions. */
928
+ userId?: string;
929
+ }
930
+ export interface MediaRemoveAudioFromVideoRequestDTO {
931
+ source: MediaSourceDTO;
932
+ output_format?: MediaVideoFormatDTO;
933
+ video_codec?: MediaVideoCodecDTO;
934
+ filename?: string;
935
+ /** For operator-scoped extensions. */
936
+ userId?: string;
937
+ }
938
+ export interface MediaAddAudioToVideoRequestDTO {
939
+ video: MediaSourceDTO;
940
+ audio: MediaSourceDTO;
941
+ output_format?: MediaVideoFormatDTO;
942
+ video_codec?: MediaVideoCodecDTO;
943
+ audio_codec?: MediaAudioCodecDTO;
944
+ /** Defaults to true: replace any existing audio track on the source video. */
945
+ replace_existing_audio?: boolean;
946
+ /** When true, clamp the output duration to the shorter input stream. */
947
+ shortest?: boolean;
948
+ /** Optional positive offset, in milliseconds, before the new audio starts. */
949
+ audio_start_ms?: number;
950
+ filename?: string;
951
+ /** For operator-scoped extensions. */
952
+ userId?: string;
953
+ }
954
+ export interface MediaCreateVideoFromImageAndAudioRequestDTO {
955
+ image: MediaSourceDTO;
956
+ audio: MediaSourceDTO;
957
+ output_format?: MediaVideoFormatDTO;
958
+ video_codec?: Exclude<MediaVideoCodecDTO, "copy">;
959
+ audio_codec?: MediaAudioCodecDTO;
960
+ width?: number;
961
+ height?: number;
962
+ fps?: number;
963
+ fit_mode?: MediaFitModeDTO;
964
+ background_color?: string;
965
+ filename?: string;
966
+ /** For operator-scoped extensions. */
967
+ userId?: string;
968
+ }
969
+ /**
970
+ * Safe representation of a character exposed to extensions.
971
+ * Includes the full `extensions` blob so extensions can read and write
972
+ * their own namespaced keys alongside the allowlisted `world_book_ids`.
973
+ */
974
+ export interface CharacterDTO {
975
+ id: string;
976
+ name: string;
977
+ description: string;
978
+ personality: string;
979
+ scenario: string;
980
+ first_mes: string;
981
+ mes_example: string;
982
+ creator_notes: string;
983
+ system_prompt: string;
984
+ post_history_instructions: string;
985
+ tags: string[];
986
+ alternate_greetings: string[];
987
+ creator: string;
988
+ image_id: string | null;
989
+ /**
990
+ * IDs of world books attached directly to this character. The legacy
991
+ * single-id form is auto-migrated, so consumers can rely on the array.
992
+ */
993
+ world_book_ids: string[];
994
+ /** The raw extensions object. Extensions should namespace their keys. */
995
+ extensions: Record<string, any>;
996
+ created_at: number;
997
+ updated_at: number;
998
+ }
999
+ export interface CharacterCreateDTO {
1000
+ name: string;
1001
+ description?: string;
1002
+ personality?: string;
1003
+ scenario?: string;
1004
+ first_mes?: string;
1005
+ mes_example?: string;
1006
+ creator_notes?: string;
1007
+ system_prompt?: string;
1008
+ post_history_instructions?: string;
1009
+ tags?: string[];
1010
+ alternate_greetings?: string[];
1011
+ creator?: string;
1012
+ /** Optional initial world book attachments. */
1013
+ world_book_ids?: string[];
1014
+ /** Optional initial extension data. */
1015
+ extensions?: Record<string, any>;
1016
+ }
1017
+ export interface CharacterUpdateDTO {
1018
+ name?: string;
1019
+ description?: string;
1020
+ personality?: string;
1021
+ scenario?: string;
1022
+ first_mes?: string;
1023
+ mes_example?: string;
1024
+ creator_notes?: string;
1025
+ system_prompt?: string;
1026
+ post_history_instructions?: string;
1027
+ tags?: string[];
1028
+ alternate_greetings?: string[];
1029
+ creator?: string;
1030
+ /**
1031
+ * Replace the character's world book attachments. Pass an empty array to
1032
+ * detach all books. Omit the field to leave attachments unchanged.
1033
+ */
1034
+ world_book_ids?: string[];
1035
+ /**
1036
+ * Shallow-merged into the character's existing extensions.
1037
+ * Extension-provided keys overwrite existing ones; omitting a key leaves it
1038
+ * untouched. Pass an empty object to make no changes, or omit entirely.
1039
+ */
1040
+ extensions?: Record<string, any>;
1041
+ }
1042
+ export interface CharacterAvatarUploadDTO {
1043
+ /** Raw avatar bytes. Extensions can source these from fetch(), storage, etc. */
1044
+ data: Uint8Array;
1045
+ /** Optional filename used to preserve the extension/MIME when storing the image. */
1046
+ filename?: string;
1047
+ /** Optional content type for the uploaded avatar. Defaults to image/png. */
1048
+ mime_type?: string;
1049
+ }
1050
+ /**
1051
+ * Safe representation of a chat session exposed to extensions.
1052
+ */
1053
+ export interface ChatDTO {
1054
+ id: string;
1055
+ character_id: string;
1056
+ name: string;
1057
+ metadata: Record<string, unknown>;
1058
+ created_at: number;
1059
+ updated_at: number;
1060
+ }
1061
+ export interface ChatUpdateDTO {
1062
+ name?: string;
1063
+ metadata?: Record<string, unknown>;
1064
+ }
1065
+ /** Payload for `CHAT_SWITCHED` events. */
1066
+ export interface ChatSwitchedPayloadDTO {
1067
+ /** The chat the user switched to, or `null` when returning to the home screen. */
1068
+ chatId: string | null;
1069
+ }
1070
+ /** Payload for `CHAT_CHANGED` events. */
1071
+ export interface ChatChangedPayloadDTO {
1072
+ /** The chat after the change. */
1073
+ chat: {
1074
+ id: string;
1075
+ [key: string]: unknown;
1076
+ };
1077
+ /** Optional. Dot-paths of fields that differed between the prior and new chat (e.g. `metadata.macro_variables.local.foo`, `name`, `metadata.last_message_id`). Absent on events emitted by sources that don't compute the diff. */
1078
+ changedFields?: string[];
1079
+ }
1080
+ /**
1081
+ * Payload for `CHAT_FORKED` events. Emitted when a chat is forked (branched)
1082
+ * from a specific message — the messages up to and including the fork point are
1083
+ * copied into a brand-new chat that shares the source chat's character.
1084
+ */
1085
+ export interface ChatForkedPayloadDTO {
1086
+ /** Id of the source chat that was forked. */
1087
+ sourceChatId: string;
1088
+ /** Id of the newly created forked chat. Equal to `chat.id`. */
1089
+ forkedChatId: string;
1090
+ /** The new forked chat row, including its `metadata` (carries `branched_from` and `branch_at_message`). */
1091
+ chat: {
1092
+ id: string;
1093
+ [key: string]: unknown;
1094
+ };
1095
+ /** The `branch_id` assigned to every message copied into the forked chat. */
1096
+ branchId: string;
1097
+ /** Id of the message in the source chat the fork was taken at. Messages up to and including this one were copied into the forked chat. */
1098
+ forkedAtMessageId: string;
1099
+ /** Zero-based index of the fork-point message within the source chat. */
1100
+ forkedAtMessageIndex: number;
1101
+ }
1102
+ /** Option entry for `select` and `multiselect` prompt variables. */
1103
+ export interface PromptVariableOptionDTO {
1104
+ id: string;
1105
+ label: string;
1106
+ value: string;
1107
+ }
1108
+ export type PromptVariableDefDTO = {
1109
+ id: string;
1110
+ name: string;
1111
+ label: string;
1112
+ type: "text";
1113
+ defaultValue: string;
1114
+ description?: string;
1115
+ } | {
1116
+ id: string;
1117
+ name: string;
1118
+ label: string;
1119
+ type: "textarea";
1120
+ defaultValue: string;
1121
+ rows?: number;
1122
+ description?: string;
1123
+ } | {
1124
+ id: string;
1125
+ name: string;
1126
+ label: string;
1127
+ type: "number";
1128
+ defaultValue: number;
1129
+ min?: number;
1130
+ max?: number;
1131
+ step?: number;
1132
+ description?: string;
1133
+ } | {
1134
+ id: string;
1135
+ name: string;
1136
+ label: string;
1137
+ type: "slider";
1138
+ defaultValue: number;
1139
+ min: number;
1140
+ max: number;
1141
+ step?: number;
1142
+ description?: string;
1143
+ } | {
1144
+ id: string;
1145
+ name: string;
1146
+ label: string;
1147
+ type: "select";
1148
+ /** Stored selection: an option id. */
1149
+ defaultValue: string;
1150
+ options: PromptVariableOptionDTO[];
1151
+ description?: string;
1152
+ } | {
1153
+ id: string;
1154
+ name: string;
1155
+ label: string;
1156
+ type: "switch";
1157
+ /** 0 (off) or 1 (on). */
1158
+ defaultValue: 0 | 1;
1159
+ description?: string;
1160
+ } | {
1161
+ id: string;
1162
+ name: string;
1163
+ label: string;
1164
+ type: "multiselect";
1165
+ /** Stored selection: array of option ids. */
1166
+ defaultValue: string[];
1167
+ options: PromptVariableOptionDTO[];
1168
+ /** String inserted between joined option values. Defaults to two newlines. */
1169
+ separator?: string;
1170
+ description?: string;
1171
+ };
1172
+ export type PromptVariableTypeDTO = PromptVariableDefDTO["type"];
1173
+ export type PromptVariableValueDTO = string | number | string[];
1174
+ export type PromptVariableValuesDTO = Record<string, Record<string, PromptVariableValueDTO>>;
1175
+ export type PromptBlockRoleDTO = "system" | "user" | "assistant" | "user_append" | "assistant_append";
1176
+ export type PromptBlockPositionDTO = "pre_history" | "post_history" | "in_history";
1177
+ export type PromptBlockCategoryModeDTO = "radio" | "checkbox" | null;
1178
+ /** A native Loom placement profile selected for a prompt block. */
1179
+ export interface PromptBlockPlacementDTO {
1180
+ role: PromptBlockRoleDTO;
1181
+ position: PromptBlockPositionDTO;
1182
+ depth: number;
1183
+ }
1184
+ /** A select variable's mapping from option ids to native Loom placement profiles. */
1185
+ export interface PromptBlockPlacementBindingDTO {
1186
+ variableId: string;
1187
+ options: Record<string, PromptBlockPlacementDTO>;
1188
+ }
1189
+ interface PromptBlockCoreDTO {
1190
+ id: string;
1191
+ name: string;
1192
+ content: string;
1193
+ role: PromptBlockRoleDTO;
1194
+ enabled: boolean;
1195
+ position: PromptBlockPositionDTO;
1196
+ depth: number;
1197
+ /** `"category"` marks a structural category header; other strings are structural insertion markers. */
1198
+ marker: string | null;
1199
+ isLocked: boolean;
1200
+ color: string | null;
1201
+ injectionTrigger: string[];
1202
+ /** Optional character-tag filter. The block is included when the focused character matches. */
1203
+ characterTagTrigger?: string[];
1204
+ group: string | null;
1205
+ /** Only meaningful when `marker === "category"`. Radio categories allow one enabled child; checkbox categories allow many. */
1206
+ categoryMode?: PromptBlockCategoryModeDTO;
1207
+ variables?: PromptVariableDefDTO[];
1208
+ }
1209
+ /**
1210
+ * Editable/public prompt-block value.
1211
+ *
1212
+ * Host placement and sealed/provenance fields are snapshot-only and therefore
1213
+ * cannot be supplied through mutable block or editor inputs.
1214
+ */
1215
+ export interface PromptBlockDTO extends PromptBlockCoreDTO {
1216
+ placementBinding?: never;
1217
+ sealed?: never;
1218
+ sealedKey?: never;
1219
+ sealedSource?: never;
1220
+ sealedOriginPresetId?: never;
1221
+ sealedOriginVersion?: never;
1222
+ sealedSha256?: never;
1223
+ }
1224
+ /**
1225
+ * Host-returned snapshot of a prompt block.
1226
+ *
1227
+ * The placement binding and sealed/provenance fields are native host snapshot
1228
+ * semantics. They are intentionally absent from the editable/public
1229
+ * `PromptBlockDTO` and must not be supplied through mutable block or editor
1230
+ * inputs.
1231
+ */
1232
+ export interface PromptBlockSnapshotDTO extends PromptBlockCoreDTO {
1233
+ placementBinding?: PromptBlockPlacementBindingDTO;
1234
+ sealed?: boolean;
1235
+ sealedKey?: string;
1236
+ sealedSource?: "lumihub" | string;
1237
+ sealedOriginPresetId?: string;
1238
+ sealedOriginVersion?: string | null;
1239
+ sealedSha256?: string;
1240
+ }
1241
+ export interface PromptBlockCategoryGroupDTO {
1242
+ /** The category header block, or null for uncategorized leading blocks. */
1243
+ categoryBlock: PromptBlockSnapshotDTO | null;
1244
+ /** Non-category blocks after the header until the next category header. */
1245
+ children: PromptBlockSnapshotDTO[];
1246
+ }
1247
+ export interface HostResponseErrorDTO {
1248
+ code: string;
1249
+ message: string;
1250
+ presetId?: string;
1251
+ expectedCacheRevision?: number;
1252
+ actualCacheRevision?: number;
1253
+ }
1254
+ export interface UserPresetDTO {
1255
+ id: string;
1256
+ name: string;
1257
+ provider: string;
1258
+ engine: string;
1259
+ parameters: Record<string, unknown>;
1260
+ prompt_order: PromptBlockSnapshotDTO[];
1261
+ prompts: Record<string, unknown>;
1262
+ metadata: Record<string, unknown>;
1263
+ cache_revision: number;
1264
+ created_at: number;
1265
+ updated_at: number;
1266
+ }
1267
+ export interface UserPresetCreateDTO {
1268
+ name: string;
1269
+ provider: string;
1270
+ engine?: string;
1271
+ parameters?: Record<string, unknown>;
1272
+ prompt_order?: PromptBlockDTO[];
1273
+ prompts?: Record<string, unknown>;
1274
+ metadata?: Record<string, unknown>;
1275
+ }
1276
+ export type UserPresetUpdateDTO = Partial<UserPresetCreateDTO> & {
1277
+ expected_cache_revision: number;
1278
+ };
1279
+ export type PromptBlockCreateDTO = Partial<PromptBlockDTO>;
1280
+ export type PromptBlockUpdateDTO = Partial<Omit<PromptBlockDTO, "id">>;
1281
+ /**
1282
+ * Safe representation of a world book exposed to extensions.
1283
+ */
1284
+ export interface WorldBookDTO {
1285
+ id: string;
1286
+ name: string;
1287
+ description: string;
1288
+ metadata: Record<string, unknown>;
1289
+ created_at: number;
1290
+ updated_at: number;
1291
+ }
1292
+ export interface WorldBookCreateDTO {
1293
+ name: string;
1294
+ description?: string;
1295
+ metadata?: Record<string, unknown>;
1296
+ }
1297
+ export interface WorldBookUpdateDTO {
1298
+ name?: string;
1299
+ description?: string;
1300
+ metadata?: Record<string, unknown>;
1301
+ }
1302
+ /**
1303
+ * Full representation of a world book entry exposed to extensions.
1304
+ */
1305
+ export interface WorldBookEntryDTO {
1306
+ id: string;
1307
+ world_book_id: string;
1308
+ uid: string;
1309
+ key: string[];
1310
+ keysecondary: string[];
1311
+ content: string;
1312
+ comment: string;
1313
+ position: number;
1314
+ depth: number;
1315
+ role: string | null;
1316
+ order_value: number;
1317
+ selective: boolean;
1318
+ constant: boolean;
1319
+ disabled: boolean;
1320
+ group_name: string;
1321
+ group_override: boolean;
1322
+ group_weight: number;
1323
+ probability: number;
1324
+ scan_depth: number | null;
1325
+ /** Exclude the synthetic character greeting from lexical activation scans. */
1326
+ exclude_greeting: boolean;
1327
+ case_sensitive: boolean;
1328
+ match_whole_words: boolean;
1329
+ automation_id: string | null;
1330
+ use_regex: boolean;
1331
+ prevent_recursion: boolean;
1332
+ exclude_recursion: boolean;
1333
+ delay_until_recursion: boolean;
1334
+ priority: number;
1335
+ sticky: number;
1336
+ cooldown: number;
1337
+ delay: number;
1338
+ selective_logic: number;
1339
+ use_probability: boolean;
1340
+ vectorized: boolean;
1341
+ extensions: Record<string, unknown>;
1342
+ created_at: number;
1343
+ updated_at: number;
1344
+ }
1345
+ export interface WorldBookEntryCreateDTO {
1346
+ key?: string[];
1347
+ keysecondary?: string[];
1348
+ content?: string;
1349
+ comment?: string;
1350
+ position?: number;
1351
+ depth?: number;
1352
+ role?: string;
1353
+ order_value?: number;
1354
+ selective?: boolean;
1355
+ constant?: boolean;
1356
+ disabled?: boolean;
1357
+ group_name?: string;
1358
+ group_override?: boolean;
1359
+ group_weight?: number;
1360
+ probability?: number;
1361
+ scan_depth?: number;
1362
+ /** Exclude the synthetic character greeting from lexical activation scans. */
1363
+ exclude_greeting?: boolean;
1364
+ case_sensitive?: boolean;
1365
+ match_whole_words?: boolean;
1366
+ automation_id?: string;
1367
+ use_regex?: boolean;
1368
+ prevent_recursion?: boolean;
1369
+ exclude_recursion?: boolean;
1370
+ delay_until_recursion?: boolean;
1371
+ priority?: number;
1372
+ sticky?: number;
1373
+ cooldown?: number;
1374
+ delay?: number;
1375
+ selective_logic?: number;
1376
+ use_probability?: boolean;
1377
+ vectorized?: boolean;
1378
+ extensions?: Record<string, unknown>;
1379
+ }
1380
+ export type WorldBookEntryUpdateDTO = WorldBookEntryCreateDTO;
1381
+ export type RegexPlacementDTO = "user_input" | "ai_output" | "world_info" | "reasoning";
1382
+ export type RegexScopeDTO = "global" | "character" | "chat";
1383
+ export type RegexTargetDTO = "prompt" | "response" | "display";
1384
+ export type RegexMacroModeDTO = "none" | "find" | "raw" | "escaped" | "after";
1385
+ export interface RegexScriptDTO {
1386
+ id: string;
1387
+ /** True when the calling extension may update or delete this script. */
1388
+ readonly can_mutate: boolean;
1389
+ name: string;
1390
+ script_id: string;
1391
+ find_regex: string;
1392
+ replace_string: string;
1393
+ flags: string;
1394
+ placement: RegexPlacementDTO[];
1395
+ scope: RegexScopeDTO;
1396
+ scope_id: string | null;
1397
+ target: RegexTargetDTO;
1398
+ min_depth: number | null;
1399
+ max_depth: number | null;
1400
+ trim_strings: string[];
1401
+ run_on_edit: boolean;
1402
+ substitute_macros: RegexMacroModeDTO;
1403
+ disabled: boolean;
1404
+ sort_order: number;
1405
+ description: string;
1406
+ folder: string;
1407
+ /** Host-validated version badge for this Spindle-owned folder, or null when unversioned/unfiled. */
1408
+ readonly folder_version?: string | null;
1409
+ metadata: Record<string, unknown>;
1410
+ created_at: number;
1411
+ updated_at: number;
1412
+ }
1413
+ export interface RegexScriptListOptionsDTO {
1414
+ scope?: RegexScopeDTO;
1415
+ scopeId?: string;
1416
+ target?: RegexTargetDTO;
1417
+ limit?: number;
1418
+ offset?: number;
1419
+ userId?: string;
1420
+ }
1421
+ export interface RegexScriptActiveOptionsDTO {
1422
+ target: RegexTargetDTO;
1423
+ characterId?: string;
1424
+ chatId?: string;
1425
+ userId?: string;
1426
+ }
1427
+ export interface RegexScriptCreateDTO {
1428
+ name: string;
1429
+ find_regex: string;
1430
+ replace_string?: string;
1431
+ flags?: string;
1432
+ placement?: RegexPlacementDTO[];
1433
+ scope?: RegexScopeDTO;
1434
+ scope_id?: string | null;
1435
+ target?: RegexTargetDTO;
1436
+ min_depth?: number | null;
1437
+ max_depth?: number | null;
1438
+ trim_strings?: string[];
1439
+ run_on_edit?: boolean;
1440
+ substitute_macros?: RegexMacroModeDTO;
1441
+ disabled?: boolean;
1442
+ sort_order?: number;
1443
+ description?: string;
1444
+ folder?: string;
1445
+ /**
1446
+ * Optional version label rendered on the regex folder created by this
1447
+ * extension. It is applied only when `folder` is non-empty. Omit it for the
1448
+ * normal unversioned folder display; pass null or an empty string on update
1449
+ * to clear an existing label.
1450
+ */
1451
+ folder_version?: string | null;
1452
+ metadata?: Record<string, unknown>;
1453
+ script_id?: string;
1454
+ }
1455
+ export type RegexScriptUpdateDTO = Partial<RegexScriptCreateDTO>;
1456
+ /**
1457
+ * One world info entry exposed to a `registerWorldInfoInterceptor` handler.
1458
+ * Subset of `WorldBookEntryDTO` covering the fields the interceptor needs to
1459
+ * inspect for activation gating.
1460
+ */
1461
+ export interface WorldInfoInterceptorEntryDTO {
1462
+ readonly id: string;
1463
+ readonly world_book_id: string;
1464
+ readonly comment: string;
1465
+ readonly disabled: boolean;
1466
+ readonly constant: boolean;
1467
+ readonly extensions: Readonly<Record<string, unknown>>;
1468
+ readonly key: readonly string[];
1469
+ readonly keysecondary: readonly string[];
1470
+ readonly position: number;
1471
+ readonly depth: number;
1472
+ readonly role: string | null;
1473
+ readonly priority: number;
1474
+ readonly probability: number;
1475
+ readonly use_probability: boolean;
1476
+ readonly content: string;
1477
+ readonly automation_id: string | null;
1478
+ readonly selective: boolean;
1479
+ readonly selective_logic: number;
1480
+ readonly group_name: string;
1481
+ readonly group_override: boolean;
1482
+ readonly group_weight: number;
1483
+ readonly match_whole_words: boolean;
1484
+ readonly case_sensitive: boolean;
1485
+ readonly use_regex: boolean;
1486
+ readonly prevent_recursion: boolean;
1487
+ readonly exclude_recursion: boolean;
1488
+ readonly delay_until_recursion: boolean;
1489
+ /** Exclude the synthetic character greeting from lexical activation scans. */
1490
+ readonly exclude_greeting: boolean;
1491
+ readonly scan_depth: number | null;
1492
+ readonly order_value: number;
1493
+ readonly sticky: number;
1494
+ readonly cooldown: number;
1495
+ readonly delay: number;
1496
+ /** Latest prompt-local chat placement supplied by an earlier handler. */
1497
+ readonly placement?: WorldInfoInterceptorPlacementDTO;
1498
+ /** Attachment scope that contributed the entry's book to this chat. */
1499
+ readonly book_source?: WorldBookSourceDTO;
1500
+ }
1501
+ /**
1502
+ * One chat message exposed to a `registerWorldInfoInterceptor` handler.
1503
+ * `index_in_chat` is the zero-based position in the chat's message list and
1504
+ * `is_greeting` is true for the synthetic greeting row at index 0.
1505
+ */
1506
+ export interface WorldInfoInterceptorMessageDTO {
1507
+ readonly id: string;
1508
+ readonly role: "system" | "user" | "assistant";
1509
+ readonly content: string;
1510
+ readonly is_user: boolean;
1511
+ readonly is_greeting: boolean;
1512
+ readonly greeting_index?: number;
1513
+ readonly swipe_id: number;
1514
+ readonly index_in_chat: number;
1515
+ }
1516
+ /** Effective host settings for world-info activation. */
1517
+ export interface WorldInfoActivationSettingsDTO {
1518
+ /** Default entry scan depth, or null to scan all available messages. */
1519
+ readonly globalScanDepth: number | null;
1520
+ readonly maxRecursionPasses: number;
1521
+ }
1522
+ /**
1523
+ * Restrictive, prompt-local changes to world info activation. Overrides from
1524
+ * multiple interceptors compose monotonically: `true` always wins.
1525
+ */
1526
+ export interface WorldInfoActivationOverridesDTO {
1527
+ /** Set the effective recursion-pass limit to zero for this prompt. */
1528
+ readonly disableRecursion?: true;
1529
+ }
1530
+ export type WorldInfoInterceptorRoleDTO = "system" | "user" | "assistant";
1531
+ /**
1532
+ * Prompt-local placement relative to the selected chat history, including its
1533
+ * greeting when present.
1534
+ *
1535
+ * Selected placements use the host's world-info insertion order. Each inserted
1536
+ * entry becomes part of the history sequence used to place the next entry. The
1537
+ * host does not persist these values to the world book.
1538
+ */
1539
+ export interface WorldInfoInterceptorPlacementDTO {
1540
+ readonly type: "chat_depth";
1541
+ readonly role: WorldInfoInterceptorRoleDTO;
1542
+ /** Non-negative integer passed to the selected direction's depth rule. */
1543
+ readonly depth: number;
1544
+ readonly direction: "from_start" | "from_end";
1545
+ }
1546
+ /**
1547
+ * Context passed to a `registerWorldInfoInterceptor` handler. Fires inside
1548
+ * `assemblePrompt` immediately before `activateWorldInfo` runs, so any
1549
+ * `disabled` / `enabled` / `forced` votes affect activation and downstream
1550
+ * token-budget calculation.
1551
+ */
1552
+ export interface WorldInfoInterceptorCtxDTO {
1553
+ readonly chatId: string;
1554
+ readonly characterId: string;
1555
+ readonly userId?: string;
1556
+ readonly entries: readonly WorldInfoInterceptorEntryDTO[];
1557
+ readonly messages: readonly WorldInfoInterceptorMessageDTO[];
1558
+ readonly chatTurn: number;
1559
+ readonly chatMetadata: Readonly<Record<string, unknown>>;
1560
+ readonly activationSettings: WorldInfoActivationSettingsDTO;
1561
+ }
1562
+ /**
1563
+ * Per-entry content overrides emitted by a `registerWorldInfoInterceptor`
1564
+ * handler. Both values are prompt-local and never persist to the world book.
1565
+ */
1566
+ export interface WorldInfoInterceptorMutationDTO {
1567
+ readonly id: string;
1568
+ /** Replaces the entry content used for final prompt insertion. */
1569
+ readonly content?: string;
1570
+ /**
1571
+ * Alternate content used only for empty filtering, content deduplication,
1572
+ * token accounting, and activation caps. Final insertion still uses
1573
+ * `content` (or the stored entry content when `content` is omitted).
1574
+ */
1575
+ readonly selectionContent?: string;
1576
+ /**
1577
+ * Overrides this entry's chat-history placement for the current prompt.
1578
+ * Omit it to retain the stored native position, depth, and role.
1579
+ */
1580
+ readonly placement?: WorldInfoInterceptorPlacementDTO;
1581
+ }
1582
+ /**
1583
+ * Return value of a `registerWorldInfoInterceptor` handler. Each list is
1584
+ * additive — handlers chain in priority order and a later handler sees the
1585
+ * prior handlers' votes applied.
1586
+ */
1587
+ export interface WorldInfoInterceptorResultDTO {
1588
+ readonly disabled?: readonly string[];
1589
+ readonly enabled?: readonly string[];
1590
+ readonly forced?: readonly string[];
1591
+ readonly mutated?: readonly WorldInfoInterceptorMutationDTO[];
1592
+ readonly captured?: readonly string[];
1593
+ readonly activationOverrides?: WorldInfoActivationOverridesDTO;
1594
+ }
1595
+ export type DatabankScopeDTO = "global" | "character" | "chat";
1596
+ export type DatabankDocumentStatusDTO = "pending" | "processing" | "ready" | "error";
1597
+ /** Safe representation of a databank exposed to extensions. */
1598
+ export interface DatabankDTO {
1599
+ id: string;
1600
+ name: string;
1601
+ description: string;
1602
+ scope: DatabankScopeDTO;
1603
+ scope_id: string | null;
1604
+ enabled: boolean;
1605
+ metadata: Record<string, unknown>;
1606
+ document_count?: number;
1607
+ created_at: number;
1608
+ updated_at: number;
1609
+ }
1610
+ export interface DatabankCreateDTO {
1611
+ name: string;
1612
+ description?: string;
1613
+ scope: DatabankScopeDTO;
1614
+ scope_id?: string | null;
1615
+ }
1616
+ export interface DatabankUpdateDTO {
1617
+ name?: string;
1618
+ description?: string;
1619
+ enabled?: boolean;
1620
+ }
1621
+ /** Safe representation of a databank document exposed to extensions. */
1622
+ export interface DatabankDocumentDTO {
1623
+ id: string;
1624
+ databank_id: string;
1625
+ name: string;
1626
+ slug: string;
1627
+ mime_type: string;
1628
+ file_size: number;
1629
+ content_hash: string;
1630
+ total_chunks: number;
1631
+ status: DatabankDocumentStatusDTO;
1632
+ error_message: string | null;
1633
+ metadata: Record<string, unknown>;
1634
+ created_at: number;
1635
+ updated_at: number;
1636
+ }
1637
+ export interface DatabankDocumentCreateDTO {
1638
+ /** Raw file bytes. */
1639
+ data: Uint8Array;
1640
+ /** Original filename including extension. */
1641
+ filename: string;
1642
+ /** Optional MIME type recorded on the document. */
1643
+ mime_type?: string;
1644
+ /** Optional display name override. Defaults to the filename without extension. */
1645
+ name?: string;
1646
+ }
1647
+ export interface DatabankDocumentUpdateDTO {
1648
+ /** Renames the document display name (and derived slug). */
1649
+ name: string;
1650
+ }
1651
+ /**
1652
+ * Safe representation of a persona exposed to extensions.
1653
+ * Omits avatar_path (internal filesystem path) — use image_id for avatar access.
1654
+ */
1655
+ export interface LumiaItemDTO {
1656
+ id: string;
1657
+ pack_id: string;
1658
+ name: string;
1659
+ avatar_url: string | null;
1660
+ author_name: string;
1661
+ definition: string;
1662
+ personality: string;
1663
+ behavior: string;
1664
+ gender_identity: 0 | 1 | 2 | 3;
1665
+ version: string;
1666
+ sort_order: number;
1667
+ created_at: number;
1668
+ updated_at: number;
1669
+ }
1670
+ /** A Loom item category included in a Lumia DLC pack. */
1671
+ export type LoomItemCategoryDTO = "narrative_style" | "loom_utility" | "retrofit";
1672
+ /** A narrative style, utility, or retrofit included in a Lumia DLC pack. */
1673
+ export interface LoomItemDTO {
1674
+ id: string;
1675
+ pack_id: string;
1676
+ name: string;
1677
+ content: string;
1678
+ category: LoomItemCategoryDTO;
1679
+ author_name: string;
1680
+ version: string;
1681
+ sort_order: number;
1682
+ created_at: number;
1683
+ updated_at: number;
1684
+ }
1685
+ /** A tool included in a Lumia DLC pack. */
1686
+ export interface LoomToolDTO {
1687
+ id: string;
1688
+ pack_id: string;
1689
+ tool_name: string;
1690
+ display_name: string;
1691
+ description: string;
1692
+ prompt: string;
1693
+ input_schema: Record<string, unknown>;
1694
+ result_variable: string;
1695
+ store_in_deliberation: boolean;
1696
+ author_name: string;
1697
+ version: string;
1698
+ sort_order: number;
1699
+ created_at: number;
1700
+ updated_at: number;
1701
+ }
1702
+ /**
1703
+ * Extension-safe metadata for a pack in the user's Lumia DLC catalog.
1704
+ * Ownership and the arbitrary pack `extras` blob are intentionally omitted.
1705
+ */
1706
+ export interface LumiaDlcPackDTO {
1707
+ id: string;
1708
+ name: string;
1709
+ author: string;
1710
+ cover_url: string | null;
1711
+ version: string;
1712
+ is_custom: boolean;
1713
+ source_url: string | null;
1714
+ created_at: number;
1715
+ updated_at: number;
1716
+ }
1717
+ /**
1718
+ * All Lumia DLC content currently available to one user. Item `pack_id`
1719
+ * values refer to an entry in `packs`.
1720
+ */
1721
+ export interface LumiaDlcCatalogDTO {
1722
+ packs: LumiaDlcPackDTO[];
1723
+ lumiaItems: LumiaItemDTO[];
1724
+ narrativeStyles: LoomItemDTO[];
1725
+ utilities: LoomItemDTO[];
1726
+ retrofits: LoomItemDTO[];
1727
+ tools: LoomToolDTO[];
1728
+ }
1729
+ export interface PersonaDTO {
1730
+ id: string;
1731
+ name: string;
1732
+ title: string;
1733
+ description: string;
1734
+ image_id: string | null;
1735
+ attached_world_book_id: string | null;
1736
+ folder: string;
1737
+ is_default: boolean;
1738
+ metadata: Record<string, unknown>;
1739
+ created_at: number;
1740
+ updated_at: number;
1741
+ }
1742
+ export interface PersonaCreateDTO {
1743
+ name: string;
1744
+ title?: string;
1745
+ description?: string;
1746
+ folder?: string;
1747
+ is_default?: boolean;
1748
+ attached_world_book_id?: string;
1749
+ metadata?: Record<string, unknown>;
1750
+ }
1751
+ export interface PersonaUpdateDTO {
1752
+ name?: string;
1753
+ title?: string;
1754
+ description?: string;
1755
+ folder?: string;
1756
+ is_default?: boolean;
1757
+ attached_world_book_id?: string;
1758
+ metadata?: Record<string, unknown>;
1759
+ }
1760
+ export interface GlobalAddonDTO {
1761
+ id: string;
1762
+ label: string;
1763
+ content: string;
1764
+ sort_order: number;
1765
+ metadata: Record<string, unknown>;
1766
+ created_at: number;
1767
+ updated_at: number;
1768
+ }
1769
+ export interface GlobalAddonUpdateDTO {
1770
+ label?: string;
1771
+ content?: string;
1772
+ sort_order?: number;
1773
+ metadata?: Record<string, unknown>;
1774
+ }
1775
+ /**
1776
+ * Which attachment scope contributed a world book to prompt assembly.
1777
+ * When a book is attached at multiple scopes the narrowest one wins:
1778
+ * character → persona → chat → global.
1779
+ */
1780
+ export type WorldBookSourceDTO = "character" | "persona" | "chat" | "global";
1781
+ /**
1782
+ * Lightweight summary of an activated world info entry.
1783
+ * Safe subset — no raw entry content or internal fields exposed.
1784
+ */
1785
+ export interface ActivatedWorldInfoEntryDTO {
1786
+ id: string;
1787
+ comment: string;
1788
+ keys: string[];
1789
+ source: "keyword" | "vector";
1790
+ score?: number;
1791
+ /** ID of the world book the entry belongs to. */
1792
+ bookId?: string;
1793
+ /** Attachment scope that contributed the entry's book. */
1794
+ bookSource?: WorldBookSourceDTO;
1795
+ }
1796
+ /** Payload of the `WORLD_INFO_ACTIVATED` event. */
1797
+ export interface WorldInfoActivatedEventDTO {
1798
+ chatId: string;
1799
+ entries: ActivatedWorldInfoEntryDTO[];
1800
+ stats?: Record<string, unknown>;
1801
+ }
1802
+ export interface DryRunRequestDTO {
1803
+ chatId: string;
1804
+ connectionId?: string;
1805
+ personaId?: string;
1806
+ presetId?: string;
1807
+ generationType?: string;
1808
+ parameters?: Record<string, unknown>;
1809
+ }
1810
+ export interface AssemblyBreakdownEntryDTO {
1811
+ type: string;
1812
+ name: string;
1813
+ role?: string;
1814
+ content?: string;
1815
+ blockId?: string;
1816
+ marker?: string;
1817
+ messageCount?: number;
1818
+ firstMessageIndex?: number;
1819
+ preCountedTokens?: number;
1820
+ excludeFromTotal?: boolean;
1821
+ extensionId?: string;
1822
+ extensionName?: string;
1823
+ }
1824
+ export interface ActivationStatsDTO {
1825
+ totalCandidates: number;
1826
+ activatedBeforeBudget: number;
1827
+ activatedAfterBudget: number;
1828
+ evictedByBudget: number;
1829
+ evictedByMinPriority: number;
1830
+ estimatedTokens: number;
1831
+ recursionPassesUsed: number;
1832
+ }
1833
+ export interface MemoryStatsDTO {
1834
+ enabled: boolean;
1835
+ chunksRetrieved: number;
1836
+ chunksAvailable: number;
1837
+ chunksPending: number;
1838
+ injectionMethod: "macro" | "fallback" | "disabled";
1839
+ /**
1840
+ * How the chunks were retrieved: a real vector/hybrid search ("vector") or
1841
+ * the recency fallback ("recency", e.g. when the query embedding failed).
1842
+ * Absent until the chat-memory cache has been populated.
1843
+ */
1844
+ retrievalMode?: "vector" | "recency" | "empty" | "disabled";
1845
+ retrievedChunks: Array<{
1846
+ /**
1847
+ * Vector distance (lower = more similar). `null` for keyword-only or
1848
+ * recency-fallback hits, which have no vector distance — do not treat a
1849
+ * missing score as a perfect (zero-distance) match.
1850
+ */
1851
+ score: number | null;
1852
+ tokenEstimate: number;
1853
+ messageRange: [number, number];
1854
+ preview: string;
1855
+ }>;
1856
+ queryPreview: string;
1857
+ settingsSource: "global" | "per_chat";
1858
+ }
1859
+ export interface DryRunTokenCountDTO {
1860
+ total_tokens: number;
1861
+ breakdown: Array<{
1862
+ name: string;
1863
+ type: string;
1864
+ tokens: number;
1865
+ role?: string;
1866
+ extensionId?: string;
1867
+ extensionName?: string;
1868
+ }>;
1869
+ tokenizer_id: string | null;
1870
+ tokenizer_name: string | null;
1871
+ }
1872
+ export interface DryRunResultDTO {
1873
+ messages: LlmMessageDTO[];
1874
+ breakdown: AssemblyBreakdownEntryDTO[];
1875
+ parameters: Record<string, unknown>;
1876
+ model: string;
1877
+ provider: string;
1878
+ tokenCount?: DryRunTokenCountDTO;
1879
+ worldInfoStats?: ActivationStatsDTO;
1880
+ memoryStats?: MemoryStatsDTO;
1881
+ }
1882
+ /**
1883
+ * Assemble an extension-supplied Loom block graph against a real chat context
1884
+ * without invoking the pre-generation context/interceptor pipeline or an LLM.
1885
+ */
1886
+ export interface AssembleRequestDTO {
1887
+ /** Native Loom prompt blocks to assemble. These replace the saved preset's block graph. */
1888
+ blocks: PromptBlockDTO[];
1889
+ /** Chat supplying history, character, world-info, and macro context. */
1890
+ chatId: string;
1891
+ connectionId?: string;
1892
+ personaId?: string;
1893
+ /** Defaults to `"normal"`. */
1894
+ generationType?: string;
1895
+ /** Per-block prompt-variable values, keyed by block id then variable name. */
1896
+ promptVariables?: PromptVariableValuesDTO;
1897
+ /** Cancel an in-flight assembly. Consumed in the extension worker and not cloned over RPC. */
1898
+ signal?: AbortSignal;
1899
+ }
1900
+ export interface AssembleResultDTO {
1901
+ messages: LlmMessageDTO[];
1902
+ breakdown: AssemblyBreakdownEntryDTO[];
1903
+ }
1904
+ export interface BoundPrefillAttachmentDTO {
1905
+ /**
1906
+ * Opaque parent-prefill attestation. It may be presented only by the live
1907
+ * worker invocation; it is not itself a reusable attachment capability,
1908
+ * message index, or carrier content.
1909
+ */
1910
+ readonly id: string;
1911
+ readonly state: "absent" | "available" | "invalid";
1912
+ }
1913
+ export interface BoundAssembleRequestDTO {
1914
+ blocks: PromptBlockSnapshotDTO[];
1915
+ promptVariableValues?: PromptVariableValuesDTO;
1916
+ dispatch: GenerationDispatchSourceDTO;
1917
+ deadlineAt: number;
1918
+ hookFailureMode?: "degrade" | "reject";
1919
+ macroFailureMode?: "degrade" | "reject";
1920
+ signal?: AbortSignal;
1921
+ }
1922
+ export interface BoundAssemblySuccessDTO {
1923
+ messages: LlmMessageDTO[];
1924
+ breakdown: AssemblyBreakdownEntryDTO[];
1925
+ resolved: {
1926
+ source: "main" | "slot";
1927
+ connectionId: string | null;
1928
+ connectionDispatchRevision: string;
1929
+ dispatchKind: "concrete";
1930
+ };
1931
+ }
1932
+ export type BoundAssemblyFailureDTO = {
1933
+ kind: "hook";
1934
+ code: "ASSEMBLY_HOOK_FAILED";
1935
+ phase: "context" | "world_info" | "macro";
1936
+ reason: "error" | "timeout";
1937
+ message: string;
1938
+ } | {
1939
+ kind: "macro";
1940
+ code: "ASSEMBLY_MACRO_FAILED";
1941
+ reason: "definition" | "parse" | "recursion" | "budget" | "evaluation";
1942
+ message: string;
1943
+ } | {
1944
+ kind: "retrieval_snapshot";
1945
+ code: "ASSEMBLY_RETRIEVAL_SNAPSHOT_UNAVAILABLE";
1946
+ reason: "missing" | "expired" | "unavailable" | "oversize";
1947
+ message: string;
1948
+ } | {
1949
+ kind: "abort";
1950
+ code: "ASSEMBLY_ABORTED";
1951
+ name: "AbortError";
1952
+ message: string;
1953
+ } | {
1954
+ kind: "precondition" | "security" | "internal";
1955
+ code: string;
1956
+ message: string;
1957
+ };
1958
+ export type BoundAssemblyOutcomeDTO = {
1959
+ ok: true;
1960
+ result: BoundAssemblySuccessDTO;
1961
+ } | {
1962
+ ok: false;
1963
+ error: BoundAssemblyFailureDTO;
1964
+ };
1965
+ export interface QuietTrackedRequestDTO {
1966
+ messages: LlmMessageDTO[];
1967
+ dispatch: GenerationDispatchSourceDTO;
1968
+ /**
1969
+ * When supplied, WorkerHost—not extension code—attaches the authenticated
1970
+ * parent assistant carrier as the final continuation carrier for this dispatch.
1971
+ */
1972
+ continuation?: {
1973
+ parentPrefill: BoundPrefillAttachmentDTO;
1974
+ mode: "append-parent-carrier-last";
1975
+ };
1976
+ parameters?: Record<string, unknown>;
1977
+ reasoning?: Record<string, unknown>;
1978
+ tools?: ToolDefinitionDTO[];
1979
+ deadlineAt: number;
1980
+ signal?: AbortSignal;
1981
+ }
1982
+ export interface QuietDispatchReceiptDTO {
1983
+ providerInvoked: boolean;
1984
+ terminalResponse: boolean;
1985
+ source: "main" | "slot";
1986
+ connectionId: string | null;
1987
+ connectionDispatchRevision: string;
1988
+ usage?: GenerationUsageDTO;
1989
+ }
1990
+ export type QuietTrackedResultDTO = {
1991
+ ok: true;
1992
+ response: GenerationResponseDTO;
1993
+ receipt: QuietDispatchReceiptDTO;
1994
+ } | {
1995
+ ok: false;
1996
+ phase: "preflight";
1997
+ providerInvoked: false;
1998
+ receipt: null;
1999
+ error: {
2000
+ kind: "precondition" | "security";
2001
+ code: string;
2002
+ name: string;
2003
+ message: string;
2004
+ };
2005
+ } | {
2006
+ ok: false;
2007
+ phase: "resolved";
2008
+ receipt: QuietDispatchReceiptDTO;
2009
+ error: {
2010
+ kind: "precondition" | "provider" | "abort" | "security" | "internal";
2011
+ code: string;
2012
+ name: string;
2013
+ message: string;
2014
+ };
2015
+ };
2016
+ export interface ChatMemoryChunkDTO {
2017
+ content: string;
2018
+ /**
2019
+ * Vector distance (lower = more similar). `null` for keyword-only or
2020
+ * recency-fallback hits, which have no vector distance — do not treat a
2021
+ * missing score as a perfect (zero-distance) match.
2022
+ */
2023
+ score: number | null;
2024
+ metadata: Record<string, unknown>;
2025
+ }
2026
+ export interface ChatMemoryResultDTO {
2027
+ chunks: ChatMemoryChunkDTO[];
2028
+ formatted: string;
2029
+ count: number;
2030
+ enabled: boolean;
2031
+ queryPreview: string;
2032
+ settingsSource: "global" | "per_chat";
2033
+ chunksAvailable: number;
2034
+ chunksPending: number;
2035
+ /** How chunks were retrieved (real vector search vs. recency fallback). */
2036
+ retrievalMode?: "vector" | "recency" | "empty" | "disabled";
2037
+ }
2038
+ /**
2039
+ * Structured error code included in permission-denied error messages.
2040
+ * Extensions can check `error.startsWith("PERMISSION_DENIED:")` to
2041
+ * programmatically distinguish permission errors from runtime failures.
2042
+ */
2043
+ export declare const PERMISSION_DENIED_PREFIX: "PERMISSION_DENIED:";
2044
+ /**
2045
+ * Detail object delivered via the `permission_denied` host→worker message
2046
+ * when a fire-and-forget registration is blocked by a missing grant.
2047
+ */
2048
+ export interface PermissionDeniedDetail {
2049
+ /** The permission that was required but not granted */
2050
+ permission: string;
2051
+ /** Human-readable description of the operation that was blocked */
2052
+ operation: string;
2053
+ }
2054
+ /**
2055
+ * Detail object delivered via the `permission_changed` host→worker message
2056
+ * when a permission is granted or revoked at runtime (without restart).
2057
+ */
2058
+ export interface PermissionChangedDetail {
2059
+ /** Identifier of the extension whose permission changed */
2060
+ extensionId: string;
2061
+ /** The permission that changed */
2062
+ permission: string;
2063
+ /** Whether the permission was granted (true) or revoked (false) */
2064
+ granted: boolean;
2065
+ /** The full list of currently granted permissions after the change */
2066
+ allGranted: string[];
2067
+ }
2068
+ export type McpTransportTypeDTO = "streamable_http" | "sse" | "stdio";
2069
+ /** Redacted MCP server profile. Connection locations and secret values are never exposed. */
2070
+ export interface McpServerDTO {
2071
+ id: string;
2072
+ name: string;
2073
+ transport_type: McpTransportTypeDTO;
2074
+ has_headers: boolean;
2075
+ env_keys: string[];
2076
+ is_enabled: boolean;
2077
+ auto_connect: boolean;
2078
+ last_connected_at: number | null;
2079
+ created_at: number;
2080
+ updated_at: number;
2081
+ }
2082
+ export interface McpServerCreateDTO {
2083
+ name: string;
2084
+ transport_type: McpTransportTypeDTO;
2085
+ url?: string;
2086
+ command?: string;
2087
+ args?: string[];
2088
+ env?: Record<string, string>;
2089
+ headers?: Record<string, string>;
2090
+ is_enabled?: boolean;
2091
+ /** Defaults to false when created through Spindle; true also requires `mcp_servers`. */
2092
+ auto_connect?: boolean;
2093
+ metadata?: Record<string, unknown>;
2094
+ }
2095
+ export interface McpToolDTO {
2096
+ server_id: string;
2097
+ server_name: string;
2098
+ name: string;
2099
+ description: string;
2100
+ input_schema: Record<string, unknown>;
2101
+ }
2102
+ export interface McpServerStatusDTO {
2103
+ id: string;
2104
+ connected: boolean;
2105
+ tool_count: number;
2106
+ tools: McpToolDTO[];
2107
+ error?: string;
2108
+ }
2109
+ export interface McpToolCallOptionsDTO {
2110
+ userId?: string;
2111
+ /** Clamped by the host to 1–120 seconds. */
2112
+ timeoutMs?: number;
2113
+ }
2114
+ /** Identifies which provider backs the user's configured web search engine. */
2115
+ export type WebSearchProviderDTO = "searxng";
2116
+ /**
2117
+ * Safe view of a user's web search configuration. The raw API key is never
2118
+ * exposed — only `hasApiKey` indicates whether one is on file.
2119
+ */
2120
+ export interface WebSearchSettingsDTO {
2121
+ enabled: boolean;
2122
+ provider: WebSearchProviderDTO;
2123
+ apiUrl: string;
2124
+ requestTimeoutMs: number;
2125
+ defaultResultCount: number;
2126
+ maxResultCount: number;
2127
+ maxPagesToScrape: number;
2128
+ maxCharsPerPage: number;
2129
+ language: string;
2130
+ safeSearch: 0 | 1 | 2;
2131
+ engines: string[];
2132
+ hasApiKey: boolean;
2133
+ }
2134
+ /** A single search result row, normalized across providers. */
2135
+ export interface WebSearchResultDTO {
2136
+ title: string;
2137
+ url: string;
2138
+ snippet: string;
2139
+ /** Provider-reported engine identifier when available (e.g. `"google"`, `"bing"`). */
2140
+ engine?: string;
2141
+ /** Provider-reported relevance score when available. */
2142
+ score?: number;
2143
+ }
2144
+ /**
2145
+ * A search result enriched with scraped page content. Only present when the
2146
+ * query was run with `scrape: true` (the default).
2147
+ */
2148
+ export interface WebSearchDocumentDTO {
2149
+ title: string;
2150
+ url: string;
2151
+ snippet: string;
2152
+ /** How the page content was extracted (e.g. `"html"`, `"pdf"`). */
2153
+ sourceType?: string;
2154
+ /** Extracted page text, clipped to `WebSearchSettingsDTO.maxCharsPerPage`. */
2155
+ content?: string;
2156
+ /** Length of the source page content before clipping. */
2157
+ contentLength?: number;
2158
+ /** Populated when scraping failed for this result; `content` is then absent. */
2159
+ error?: string;
2160
+ }
2161
+ /** Options forwarded to `spindle.webSearch.query()`. */
2162
+ export interface WebSearchRequestDTO {
2163
+ /** Free-text search query. Trimmed by the host; empty values are rejected. */
2164
+ query: string;
2165
+ /**
2166
+ * Desired number of results. Clamped to `WebSearchSettingsDTO.maxResultCount`
2167
+ * on the host; omit to use the user's `defaultResultCount`.
2168
+ */
2169
+ count?: number;
2170
+ /**
2171
+ * When `true` (default), the host scrapes the first
2172
+ * `WebSearchSettingsDTO.maxPagesToScrape` results, fills in
2173
+ * `documents[].content`, and assembles the `context` block. Set to `false`
2174
+ * to skip scraping entirely — only `results` are returned, and
2175
+ * `documents` / `context` are omitted from the response. Useful when the
2176
+ * extension only needs titles, URLs, and snippets.
2177
+ */
2178
+ scrape?: boolean;
2179
+ /** For operator-scoped extensions; ignored on user-scoped extensions. */
2180
+ userId?: string;
2181
+ }
2182
+ /**
2183
+ * Result of a successful `spindle.webSearch.query()` call. `documents` and
2184
+ * `context` are omitted when the request was issued with `scrape: false`.
2185
+ */
2186
+ export interface WebSearchResponseDTO {
2187
+ /** The (trimmed) query that was executed. */
2188
+ query: string;
2189
+ /** Raw normalized results from the search provider. */
2190
+ results: WebSearchResultDTO[];
2191
+ /** Per-result scraped page content. Absent when `scrape: false`. */
2192
+ documents?: WebSearchDocumentDTO[];
2193
+ /**
2194
+ * Pre-assembled, prompt-ready context block summarizing the query plus the
2195
+ * scraped documents. Absent when `scrape: false`.
2196
+ */
2197
+ context?: string;
2198
+ }
2199
+ /**
2200
+ * Theme override payload sent by extensions to customize the UI appearance.
2201
+ * Overrides are applied on top of the user's current theme and automatically
2202
+ * removed when the extension is disabled or unloaded.
2203
+ */
2204
+ export interface ThemeOverrideDTO {
2205
+ /**
2206
+ * Direct CSS variable overrides applied regardless of light/dark mode.
2207
+ * Keys are CSS custom property names (e.g. `--lumiverse-primary`).
2208
+ * Values must be valid CSS values.
2209
+ *
2210
+ * Common variable groups:
2211
+ * - **Primary accent**: `--lumiverse-primary`, `--lumiverse-primary-hover`, `-text`, `-muted`, `-light`, `-010`…`-050`, `-contrast`
2212
+ * - **Backgrounds**: `--lumiverse-bg`, `-elevated`, `-hover`, `-dark`, `-darker`, `-deep`, `-040`…`-070`
2213
+ * - **Text**: `--lumiverse-text`, `-muted`, `-dim`, `-hint`
2214
+ * - **Borders**: `--lumiverse-border`, `-hover`, `-light`, `-neutral`, `-neutral-hover`
2215
+ * - **Status**: `--lumiverse-danger`, `--lumiverse-success`, `--lumiverse-warning` (+ `-015`, `-020`, `-050` variants)
2216
+ * - **Glass**: `--lcs-glass-bg`, `-bg-hover`, `-border`, `-border-hover`, `-blur`, `-soft-blur`, `-strong-blur`
2217
+ * - **Prose**: `--lumiverse-prose-italic`, `-bold`, `-dialogue`, `-blockquote`, `-link`
2218
+ * - **Shadows**: `--lumiverse-shadow`, `-sm`, `-md`, `-lg`, `-xl`
2219
+ * - **Radii**: `--lumiverse-radius`, `-sm`, `-md`, `-lg`, `-xl`, `--lcs-radius`, `-sm`, `-xs`
2220
+ * - **Fills**: `--lumiverse-fill`, `-subtle`, `-hover`, `-medium`, `-strong`, `-heavy`, `-deepest`
2221
+ * - **Cards**: `--lumiverse-card-bg`, `--lumiverse-card-image-bg`
2222
+ * - **Icons**: `--lumiverse-icon`, `-muted`, `-dim`
2223
+ * - **Modals**: `--lumiverse-modal-backdrop`, `--lumiverse-gradient-modal`, `--lumiverse-swatch-border`
2224
+ * - **Typography**: `--lumiverse-font-family`, `--lumiverse-font-mono`, `--lumiverse-font-scale`
2225
+ * - **Transitions**: `--lumiverse-transition`, `--lumiverse-transition-fast`, `--lcs-transition`, `--lcs-transition-fast`
2226
+ */
2227
+ variables?: Record<string, string>;
2228
+ /**
2229
+ * Mode-specific CSS variable overrides. When the user switches between
2230
+ * light and dark mode, the frontend selects the matching set.
2231
+ * Mode-specific values override flat `variables` for the same key.
2232
+ */
2233
+ variablesByMode?: {
2234
+ dark?: Record<string, string>;
2235
+ light?: Record<string, string>;
2236
+ };
2237
+ }
2238
+ /**
2239
+ * RGB color value (0–255 per channel).
2240
+ */
2241
+ export interface ColorRGB {
2242
+ r: number;
2243
+ g: number;
2244
+ b: number;
2245
+ }
2246
+ /**
2247
+ * HSL color value (h: 0–360, s: 0–100, l: 0–100).
2248
+ */
2249
+ export interface ColorHSL {
2250
+ h: number;
2251
+ s: number;
2252
+ l: number;
2253
+ }
2254
+ /**
2255
+ * Result of extracting colors from an image.
2256
+ * Each region's dominant color is returned along with metadata.
2257
+ */
2258
+ export interface ColorExtractionResult {
2259
+ /** Overall dominant color of the full image */
2260
+ dominant: ColorRGB;
2261
+ /** Dominant color per sampled region */
2262
+ regions: {
2263
+ top: ColorRGB;
2264
+ center: ColorRGB;
2265
+ bottom: ColorRGB;
2266
+ left: ColorRGB;
2267
+ right: ColorRGB;
2268
+ };
2269
+ /** Per-region flatness score (0–1). High values = monotone/solid region. */
2270
+ flatness: {
2271
+ top: number;
2272
+ center: number;
2273
+ bottom: number;
2274
+ left: number;
2275
+ right: number;
2276
+ full: number;
2277
+ };
2278
+ /** Simple average color across all sampled pixels */
2279
+ average: ColorRGB;
2280
+ /** Whether the dominant color is perceived as light (luminance > 152) */
2281
+ isLight: boolean;
2282
+ /** HSL representation of the dominant color */
2283
+ dominantHsl: ColorHSL;
2284
+ }
2285
+ /**
2286
+ * Read-only snapshot of the user's current theme configuration.
2287
+ */
2288
+ export interface ThemeInfoDTO {
2289
+ /** Theme preset ID (e.g. `"lumiverse-purple"`, `"character-aware"`) */
2290
+ id: string;
2291
+ /** Display name of the theme */
2292
+ name: string;
2293
+ /** Resolved mode — always `"light"` or `"dark"`, never `"system"` */
2294
+ mode: "light" | "dark";
2295
+ /** Primary accent color in HSL (hue 0-360, saturation 0-100, lightness 0-100) */
2296
+ accent: {
2297
+ h: number;
2298
+ s: number;
2299
+ l: number;
2300
+ };
2301
+ /** Whether glassmorphic backdrop-filter effects are enabled */
2302
+ enableGlass: boolean;
2303
+ /** Border radius multiplier (1.0 = default) */
2304
+ radiusScale: number;
2305
+ /** Font size multiplier (1.0 = default) */
2306
+ fontScale: number;
2307
+ /** Full UI zoom multiplier (1.0 = default, affects all elements via CSS zoom) */
2308
+ uiScale: number;
2309
+ /** Whether the theme dynamically adapts to the active character's avatar */
2310
+ characterAware: boolean;
2311
+ }
2312
+ /**
2313
+ * Input config for `spindle.theme.generateVariables()`.
2314
+ *
2315
+ * Mirrors the inputs that Lumiverse's theme engine uses to produce the full
2316
+ * set of ~80+ CSS variables. Extensions can use the result as a complete,
2317
+ * coherent override set for `spindle.theme.apply()`.
2318
+ */
2319
+ export interface ThemeVariablesConfigDTO {
2320
+ /** Primary accent color in HSL. */
2321
+ accent: {
2322
+ h: number;
2323
+ s: number;
2324
+ l: number;
2325
+ };
2326
+ /** Resolved color mode. */
2327
+ mode: "dark" | "light";
2328
+ /** Enable glassmorphic backdrop-filter tokens (default: `true`). */
2329
+ enableGlass?: boolean;
2330
+ /** Border radius multiplier (default: `1`). */
2331
+ radiusScale?: number;
2332
+ /** Font size multiplier (default: `1`). */
2333
+ fontScale?: number;
2334
+ /** Full UI zoom multiplier (default: `1`). */
2335
+ uiScale?: number;
2336
+ /** Optional base color overrides. Each value is a CSS color string. */
2337
+ baseColors?: {
2338
+ primary?: string;
2339
+ secondary?: string;
2340
+ background?: string;
2341
+ text?: string;
2342
+ danger?: string;
2343
+ success?: string;
2344
+ warning?: string;
2345
+ /** Dialogue / speech color override. */
2346
+ speech?: string;
2347
+ /** Italic / thoughts color override. */
2348
+ thoughts?: string;
2349
+ };
2350
+ /** Status color overrides (danger, success, warning). */
2351
+ statusColors?: {
2352
+ danger?: string;
2353
+ success?: string;
2354
+ warning?: string;
2355
+ };
2356
+ }
2357
+ /**
2358
+ * Input config for `spindle.theme.applyPalette()`.
2359
+ *
2360
+ * This is the safe, presentation-owned path for live extension theming.
2361
+ * Extensions provide palette intent only; Lumiverse preserves the user's
2362
+ * radius, glass, font, and UI-scale settings and generates the final
2363
+ * mode-aware variable maps itself. Pass `null` to clear a previously applied
2364
+ * palette override when no valid color data is available.
2365
+ */
2366
+ export interface ThemePaletteConfigDTO {
2367
+ /** Primary accent color in HSL. */
2368
+ accent: {
2369
+ h: number;
2370
+ s: number;
2371
+ l: number;
2372
+ };
2373
+ }
2374
+ /**
2375
+ * Structured content items for backend-initiated modals (`spindle.modal.open`).
2376
+ * The host renders these into the modal body using system theming.
2377
+ * For full DOM control, use the frontend `ctx.ui.showModal()` API instead.
2378
+ */
2379
+ export type SpindleModalItemDTO =
2380
+ /** A block of text content. Supports multiline via newlines. */
2381
+ {
2382
+ type: "text";
2383
+ content: string;
2384
+ muted?: boolean;
2385
+ }
2386
+ /** A horizontal divider line. */
2387
+ | {
2388
+ type: "divider";
2389
+ }
2390
+ /** A label–value pair displayed in a horizontal row. */
2391
+ | {
2392
+ type: "key_value";
2393
+ label: string;
2394
+ value: string;
2395
+ }
2396
+ /** A section heading within the modal body. */
2397
+ | {
2398
+ type: "heading";
2399
+ content: string;
2400
+ }
2401
+ /** A themed card/container that groups child items. */
2402
+ | {
2403
+ type: "card";
2404
+ items: SpindleModalItemDTO[];
2405
+ };
2406
+ /**
2407
+ * Command registration payload sent by extensions to add entries
2408
+ * to the Lumiverse command palette (Cmd/Ctrl+K).
2409
+ *
2410
+ * Commands are contextual — extensions can register different sets
2411
+ * based on the current chat, page, or app state by calling
2412
+ * `spindle.commands.register()` with an updated list at any time.
2413
+ * Each call replaces all previously registered commands from that extension.
2414
+ */
2415
+ export interface SpindleCommandDTO {
2416
+ /** Unique identifier for this command within the extension (e.g. `"summarize-chat"`). */
2417
+ id: string;
2418
+ /** Display label shown in the command palette. Max 80 characters. */
2419
+ label: string;
2420
+ /** Description shown below the label. Max 200 characters. */
2421
+ description: string;
2422
+ /** Optional search keywords for fuzzy matching. Max 10 keywords, 30 chars each. */
2423
+ keywords?: string[];
2424
+ /**
2425
+ * Scope restriction controlling when the command appears.
2426
+ * - `'global'` — always visible (default)
2427
+ * - `'chat'` — only when viewing a chat
2428
+ * - `'chat-idle'` — only when in a chat and not streaming
2429
+ * - `'landing'` — only on the home page
2430
+ * - `'character'` — only on character pages
2431
+ */
2432
+ scope?: "global" | "chat" | "chat-idle" | "landing" | "character";
2433
+ }
2434
+ /**
2435
+ * Context snapshot sent to the extension when a command is invoked
2436
+ * from the command palette. Contains the frontend's current UI state
2437
+ * so the extension can act on the right chat/character/page.
2438
+ */
2439
+ export interface SpindleCommandContextDTO {
2440
+ /** Current route path (e.g. `"/chat/abc-123"`, `"/"`, `"/characters/xyz"`). */
2441
+ route: string;
2442
+ /** Active chat ID, if the user is in a chat view. */
2443
+ chatId?: string;
2444
+ /** Active character ID, if available. */
2445
+ characterId?: string;
2446
+ /** Whether the active chat is a group chat. */
2447
+ isGroupChat?: boolean;
2448
+ }
2449
+ /**
2450
+ * Read-only snapshot of a drawer tab discoverable via
2451
+ * {@link SpindleAPI.ui.getDrawerTabs}. Mirrors the metadata used by the
2452
+ * built-in Command Palette to render the "Panels" group.
2453
+ */
2454
+ export interface SpindleUIDrawerTabDTO {
2455
+ /** Stable id used by `openDrawerTab(id)`. */
2456
+ id: string;
2457
+ /** Short label shown beneath the sidebar icon (max ~8 characters). */
2458
+ shortName: string;
2459
+ /** Full title shown in menus and the command palette. */
2460
+ tabName: string;
2461
+ /** One-line description shown in the command palette. */
2462
+ tabDescription: string;
2463
+ /** Keywords used for command-palette fuzzy search. */
2464
+ keywords: string[];
2465
+ /** Whether the tab is built into Lumiverse or contributed by another extension. */
2466
+ source: "builtin" | "extension";
2467
+ /** For extension-contributed tabs, the owning extension's identifier. */
2468
+ extensionId?: string;
2469
+ }
2470
+ /**
2471
+ * Read-only snapshot of a settings tab discoverable via
2472
+ * {@link SpindleAPI.ui.getSettingsTabs}. Restricted entries (`role` set) are
2473
+ * filtered out when the call resolves to a user that lacks the required role.
2474
+ */
2475
+ export interface SpindleUISettingsTabDTO {
2476
+ /** Stable id used by `openSettings(id)`. */
2477
+ id: string;
2478
+ /** Short label shown in the settings sidebar. */
2479
+ shortName: string;
2480
+ /** Full title shown in the settings header / command palette. */
2481
+ tabName: string;
2482
+ /** One-line description shown in the command palette. */
2483
+ tabDescription: string;
2484
+ /** Keywords used for command-palette fuzzy search. */
2485
+ keywords: string[];
2486
+ /** Set when the tab is only visible to certain roles. */
2487
+ role?: "admin" | "owner";
2488
+ }
2489
+ /** High-level lifecycle state for a frontend process tracked by the backend host. */
2490
+ export type FrontendProcessStateDTO = "starting" | "running" | "stopping" | "stopped" | "completed" | "failed" | "timed_out";
2491
+ /** Terminal reason attached to lifecycle events and snapshots when available. */
2492
+ export type FrontendProcessExitReasonDTO = "completed" | "failed" | "stopped" | "timed_out" | "frontend_unloaded" | "backend_unloaded" | "replaced";
2493
+ /** Options used when spawning a tracked frontend process from the backend worker. */
2494
+ export interface FrontendProcessSpawnOptionsDTO {
2495
+ /** Frontend handler key registered via `ctx.processes.register(kind, ...)`. */
2496
+ kind: string;
2497
+ /** Optional extension-defined stable key used for dedupe / replacement semantics. */
2498
+ key?: string;
2499
+ /** Optional process-scoped startup payload delivered to the frontend handler. */
2500
+ payload?: unknown;
2501
+ /** Arbitrary metadata stored alongside the process snapshot for backend bookkeeping. */
2502
+ metadata?: Record<string, unknown>;
2503
+ /** For operator-scoped extensions only. */
2504
+ userId?: string;
2505
+ /** Reject spawn if the frontend does not call `process.ready()` within this window. */
2506
+ startupTimeoutMs?: number;
2507
+ /** Mark the process timed out if the frontend stops heartbeating for this long after ready. */
2508
+ heartbeatTimeoutMs?: number;
2509
+ /** Replace any existing process with the same `key` for the target user. */
2510
+ replaceExisting?: boolean;
2511
+ }
2512
+ /** Filter used for controller list queries. */
2513
+ export interface FrontendProcessListOptionsDTO {
2514
+ userId?: string;
2515
+ kind?: string;
2516
+ key?: string;
2517
+ state?: FrontendProcessStateDTO;
2518
+ }
2519
+ /** Current host-tracked snapshot of a frontend process. */
2520
+ export interface FrontendProcessInfoDTO {
2521
+ processId: string;
2522
+ kind: string;
2523
+ key?: string;
2524
+ state: FrontendProcessStateDTO;
2525
+ userId?: string;
2526
+ metadata?: Record<string, unknown>;
2527
+ startedAt: string;
2528
+ readyAt?: string;
2529
+ lastHeartbeatAt?: string;
2530
+ endedAt?: string;
2531
+ exitReason?: FrontendProcessExitReasonDTO;
2532
+ error?: string;
2533
+ }
2534
+ /** Lifecycle event emitted to backend workers for tracked frontend processes. */
2535
+ export interface FrontendProcessLifecycleEventDTO {
2536
+ processId: string;
2537
+ kind: string;
2538
+ key?: string;
2539
+ userId?: string;
2540
+ state: FrontendProcessStateDTO;
2541
+ previousState?: FrontendProcessStateDTO;
2542
+ at: string;
2543
+ exitReason?: FrontendProcessExitReasonDTO;
2544
+ error?: string;
2545
+ metadata?: Record<string, unknown>;
2546
+ }
2547
+ /** Options for graceful process termination. */
2548
+ export interface FrontendProcessStopOptionsDTO {
2549
+ userId?: string;
2550
+ /** Optional reason surfaced to the frontend process' stop handler. */
2551
+ reason?: string;
2552
+ }
2553
+ /** High-level lifecycle state for an isolated backend subprocess tracked by the host. */
2554
+ export type BackendProcessStateDTO = "starting" | "running" | "stopping" | "stopped" | "completed" | "failed" | "timed_out";
2555
+ /** Terminal reason attached to backend-process lifecycle events and snapshots when available. */
2556
+ export type BackendProcessExitReasonDTO = "completed" | "failed" | "stopped" | "timed_out" | "backend_unloaded" | "replaced";
2557
+ /** Options used when spawning an isolated backend subprocess from the backend worker. */
2558
+ export interface BackendProcessSpawnOptionsDTO {
2559
+ /** Built JS entry file under the extension repo, typically in `dist/`. */
2560
+ entry: string;
2561
+ /** Optional logical label used for lifecycle filtering and dedupe semantics. Defaults to `entry`. */
2562
+ kind?: string;
2563
+ /** Optional extension-defined stable key used for dedupe / replacement semantics. */
2564
+ key?: string;
2565
+ /** Optional process-scoped startup payload delivered to the subprocess entry. */
2566
+ payload?: unknown;
2567
+ /** Arbitrary metadata stored alongside the process snapshot for backend bookkeeping. */
2568
+ metadata?: Record<string, unknown>;
2569
+ /** For operator-scoped extensions only. */
2570
+ userId?: string;
2571
+ /** Reject spawn if the subprocess does not call `process.ready()` within this window. */
2572
+ startupTimeoutMs?: number;
2573
+ /** Mark the subprocess timed out if it stops heartbeating for this long after ready. */
2574
+ heartbeatTimeoutMs?: number;
2575
+ /** Replace any existing process with the same `key` for the target user. */
2576
+ replaceExisting?: boolean;
2577
+ }
2578
+ /** Filter used for backend subprocess list queries. */
2579
+ export interface BackendProcessListOptionsDTO {
2580
+ userId?: string;
2581
+ kind?: string;
2582
+ key?: string;
2583
+ state?: BackendProcessStateDTO;
2584
+ }
2585
+ /** Current host-tracked snapshot of an isolated backend subprocess. */
2586
+ export interface BackendProcessInfoDTO {
2587
+ processId: string;
2588
+ entry: string;
2589
+ kind: string;
2590
+ key?: string;
2591
+ state: BackendProcessStateDTO;
2592
+ userId?: string;
2593
+ metadata?: Record<string, unknown>;
2594
+ startedAt: string;
2595
+ readyAt?: string;
2596
+ lastHeartbeatAt?: string;
2597
+ endedAt?: string;
2598
+ exitReason?: BackendProcessExitReasonDTO;
2599
+ error?: string;
2600
+ }
2601
+ /** Lifecycle event emitted to backend workers for isolated backend subprocesses. */
2602
+ export interface BackendProcessLifecycleEventDTO {
2603
+ processId: string;
2604
+ entry: string;
2605
+ kind: string;
2606
+ key?: string;
2607
+ userId?: string;
2608
+ state: BackendProcessStateDTO;
2609
+ previousState?: BackendProcessStateDTO;
2610
+ at: string;
2611
+ exitReason?: BackendProcessExitReasonDTO;
2612
+ error?: string;
2613
+ metadata?: Record<string, unknown>;
2614
+ }
2615
+ /** Options for graceful isolated-backend-process termination. */
2616
+ export interface BackendProcessStopOptionsDTO {
2617
+ userId?: string;
2618
+ /** Optional reason surfaced to the subprocess stop handler. */
2619
+ reason?: string;
2620
+ }
2621
+ /** Payload for `GENERATION_STARTED` events. */
2622
+ export interface GenerationStartedPayloadDTO {
2623
+ generationId: string;
2624
+ chatId: string;
2625
+ model: string;
2626
+ targetMessageId?: string;
2627
+ characterId?: string;
2628
+ characterName?: string;
2629
+ breakdown?: AssemblyBreakdownEntryDTO[];
2630
+ /** The type of generation: normal, continue, regenerate, swipe, or impersonate. */
2631
+ generationType?: string;
2632
+ }
2633
+ /** Payload for `STREAM_TOKEN_RECEIVED` events. */
2634
+ export interface StreamTokenPayloadDTO {
2635
+ generationId: string;
2636
+ chatId: string;
2637
+ /** The token text chunk. */
2638
+ token: string;
2639
+ /** Monotonic sequence number for deduplication on reconnect. */
2640
+ seq: number;
2641
+ /** Present and set to `"reasoning"` for chain-of-thought tokens. */
2642
+ type?: "reasoning";
2643
+ }
2644
+ /** Payload for `GENERATION_ENDED` events. */
2645
+ export interface GenerationEndedPayloadDTO {
2646
+ generationId: string;
2647
+ chatId: string;
2648
+ /** ID of the saved message (absent on error). */
2649
+ messageId?: string;
2650
+ /** Final generated content (absent on error). */
2651
+ content?: string;
2652
+ /** Error message when the generation failed. */
2653
+ error?: string;
2654
+ /** The type of generation: normal, continue, regenerate, swipe, or impersonate. */
2655
+ generationType?: string;
2656
+ }
2657
+ /** Payload for `GENERATION_STOPPED` events (user-initiated stop). */
2658
+ export interface GenerationStoppedPayloadDTO {
2659
+ generationId: string;
2660
+ chatId: string;
2661
+ /** Partial content accumulated before the stop. */
2662
+ content?: string;
2663
+ }
2664
+ /**
2665
+ * Wire shape of a chat message as delivered in WebSocket event payloads
2666
+ * (e.g. `MESSAGE_SENT`, `MESSAGE_EDITED`, `MESSAGE_SWIPED`).
2667
+ *
2668
+ * `spindle.chat.getMessages()` returns this backend shape plus normalized
2669
+ * convenience fields such as `role` and `metadata`.
2670
+ */
2671
+ export interface ChatMessageDTO {
2672
+ id: string;
2673
+ chat_id: string;
2674
+ index_in_chat: number;
2675
+ is_user: boolean;
2676
+ name: string;
2677
+ /** The currently active swipe content (mirrors `swipes[swipe_id]`). */
2678
+ content: string;
2679
+ send_date: number;
2680
+ /** Index of the active swipe in `swipes`. `0` when the message has no alternates. */
2681
+ swipe_id: number;
2682
+ /** All swipe variants for this message, including the currently active one. */
2683
+ swipes: string[];
2684
+ /** Per-swipe creation timestamps (unix epoch seconds), aligned with `swipes`. */
2685
+ swipe_dates: number[];
2686
+ /** Free-form metadata bag (attachments, spindle metadata, reasoning, etc.). */
2687
+ extra: Record<string, unknown>;
2688
+ parent_message_id: string | null;
2689
+ branch_id: string | null;
2690
+ created_at: number;
2691
+ }
2692
+ /**
2693
+ * Distinguishes the four ways a `MESSAGE_SWIPED` event can be triggered.
2694
+ *
2695
+ * - `'added'` — a new swipe variant was created (e.g. regenerate, manual add)
2696
+ * - `'updated'` — an existing swipe's content was edited in place
2697
+ * - `'deleted'` — a swipe variant was removed from the message
2698
+ * - `'navigated'` — the user (or an extension) cycled the active swipe slot
2699
+ */
2700
+ export type MessageSwipeAction = "added" | "updated" | "deleted" | "navigated";
2701
+ /**
2702
+ * Payload for `MESSAGE_SWIPED` events.
2703
+ *
2704
+ * The discriminator fields (`action`, `swipeId`, `previousSwipeId`) let
2705
+ * extensions tell the four swipe operations apart and maintain swipe-keyed
2706
+ * state correctly without diffing the `swipes` array on every event.
2707
+ */
2708
+ export interface MessageSwipedPayloadDTO {
2709
+ chatId: string;
2710
+ /** The full message after the mutation. */
2711
+ message: ChatMessageDTO;
2712
+ /** Distinguishes which swipe operation produced this event. */
2713
+ action: MessageSwipeAction;
2714
+ /**
2715
+ * The swipe index this event concerns. Semantics depend on `action`:
2716
+ *
2717
+ * - `'added'` — index of the new swipe (equal to `message.swipe_id` post-add)
2718
+ * - `'updated'` — index of the edited swipe (may or may not equal `message.swipe_id`)
2719
+ * - `'deleted'` — index that was removed. Note: this slot is no longer present
2720
+ * in `message.swipes`, and `message.swipe_id` may have shifted
2721
+ * if the deleted slot was at or before the previously active one.
2722
+ * - `'navigated'` — destination index (equal to `message.swipe_id`)
2723
+ */
2724
+ swipeId: number;
2725
+ /**
2726
+ * For `'navigated'` and `'deleted'` events: the active swipe index *before*
2727
+ * the change. Useful for direction detection on navigation, and for detecting
2728
+ * whether the active slot was the one removed on deletion. Omitted for
2729
+ * `'added'` and `'updated'`.
2730
+ */
2731
+ previousSwipeId?: number;
2732
+ }
2733
+ /**
2734
+ * Payload for `SWIPE_EDITED` events.
2735
+ *
2736
+ * Fires when `spindle.chat.updateMessage()` explicitly supplies one or more
2737
+ * swipe-shaped fields (`swipes`, `swipe_id`, or `swipe_dates`). Plain content
2738
+ * edits that mirror into the active swipe slot continue to emit only
2739
+ * `MESSAGE_EDITED` — this event is for extension-driven rewrites of the
2740
+ * swipe array itself, index navigation, or date-array rewrites.
2741
+ *
2742
+ * `MESSAGE_SWIPED` still fires for the dedicated swipe REST routes
2743
+ * (`addSwipe`, `updateSwipe`, `deleteSwipe`, `cycleSwipe`) with its
2744
+ * `action` discriminator. Consumers that need fine-grained action semantics
2745
+ * should prefer `MESSAGE_SWIPED`; `SWIPE_EDITED` is intentionally coarser.
2746
+ */
2747
+ export interface SwipeEditedPayloadDTO {
2748
+ chatId: string;
2749
+ /** The full message after the mutation. */
2750
+ message: ChatMessageDTO;
2751
+ /** Active swipe index before the mutation. Equals `message.swipe_id` when navigation did not occur. */
2752
+ previousSwipeId: number;
2753
+ }
2754
+ /**
2755
+ * Payload delivered to `spindle.on("TOOL_INVOCATION", ...)` handlers.
2756
+ *
2757
+ * Fires whenever an extension-registered tool is invoked by Lumiverse. Handlers
2758
+ * must return a string (or promise thereof) with the tool's result — the host
2759
+ * coerces `undefined` / `null` to an empty string.
2760
+ *
2761
+ * `councilMember` is populated when the invocation originates from a council
2762
+ * execution cycle, providing the assigned member's identity, role, chance,
2763
+ * avatar URL, and Lumia personality fields. It is `undefined` for all other
2764
+ * invocation paths.
2765
+ *
2766
+ * `contextMessages` is populated when the invocation originates from a council
2767
+ * execution cycle — carrying the structured chat context (system enrichment +
2768
+ * chat history) that was assembled for this member. Extensions can inspect
2769
+ * role boundaries directly instead of re-parsing the flattened `args.context`
2770
+ * string. Multi-part message content is flattened to its text portion before
2771
+ * being delivered. `undefined` for non-council invocation paths.
2772
+ */
2773
+ export interface ToolInvocationPayloadDTO {
2774
+ /** The bare (unqualified) tool name, matching what was passed to `registerTool`. */
2775
+ toolName: string;
2776
+ /** Arguments delivered to the tool. Shape depends on the tool's JSON Schema. */
2777
+ args: Record<string, unknown>;
2778
+ /** Host-side correlation id for this invocation. */
2779
+ requestId: string;
2780
+ /** Council member snapshot when invoked via council — otherwise `undefined`. */
2781
+ councilMember?: CouncilMemberContext;
2782
+ /**
2783
+ * Structured chat context for council invocations — preserves role
2784
+ * boundaries lost by the flattened `args.context` string. `undefined` for
2785
+ * non-council paths.
2786
+ */
2787
+ contextMessages?: LlmMessageDTO[];
2788
+ }
2789
+ /**
2790
+ * Observer handle returned by `spindle.generate.observe()`.
2791
+ * Provides a high-level API for watching an in-flight generation on a
2792
+ * specific chat, with automatic token accumulation and lifecycle callbacks.
2793
+ */
2794
+ export interface GenerationObserver {
2795
+ /** Register a callback for when a generation starts on the observed chat. */
2796
+ onStart(handler: (info: GenerationStartedPayloadDTO) => void): void;
2797
+ /** Register a callback for each streamed token (content or reasoning). */
2798
+ onToken(handler: (token: StreamTokenPayloadDTO) => void): void;
2799
+ /** Register a callback for when the generation completes (success or error). */
2800
+ onEnd(handler: (result: GenerationEndedPayloadDTO) => void): void;
2801
+ /** Register a callback for when the generation is stopped by the user. */
2802
+ onStop(handler: (result: GenerationStoppedPayloadDTO) => void): void;
2803
+ /** Accumulated content tokens so far. */
2804
+ readonly content: string;
2805
+ /** Accumulated reasoning tokens so far. */
2806
+ readonly reasoning: string;
2807
+ /** The active generation ID, or `null` if idle. */
2808
+ readonly generationId: string | null;
2809
+ /** Stop observing and unsubscribe from all events. */
2810
+ dispose(): void;
2811
+ }
2812
+ /** Where the model used for server-side token counting came from. */
2813
+ export type TokenModelSourceDTO = "main" | "sidecar" | "explicit";
2814
+ /** Optional settings for Spindle token count helpers. */
2815
+ export interface TokenCountOptionsDTO {
2816
+ /**
2817
+ * Explicit model ID to resolve the tokenizer against.
2818
+ *
2819
+ * When provided, this takes precedence over `modelSource`.
2820
+ */
2821
+ model?: string;
2822
+ /**
2823
+ * Which configured model to use when resolving the tokenizer.
2824
+ *
2825
+ * - `"main"` → the user's default main connection profile model
2826
+ * - `"sidecar"` → the user's selected sidecar model (or its backing connection model)
2827
+ *
2828
+ * Defaults to `"main"`.
2829
+ */
2830
+ modelSource?: TokenModelSourceDTO;
2831
+ /** For operator-scoped extensions. */
2832
+ userId?: string;
2833
+ }
2834
+ /** Server-resolved token count result for a text or chat payload. */
2835
+ export interface TokenCountResultDTO {
2836
+ total_tokens: number;
2837
+ /** Model ID that was actually used to resolve the tokenizer. */
2838
+ model: string;
2839
+ /** Whether the model came from the main connection, sidecar selection, or an explicit override. */
2840
+ modelSource: TokenModelSourceDTO;
2841
+ /** Null when no exact tokenizer match was found and an approximate fallback was used. */
2842
+ tokenizer_id: string | null;
2843
+ tokenizer_name: string;
2844
+ /** True when Lumiverse had to fall back to its approximate char/4 heuristic. */
2845
+ approximate: boolean;
2846
+ }
2847
+ /** A completed resumable upload read back by `spindle.uploads.get`. */
2848
+ export interface SpindleUploadDTO {
2849
+ fileName: string;
2850
+ size: number;
2851
+ data: Uint8Array;
2852
+ }
2853
+ /** Context delivered to an on-request shared RPC endpoint handler. */
2854
+ export interface SharedRpcRequestContextDTO {
2855
+ /** Fully-qualified endpoint name (for example `weather_ext.status.current`). */
2856
+ endpoint: string;
2857
+ /** Identifier of the extension requesting the value. */
2858
+ requesterExtensionId: string;
2859
+ /** Gated permissions available while this delegated handler request is running. */
2860
+ effectivePermissions: readonly string[];
2861
+ }
2862
+ /** Optional read policy for a shared RPC endpoint. Omit to require legacy owner-permission inheritance. */
2863
+ export interface SharedRpcEndpointPolicyDTO {
2864
+ /** Gated permissions both owner and requester must have; `[]` means no gated permissions are delegated. */
2865
+ requires?: readonly string[];
2866
+ }
2867
+ /**
2868
+ * Provider runtime size limits:
2869
+ * - descriptor: 64KiB
2870
+ * - request: 256KiB
2871
+ * - result / error / envelope: 1MiB
2872
+ * Default invoke timeout: 30000ms.
2873
+ */
2874
+ export type ProviderKind = "embedding" | "tts" | "stt" | "sidecar";
2875
+ /**
2876
+ * Broker specification declared at registration time. The broker URL is
2877
+ * immutable — the host always dispatches to the registration-time `url`
2878
+ * and never honours per-invocation destination overrides.
2879
+ */
2880
+ export interface ProviderBrokerSpec {
2881
+ url: string;
2882
+ method?: string;
2883
+ /**
2884
+ * Secret reference resolved host-side at request time. Must be namespaced
2885
+ * to the registering installation: `extension:<installationId>:<name>`.
2886
+ * Global or user-scoped secret keys are rejected with an authorization
2887
+ * error before any network request is made.
2888
+ */
2889
+ secretKey?: string;
2890
+ headers?: Record<string, string>;
2891
+ kind?: ProviderKind;
2892
+ }
2893
+ /** Scope-qualified identity of a registered provider. Limit: full envelope 1MiB. */
2894
+ export interface ProviderKeyDTO {
2895
+ effectiveScope: string;
2896
+ installationId: string;
2897
+ kind: string;
2898
+ id: string;
2899
+ }
2900
+ /** Provider descriptor payload. Limit: 64KiB. */
2901
+ export interface ProviderDescriptor {
2902
+ kind: string;
2903
+ id: string;
2904
+ description?: unknown;
2905
+ broker?: ProviderBrokerSpec;
2906
+ generation?: number;
2907
+ revision?: number;
2908
+ }
2909
+ /**
2910
+ * Pipelined worker → host provider messages (phase-tagged). Mirrors Core's
2911
+ * runtime wire schema exactly.
2912
+ */
2913
+ export type WorkerToHostProviderMessage = {
2914
+ type: "provider_register";
2915
+ phase: "register";
2916
+ kind: string;
2917
+ id: string;
2918
+ /** Descriptor payload limit: 64KiB. */
2919
+ description?: unknown;
2920
+ broker?: ProviderBrokerSpec;
2921
+ generation?: number;
2922
+ revision?: number;
2923
+ } | {
2924
+ type: "provider_unregister";
2925
+ phase: "unregister";
2926
+ kind: string;
2927
+ id: string;
2928
+ } | {
2929
+ type: "provider_result";
2930
+ phase: "result";
2931
+ correlationId: string;
2932
+ round?: number;
2933
+ /** Result / error / envelope limit: 1MiB. */
2934
+ result?: unknown;
2935
+ error?: string;
2936
+ };
2937
+ /**
2938
+ * Pipelined host → worker provider messages (phase-tagged). Mirrors Core's
2939
+ * runtime wire schema exactly.
2940
+ */
2941
+ export type HostToWorkerProviderMessage = {
2942
+ type: "provider_invoke";
2943
+ phase: "invoke";
2944
+ correlationId: string;
2945
+ round: number;
2946
+ key: ProviderKeyDTO;
2947
+ /** Request payload limit: 256KiB. Default timeout: 30000ms. */
2948
+ request: unknown;
2949
+ } | {
2950
+ type: "provider_abort";
2951
+ phase: "abort";
2952
+ correlationId: string;
2953
+ round: number;
2954
+ reason?: string;
2955
+ } | {
2956
+ type: "provider_changed";
2957
+ phase: "changed";
2958
+ action: "registered" | "unregistered" | "updated";
2959
+ key: ProviderKeyDTO;
2960
+ };
2961
+ export type ProviderRuntimeMessage = WorkerToHostProviderMessage | HostToWorkerProviderMessage;
2962
+ export interface BrokerRequest {
2963
+ kind: string;
2964
+ id: string;
2965
+ method: string;
2966
+ url: string;
2967
+ headers: Record<string, string>;
2968
+ body: string;
2969
+ bodyEncoding: "utf8" | "base64";
2970
+ expectedResponseEncoding: "utf8" | "base64";
2971
+ /** Default timeout: 30000ms. */
2972
+ timeoutMs: number;
2973
+ allowlistKey: string;
2974
+ correlationId: string;
2975
+ round: number;
2976
+ }
2977
+ export interface BrokerResponse {
2978
+ status: number;
2979
+ headers: Record<string, string>;
2980
+ body: string;
2981
+ bodyEncoding: "utf8" | "base64";
2982
+ contentType: string;
2983
+ ok: boolean;
2984
+ correlationId: string;
2985
+ round: number;
2986
+ }
2987
+ /**
2988
+ * Host-side provider manager exposed to extension workers as
2989
+ * `spindle.providers`. Mirrors the runtime API exactly: registration is
2990
+ * fire-and-forget, invocations arrive through `handle`, and registry changes
2991
+ * are observed via `onChanged`.
2992
+ */
2993
+ export interface ProviderManager {
2994
+ /** Register a provider descriptor (kind/id unique per installation scope). */
2995
+ register(descriptor: ProviderDescriptor): void;
2996
+ /** Remove a previously registered provider. */
2997
+ unregister(kind: string, id: string): void;
2998
+ /**
2999
+ * Bind an invocation handler for `kind/id`. The handler receives the
3000
+ * pipelined invoke payload plus a worker-local abort signal; its return
3001
+ * value is delivered back to the host as a `provider_result`.
3002
+ */
3003
+ handle(kind: string, id: string, handler: (req: {
3004
+ correlationId: string;
3005
+ round: number;
3006
+ key: ProviderKeyDTO;
3007
+ request: unknown;
3008
+ signal: AbortSignal;
3009
+ }) => unknown | Promise<unknown>): () => void;
3010
+ /** Subscribe to registry changes for this installation. Returns an unsubscribe function. */
3011
+ onChanged(handler: (event: {
3012
+ action: "registered" | "unregistered" | "updated";
3013
+ key: ProviderKeyDTO;
3014
+ }) => void): () => void;
3015
+ }
3016
+ export type WorkerToHost = {
3017
+ type: "subscribe_event";
3018
+ event: string;
3019
+ } | {
3020
+ type: "unsubscribe_event";
3021
+ event: string;
3022
+ } | {
3023
+ type: "register_frontend_runtime_capability";
3024
+ capability: import("./frontend-capabilities.js").SpindleFrontendRuntimeCapability;
3025
+ } | {
3026
+ type: "unregister_frontend_runtime_capability";
3027
+ capability: import("./frontend-capabilities.js").SpindleFrontendRuntimeCapability;
3028
+ } | {
3029
+ type: "register_macro";
3030
+ definition: MacroDefinitionDTO;
3031
+ } | {
3032
+ type: "unregister_macro";
3033
+ name: string;
3034
+ } | {
3035
+ type: "update_macro_value";
3036
+ name: string;
3037
+ value: string;
3038
+ } | {
3039
+ type: "register_interceptor";
3040
+ registrationId: string;
3041
+ priority?: number;
3042
+ match?: InterceptorMatchDTO;
3043
+ } | {
3044
+ type: "unregister_interceptor";
3045
+ registrationId: string;
3046
+ } | {
3047
+ type: "intercept_result";
3048
+ requestId: string;
3049
+ registrationId: string;
3050
+ messages: LlmMessageDTO[];
3051
+ parameters?: Record<string, unknown>;
3052
+ breakdown?: InterceptorBreakdownEntryDTO[];
3053
+ deferredGuidance?: DeferredGuidanceDTO[];
3054
+ finalResponse?: FinalResponseDTO;
3055
+ } | {
3056
+ type: "assemble_prompt";
3057
+ requestId: string;
3058
+ input: Omit<AssembleRequestDTO, "signal">;
3059
+ userId?: string;
3060
+ }
3061
+ /**
3062
+ * Assemble through the active bound interceptor generation context. The
3063
+ * worker-local signal is omitted from the structured-clone payload.
3064
+ */
3065
+ | {
3066
+ type: "generate_assemble";
3067
+ requestId: string;
3068
+ input: Omit<BoundAssembleRequestDTO, "signal">;
3069
+ }
3070
+ /**
3071
+ * Dispatch tracked quiet generation through the active bound context.
3072
+ * The worker-local signal is omitted from the structured-clone payload.
3073
+ */
3074
+ | {
3075
+ type: "generate_quiet_tracked";
3076
+ requestId: string;
3077
+ input: Omit<QuietTrackedRequestDTO, "signal">;
3078
+ }
3079
+ /**
3080
+ * Inspect an owned concrete slot through the active bound interceptor
3081
+ * context. The host derives user scope from the authenticated callback.
3082
+ */
3083
+ | {
3084
+ type: "connections_resolve_dispatch";
3085
+ requestId: string;
3086
+ connectionId: string;
3087
+ } | {
3088
+ type: "register_tool";
3089
+ tool: ToolRegistrationDTO;
3090
+ } | {
3091
+ type: "unregister_tool";
3092
+ name: string;
3093
+ } | {
3094
+ type: "request_generation";
3095
+ requestId: string;
3096
+ input: GenerationRequestDTO;
3097
+ }
3098
+ /**
3099
+ * Start a streaming generation. The host responds asynchronously with
3100
+ * one or more `generation_stream_chunk` messages, terminating with a
3101
+ * `done` chunk on success or a `generation_stream_error` on failure.
3102
+ */
3103
+ | {
3104
+ type: "request_generation_stream";
3105
+ requestId: string;
3106
+ input: GenerationRequestDTO;
3107
+ }
3108
+ /**
3109
+ * Cancel an in-flight generation started via `request_generation` or
3110
+ * `request_generation_stream`. `requestId` matches the original request.
3111
+ * The host aborts the upstream LLM fetch and responds with an `AbortError`.
3112
+ */
3113
+ | {
3114
+ type: "cancel_generation";
3115
+ requestId: string;
3116
+ } | {
3117
+ type: "storage_read";
3118
+ requestId: string;
3119
+ path: string;
3120
+ } | {
3121
+ type: "storage_write";
3122
+ requestId: string;
3123
+ path: string;
3124
+ data: string;
3125
+ } | {
3126
+ type: "storage_read_binary";
3127
+ requestId: string;
3128
+ path: string;
3129
+ } | {
3130
+ type: "storage_write_binary";
3131
+ requestId: string;
3132
+ path: string;
3133
+ data: Uint8Array;
3134
+ } | {
3135
+ type: "storage_delete";
3136
+ requestId: string;
3137
+ path: string;
3138
+ } | {
3139
+ type: "storage_list";
3140
+ requestId: string;
3141
+ prefix?: string;
3142
+ } | {
3143
+ type: "storage_exists";
3144
+ requestId: string;
3145
+ path: string;
3146
+ } | {
3147
+ type: "storage_mkdir";
3148
+ requestId: string;
3149
+ path: string;
3150
+ } | {
3151
+ type: "storage_move";
3152
+ requestId: string;
3153
+ from: string;
3154
+ to: string;
3155
+ } | {
3156
+ type: "storage_stat";
3157
+ requestId: string;
3158
+ path: string;
3159
+ } | {
3160
+ type: "ephemeral_read";
3161
+ requestId: string;
3162
+ path: string;
3163
+ } | {
3164
+ type: "ephemeral_write";
3165
+ requestId: string;
3166
+ path: string;
3167
+ data: string;
3168
+ ttlMs?: number;
3169
+ reservationId?: string;
3170
+ } | {
3171
+ type: "ephemeral_read_binary";
3172
+ requestId: string;
3173
+ path: string;
3174
+ } | {
3175
+ type: "ephemeral_write_binary";
3176
+ requestId: string;
3177
+ path: string;
3178
+ data: Uint8Array;
3179
+ ttlMs?: number;
3180
+ reservationId?: string;
3181
+ } | {
3182
+ type: "ephemeral_delete";
3183
+ requestId: string;
3184
+ path: string;
3185
+ } | {
3186
+ type: "ephemeral_list";
3187
+ requestId: string;
3188
+ prefix?: string;
3189
+ } | {
3190
+ type: "ephemeral_stat";
3191
+ requestId: string;
3192
+ path: string;
3193
+ } | {
3194
+ type: "ephemeral_clear_expired";
3195
+ requestId: string;
3196
+ } | {
3197
+ type: "ephemeral_pool_status";
3198
+ requestId: string;
3199
+ } | {
3200
+ type: "ephemeral_request_block";
3201
+ requestId: string;
3202
+ sizeBytes: number;
3203
+ ttlMs?: number;
3204
+ reason?: string;
3205
+ } | {
3206
+ type: "ephemeral_release_block";
3207
+ requestId: string;
3208
+ reservationId: string;
3209
+ } | {
3210
+ type: "permissions_get_granted";
3211
+ requestId: string;
3212
+ } | {
3213
+ type: "mcp_servers_list";
3214
+ requestId: string;
3215
+ limit?: number;
3216
+ offset?: number;
3217
+ userId?: string;
3218
+ } | {
3219
+ type: "mcp_servers_get";
3220
+ requestId: string;
3221
+ serverId: string;
3222
+ userId?: string;
3223
+ } | {
3224
+ type: "mcp_servers_create";
3225
+ requestId: string;
3226
+ input: McpServerCreateDTO;
3227
+ userId?: string;
3228
+ } | {
3229
+ type: "mcp_servers_connect";
3230
+ requestId: string;
3231
+ serverId: string;
3232
+ userId?: string;
3233
+ } | {
3234
+ type: "mcp_servers_status";
3235
+ requestId: string;
3236
+ serverId: string;
3237
+ userId?: string;
3238
+ } | {
3239
+ type: "mcp_tools_list";
3240
+ requestId: string;
3241
+ serverId: string;
3242
+ userId?: string;
3243
+ } | {
3244
+ type: "mcp_tools_call";
3245
+ requestId: string;
3246
+ serverId: string;
3247
+ toolName: string;
3248
+ args: Record<string, unknown>;
3249
+ timeoutMs?: number;
3250
+ userId?: string;
3251
+ } | {
3252
+ type: "rpc_pool_sync";
3253
+ endpoint: string;
3254
+ value: unknown;
3255
+ policy?: SharedRpcEndpointPolicyDTO;
3256
+ rpcPermissionScopeId?: string;
3257
+ } | {
3258
+ type: "rpc_pool_register_handler";
3259
+ endpoint: string;
3260
+ policy?: SharedRpcEndpointPolicyDTO;
3261
+ rpcPermissionScopeId?: string;
3262
+ } | {
3263
+ type: "rpc_pool_unregister";
3264
+ endpoint: string;
3265
+ } | {
3266
+ type: "rpc_pool_read";
3267
+ requestId: string;
3268
+ endpoint: string;
3269
+ rpcPermissionScopeId?: string;
3270
+ } | {
3271
+ type: "rpc_pool_handler_result";
3272
+ requestId: string;
3273
+ result?: unknown;
3274
+ error?: string;
3275
+ rpcPermissionScopeId?: string;
3276
+ } | {
3277
+ type: "connections_list";
3278
+ requestId: string;
3279
+ userId?: string;
3280
+ } | {
3281
+ type: "connections_get";
3282
+ requestId: string;
3283
+ connectionId: string;
3284
+ userId?: string;
3285
+ } | {
3286
+ type: "chat_get_messages";
3287
+ requestId: string;
3288
+ chatId: string;
3289
+ } | {
3290
+ type: "chat_append_message";
3291
+ requestId: string;
3292
+ chatId: string;
3293
+ message: {
3294
+ role: "system" | "user" | "assistant";
3295
+ content: string;
3296
+ metadata?: Record<string, unknown>;
3297
+ };
3298
+ options?: ChatAppendMessageOptionsDTO;
3299
+ } | {
3300
+ type: "chat_update_message";
3301
+ requestId: string;
3302
+ chatId: string;
3303
+ messageId: string;
3304
+ patch: {
3305
+ content?: string;
3306
+ metadata?: Record<string, unknown>;
3307
+ swipes?: string[];
3308
+ swipe_id?: number;
3309
+ swipe_dates?: number[];
3310
+ reasoning?: {
3311
+ text?: string | null;
3312
+ duration?: number | null;
3313
+ };
3314
+ skipChunkRebuild?: boolean;
3315
+ };
3316
+ } | {
3317
+ type: "chat_delete_message";
3318
+ requestId: string;
3319
+ chatId: string;
3320
+ messageId: string;
3321
+ } | {
3322
+ type: "chat_set_message_hidden";
3323
+ requestId: string;
3324
+ chatId: string;
3325
+ messageId: string;
3326
+ hidden: boolean;
3327
+ } | {
3328
+ type: "chat_set_messages_hidden";
3329
+ requestId: string;
3330
+ chatId: string;
3331
+ messageIds: string[];
3332
+ hidden: boolean;
3333
+ } | {
3334
+ type: "chat_is_message_hidden";
3335
+ requestId: string;
3336
+ chatId: string;
3337
+ messageId: string;
3338
+ } | {
3339
+ type: "chat_set_style_mode";
3340
+ requestId: string;
3341
+ chatId: string;
3342
+ mode: "bounded" | "extension-relaxed";
3343
+ userId?: string;
3344
+ } | {
3345
+ type: "events_track";
3346
+ requestId: string;
3347
+ eventName: string;
3348
+ payload?: Record<string, unknown>;
3349
+ options?: {
3350
+ level?: "debug" | "info" | "warn" | "error";
3351
+ chatId?: string;
3352
+ retentionDays?: number;
3353
+ };
3354
+ } | {
3355
+ type: "events_query";
3356
+ requestId: string;
3357
+ filter?: {
3358
+ eventName?: string;
3359
+ chatId?: string;
3360
+ since?: string;
3361
+ until?: string;
3362
+ level?: "debug" | "info" | "warn" | "error";
3363
+ limit?: number;
3364
+ };
3365
+ } | {
3366
+ type: "events_replay";
3367
+ requestId: string;
3368
+ filter?: {
3369
+ eventName?: string;
3370
+ chatId?: string;
3371
+ since?: string;
3372
+ until?: string;
3373
+ level?: "debug" | "info" | "warn" | "error";
3374
+ limit?: number;
3375
+ };
3376
+ } | {
3377
+ type: "events_get_latest_state";
3378
+ requestId: string;
3379
+ keys: string[];
3380
+ } | {
3381
+ type: "cors_request";
3382
+ requestId: string;
3383
+ url: string;
3384
+ options: RequestInitDTO;
3385
+ } | {
3386
+ type: "register_context_handler";
3387
+ priority?: number;
3388
+ timeoutMs?: number;
3389
+ } | {
3390
+ type: "context_handler_result";
3391
+ requestId: string;
3392
+ context: unknown;
3393
+ } | {
3394
+ type: "register_macro_interceptor";
3395
+ priority?: number;
3396
+ } | {
3397
+ type: "macro_interceptor_result";
3398
+ requestId: string;
3399
+ result: MacroInterceptorResultDTO;
3400
+ } | {
3401
+ type: "register_world_info_interceptor";
3402
+ priority?: number;
3403
+ } | {
3404
+ type: "world_info_interceptor_result";
3405
+ requestId: string;
3406
+ result: WorldInfoInterceptorResultDTO | null;
3407
+ } | {
3408
+ type: "register_message_content_processor";
3409
+ priority?: number;
3410
+ } | {
3411
+ type: "message_content_processor_result";
3412
+ requestId: string;
3413
+ result: MessageContentProcessorResultDTO | void;
3414
+ } | {
3415
+ type: "macro_result";
3416
+ requestId: string;
3417
+ result?: string;
3418
+ error?: string;
3419
+ } | {
3420
+ type: "frontend_message";
3421
+ payload: unknown;
3422
+ userId?: string;
3423
+ } | {
3424
+ type: "user_storage_read";
3425
+ requestId: string;
3426
+ path: string;
3427
+ userId?: string;
3428
+ } | {
3429
+ type: "user_storage_write";
3430
+ requestId: string;
3431
+ path: string;
3432
+ data: string;
3433
+ userId?: string;
3434
+ } | {
3435
+ type: "user_storage_read_binary";
3436
+ requestId: string;
3437
+ path: string;
3438
+ userId?: string;
3439
+ } | {
3440
+ type: "user_storage_write_binary";
3441
+ requestId: string;
3442
+ path: string;
3443
+ data: Uint8Array;
3444
+ userId?: string;
3445
+ } | {
3446
+ type: "user_storage_delete";
3447
+ requestId: string;
3448
+ path: string;
3449
+ userId?: string;
3450
+ } | {
3451
+ type: "user_storage_list";
3452
+ requestId: string;
3453
+ prefix?: string;
3454
+ userId?: string;
3455
+ } | {
3456
+ type: "user_storage_exists";
3457
+ requestId: string;
3458
+ path: string;
3459
+ userId?: string;
3460
+ } | {
3461
+ type: "user_storage_mkdir";
3462
+ requestId: string;
3463
+ path: string;
3464
+ userId?: string;
3465
+ } | {
3466
+ type: "user_storage_move";
3467
+ requestId: string;
3468
+ from: string;
3469
+ to: string;
3470
+ userId?: string;
3471
+ } | {
3472
+ type: "user_storage_stat";
3473
+ requestId: string;
3474
+ path: string;
3475
+ userId?: string;
3476
+ } | {
3477
+ type: "enclave_put";
3478
+ requestId: string;
3479
+ key: string;
3480
+ value: string;
3481
+ userId?: string;
3482
+ } | {
3483
+ type: "enclave_get";
3484
+ requestId: string;
3485
+ key: string;
3486
+ userId?: string;
3487
+ } | {
3488
+ type: "enclave_delete";
3489
+ requestId: string;
3490
+ key: string;
3491
+ userId?: string;
3492
+ } | {
3493
+ type: "enclave_has";
3494
+ requestId: string;
3495
+ key: string;
3496
+ userId?: string;
3497
+ } | {
3498
+ type: "enclave_list";
3499
+ requestId: string;
3500
+ userId?: string;
3501
+ } | {
3502
+ type: "oauth_callback_result";
3503
+ requestId: string;
3504
+ html?: string;
3505
+ error?: string;
3506
+ } | {
3507
+ type: "tool_invocation_result";
3508
+ requestId: string;
3509
+ result?: string;
3510
+ error?: string;
3511
+ } | {
3512
+ type: "create_oauth_state";
3513
+ requestId: string;
3514
+ } | {
3515
+ type: "log";
3516
+ level: "info" | "warn" | "error";
3517
+ message: string;
3518
+ } | {
3519
+ type: "vars_get_local";
3520
+ requestId: string;
3521
+ chatId: string;
3522
+ key: string;
3523
+ } | {
3524
+ type: "vars_set_local";
3525
+ requestId: string;
3526
+ chatId: string;
3527
+ key: string;
3528
+ value: string;
3529
+ } | {
3530
+ type: "vars_delete_local";
3531
+ requestId: string;
3532
+ chatId: string;
3533
+ key: string;
3534
+ } | {
3535
+ type: "vars_list_local";
3536
+ requestId: string;
3537
+ chatId: string;
3538
+ } | {
3539
+ type: "vars_has_local";
3540
+ requestId: string;
3541
+ chatId: string;
3542
+ key: string;
3543
+ } | {
3544
+ type: "vars_get_global";
3545
+ requestId: string;
3546
+ key: string;
3547
+ userId?: string;
3548
+ } | {
3549
+ type: "vars_set_global";
3550
+ requestId: string;
3551
+ key: string;
3552
+ value: string;
3553
+ userId?: string;
3554
+ } | {
3555
+ type: "vars_delete_global";
3556
+ requestId: string;
3557
+ key: string;
3558
+ userId?: string;
3559
+ } | {
3560
+ type: "vars_list_global";
3561
+ requestId: string;
3562
+ userId?: string;
3563
+ } | {
3564
+ type: "vars_has_global";
3565
+ requestId: string;
3566
+ key: string;
3567
+ userId?: string;
3568
+ } | {
3569
+ type: "vars_get_chat";
3570
+ requestId: string;
3571
+ chatId: string;
3572
+ key: string;
3573
+ } | {
3574
+ type: "vars_set_chat";
3575
+ requestId: string;
3576
+ chatId: string;
3577
+ key: string;
3578
+ value: string;
3579
+ } | {
3580
+ type: "vars_delete_chat";
3581
+ requestId: string;
3582
+ chatId: string;
3583
+ key: string;
3584
+ } | {
3585
+ type: "vars_list_chat";
3586
+ requestId: string;
3587
+ chatId: string;
3588
+ } | {
3589
+ type: "vars_has_chat";
3590
+ requestId: string;
3591
+ chatId: string;
3592
+ key: string;
3593
+ } | {
3594
+ type: "characters_list";
3595
+ requestId: string;
3596
+ limit?: number;
3597
+ offset?: number;
3598
+ userId?: string;
3599
+ } | {
3600
+ type: "characters_get";
3601
+ requestId: string;
3602
+ characterId: string;
3603
+ userId?: string;
3604
+ } | {
3605
+ type: "characters_create";
3606
+ requestId: string;
3607
+ input: CharacterCreateDTO;
3608
+ userId?: string;
3609
+ } | {
3610
+ type: "characters_set_avatar";
3611
+ requestId: string;
3612
+ characterId: string;
3613
+ avatar: CharacterAvatarUploadDTO;
3614
+ userId?: string;
3615
+ } | {
3616
+ type: "characters_update";
3617
+ requestId: string;
3618
+ characterId: string;
3619
+ input: CharacterUpdateDTO;
3620
+ userId?: string;
3621
+ } | {
3622
+ type: "characters_delete";
3623
+ requestId: string;
3624
+ characterId: string;
3625
+ userId?: string;
3626
+ } | {
3627
+ type: "chats_list";
3628
+ requestId: string;
3629
+ characterId?: string;
3630
+ limit?: number;
3631
+ offset?: number;
3632
+ userId?: string;
3633
+ } | {
3634
+ type: "chats_get";
3635
+ requestId: string;
3636
+ chatId: string;
3637
+ userId?: string;
3638
+ } | {
3639
+ type: "chats_get_active";
3640
+ requestId: string;
3641
+ userId?: string;
3642
+ } | {
3643
+ type: "chats_update";
3644
+ requestId: string;
3645
+ chatId: string;
3646
+ input: ChatUpdateDTO;
3647
+ userId?: string;
3648
+ } | {
3649
+ type: "chats_delete";
3650
+ requestId: string;
3651
+ chatId: string;
3652
+ userId?: string;
3653
+ } | {
3654
+ type: "presets_list";
3655
+ requestId: string;
3656
+ limit?: number;
3657
+ offset?: number;
3658
+ userId?: string;
3659
+ } | {
3660
+ type: "presets_get";
3661
+ requestId: string;
3662
+ presetId: string;
3663
+ userId?: string;
3664
+ } | {
3665
+ type: "presets_create";
3666
+ requestId: string;
3667
+ input: UserPresetCreateDTO;
3668
+ userId?: string;
3669
+ } | {
3670
+ type: "presets_update";
3671
+ requestId: string;
3672
+ presetId: string;
3673
+ input: UserPresetUpdateDTO;
3674
+ userId?: string;
3675
+ } | {
3676
+ type: "presets_delete";
3677
+ requestId: string;
3678
+ presetId: string;
3679
+ userId?: string;
3680
+ } | {
3681
+ type: "preset_blocks_list";
3682
+ requestId: string;
3683
+ presetId: string;
3684
+ userId?: string;
3685
+ } | {
3686
+ type: "preset_blocks_get";
3687
+ requestId: string;
3688
+ presetId: string;
3689
+ blockId: string;
3690
+ userId?: string;
3691
+ } | {
3692
+ type: "preset_blocks_create";
3693
+ requestId: string;
3694
+ presetId: string;
3695
+ input: PromptBlockCreateDTO;
3696
+ index?: number;
3697
+ userId?: string;
3698
+ } | {
3699
+ type: "preset_blocks_update";
3700
+ requestId: string;
3701
+ presetId: string;
3702
+ blockId: string;
3703
+ input: PromptBlockUpdateDTO;
3704
+ userId?: string;
3705
+ } | {
3706
+ type: "preset_blocks_delete";
3707
+ requestId: string;
3708
+ presetId: string;
3709
+ blockId: string;
3710
+ userId?: string;
3711
+ } | {
3712
+ type: "preset_categories_list";
3713
+ requestId: string;
3714
+ presetId: string;
3715
+ userId?: string;
3716
+ } | {
3717
+ type: "world_books_list";
3718
+ requestId: string;
3719
+ limit?: number;
3720
+ offset?: number;
3721
+ userId?: string;
3722
+ } | {
3723
+ type: "world_books_get";
3724
+ requestId: string;
3725
+ worldBookId: string;
3726
+ userId?: string;
3727
+ } | {
3728
+ type: "world_books_create";
3729
+ requestId: string;
3730
+ input: WorldBookCreateDTO;
3731
+ userId?: string;
3732
+ } | {
3733
+ type: "world_books_update";
3734
+ requestId: string;
3735
+ worldBookId: string;
3736
+ input: WorldBookUpdateDTO;
3737
+ userId?: string;
3738
+ } | {
3739
+ type: "world_books_delete";
3740
+ requestId: string;
3741
+ worldBookId: string;
3742
+ userId?: string;
3743
+ } | {
3744
+ type: "world_book_entries_list";
3745
+ requestId: string;
3746
+ worldBookId: string;
3747
+ limit?: number;
3748
+ offset?: number;
3749
+ userId?: string;
3750
+ } | {
3751
+ type: "world_book_entries_get";
3752
+ requestId: string;
3753
+ entryId: string;
3754
+ userId?: string;
3755
+ } | {
3756
+ type: "world_book_entries_create";
3757
+ requestId: string;
3758
+ worldBookId: string;
3759
+ input: WorldBookEntryCreateDTO;
3760
+ userId?: string;
3761
+ } | {
3762
+ type: "world_book_entries_update";
3763
+ requestId: string;
3764
+ entryId: string;
3765
+ input: WorldBookEntryUpdateDTO;
3766
+ userId?: string;
3767
+ } | {
3768
+ type: "world_book_entries_delete";
3769
+ requestId: string;
3770
+ entryId: string;
3771
+ userId?: string;
3772
+ } | {
3773
+ type: "databanks_list";
3774
+ requestId: string;
3775
+ limit?: number;
3776
+ offset?: number;
3777
+ scope?: DatabankScopeDTO;
3778
+ scopeId?: string | null;
3779
+ userId?: string;
3780
+ } | {
3781
+ type: "databanks_get";
3782
+ requestId: string;
3783
+ databankId: string;
3784
+ userId?: string;
3785
+ } | {
3786
+ type: "databanks_create";
3787
+ requestId: string;
3788
+ input: DatabankCreateDTO;
3789
+ userId?: string;
3790
+ } | {
3791
+ type: "databanks_update";
3792
+ requestId: string;
3793
+ databankId: string;
3794
+ input: DatabankUpdateDTO;
3795
+ userId?: string;
3796
+ } | {
3797
+ type: "databanks_delete";
3798
+ requestId: string;
3799
+ databankId: string;
3800
+ userId?: string;
3801
+ } | {
3802
+ type: "databank_documents_list";
3803
+ requestId: string;
3804
+ databankId: string;
3805
+ limit?: number;
3806
+ offset?: number;
3807
+ userId?: string;
3808
+ } | {
3809
+ type: "databank_documents_get";
3810
+ requestId: string;
3811
+ documentId: string;
3812
+ userId?: string;
3813
+ } | {
3814
+ type: "databank_documents_create";
3815
+ requestId: string;
3816
+ databankId: string;
3817
+ input: DatabankDocumentCreateDTO;
3818
+ userId?: string;
3819
+ } | {
3820
+ type: "databank_documents_update";
3821
+ requestId: string;
3822
+ documentId: string;
3823
+ input: DatabankDocumentUpdateDTO;
3824
+ userId?: string;
3825
+ } | {
3826
+ type: "databank_documents_delete";
3827
+ requestId: string;
3828
+ documentId: string;
3829
+ userId?: string;
3830
+ } | {
3831
+ type: "databank_documents_get_content";
3832
+ requestId: string;
3833
+ documentId: string;
3834
+ userId?: string;
3835
+ } | {
3836
+ type: "databank_documents_reprocess";
3837
+ requestId: string;
3838
+ documentId: string;
3839
+ userId?: string;
3840
+ } | {
3841
+ type: "personas_list";
3842
+ requestId: string;
3843
+ limit?: number;
3844
+ offset?: number;
3845
+ userId?: string;
3846
+ } | {
3847
+ type: "personas_get";
3848
+ requestId: string;
3849
+ personaId: string;
3850
+ userId?: string;
3851
+ } | {
3852
+ type: "personas_get_default";
3853
+ requestId: string;
3854
+ userId?: string;
3855
+ } | {
3856
+ type: "personas_get_active";
3857
+ requestId: string;
3858
+ userId?: string;
3859
+ } | {
3860
+ type: "personas_create";
3861
+ requestId: string;
3862
+ input: PersonaCreateDTO;
3863
+ userId?: string;
3864
+ } | {
3865
+ type: "personas_update";
3866
+ requestId: string;
3867
+ personaId: string;
3868
+ input: PersonaUpdateDTO;
3869
+ userId?: string;
3870
+ } | {
3871
+ type: "personas_delete";
3872
+ requestId: string;
3873
+ personaId: string;
3874
+ userId?: string;
3875
+ } | {
3876
+ type: "personas_switch";
3877
+ requestId: string;
3878
+ personaId: string | null;
3879
+ userId?: string;
3880
+ } | {
3881
+ type: "personas_get_world_book";
3882
+ requestId: string;
3883
+ personaId: string;
3884
+ userId?: string;
3885
+ } | {
3886
+ type: "global_addons_list";
3887
+ requestId: string;
3888
+ limit?: number;
3889
+ offset?: number;
3890
+ userId?: string;
3891
+ } | {
3892
+ type: "global_addons_get";
3893
+ requestId: string;
3894
+ addonId: string;
3895
+ userId?: string;
3896
+ } | {
3897
+ type: "global_addons_update";
3898
+ requestId: string;
3899
+ addonId: string;
3900
+ input: GlobalAddonUpdateDTO;
3901
+ userId?: string;
3902
+ } | {
3903
+ type: "council_get_settings";
3904
+ requestId: string;
3905
+ userId?: string;
3906
+ } | {
3907
+ type: "council_get_members";
3908
+ requestId: string;
3909
+ userId?: string;
3910
+ } | {
3911
+ type: "council_get_available_lumia_items";
3912
+ requestId: string;
3913
+ userId?: string;
3914
+ } | {
3915
+ type: "dlc_get_catalog";
3916
+ requestId: string;
3917
+ userId?: string;
3918
+ } | {
3919
+ type: "world_books_get_activated";
3920
+ requestId: string;
3921
+ chatId: string;
3922
+ userId?: string;
3923
+ } | {
3924
+ type: "world_books_get_global";
3925
+ requestId: string;
3926
+ userId?: string;
3927
+ } | {
3928
+ type: "world_books_set_global";
3929
+ requestId: string;
3930
+ worldBookIds: string[];
3931
+ userId?: string;
3932
+ } | {
3933
+ type: "world_books_activate_global";
3934
+ requestId: string;
3935
+ worldBookId: string;
3936
+ userId?: string;
3937
+ } | {
3938
+ type: "world_books_deactivate_global";
3939
+ requestId: string;
3940
+ worldBookId: string;
3941
+ userId?: string;
3942
+ } | {
3943
+ type: "regex_scripts_list";
3944
+ requestId: string;
3945
+ scope?: RegexScopeDTO;
3946
+ scopeId?: string;
3947
+ target?: RegexTargetDTO;
3948
+ limit?: number;
3949
+ offset?: number;
3950
+ userId?: string;
3951
+ } | {
3952
+ type: "regex_scripts_get";
3953
+ requestId: string;
3954
+ scriptId: string;
3955
+ userId?: string;
3956
+ } | {
3957
+ type: "regex_scripts_get_active";
3958
+ requestId: string;
3959
+ target: RegexTargetDTO;
3960
+ characterId?: string;
3961
+ chatId?: string;
3962
+ userId?: string;
3963
+ } | {
3964
+ type: "regex_scripts_create";
3965
+ requestId: string;
3966
+ input: RegexScriptCreateDTO;
3967
+ userId?: string;
3968
+ } | {
3969
+ type: "regex_scripts_update";
3970
+ requestId: string;
3971
+ scriptId: string;
3972
+ input: RegexScriptUpdateDTO;
3973
+ userId?: string;
3974
+ } | {
3975
+ type: "regex_scripts_delete";
3976
+ requestId: string;
3977
+ scriptId: string;
3978
+ userId?: string;
3979
+ } | {
3980
+ type: "generate_dry_run";
3981
+ requestId: string;
3982
+ input: DryRunRequestDTO;
3983
+ userId?: string;
3984
+ } | {
3985
+ type: "chats_get_memories";
3986
+ requestId: string;
3987
+ chatId: string;
3988
+ topK?: number;
3989
+ userId?: string;
3990
+ } | {
3991
+ type: "memories_config_get";
3992
+ requestId: string;
3993
+ userId?: string;
3994
+ } | {
3995
+ type: "memories_config_put";
3996
+ requestId: string;
3997
+ patch: Partial<MemoryCortexConfigDTO>;
3998
+ userId?: string;
3999
+ } | {
4000
+ type: "memories_query_cortex";
4001
+ requestId: string;
4002
+ query: CortexQueryDTO;
4003
+ } | {
4004
+ type: "memories_query_linked";
4005
+ requestId: string;
4006
+ chatId: string;
4007
+ queryText?: string;
4008
+ userId?: string;
4009
+ } | {
4010
+ type: "memories_get_cached";
4011
+ requestId: string;
4012
+ chatId: string;
4013
+ } | {
4014
+ type: "memories_get_cached_linked";
4015
+ requestId: string;
4016
+ chatId: string;
4017
+ } | {
4018
+ type: "memories_invalidate_cache";
4019
+ requestId: string;
4020
+ chatId: string;
4021
+ } | {
4022
+ type: "memories_invalidate_linked_cache";
4023
+ requestId: string;
4024
+ chatId: string;
4025
+ } | {
4026
+ type: "memories_entities_list";
4027
+ requestId: string;
4028
+ chatId: string;
4029
+ activeOnly?: boolean;
4030
+ limit?: number;
4031
+ userId?: string;
4032
+ } | {
4033
+ type: "memories_entities_get";
4034
+ requestId: string;
4035
+ entityId: string;
4036
+ userId?: string;
4037
+ } | {
4038
+ type: "memories_entities_find_by_name";
4039
+ requestId: string;
4040
+ chatId: string;
4041
+ name: string;
4042
+ userId?: string;
4043
+ } | {
4044
+ type: "memories_entities_upsert";
4045
+ requestId: string;
4046
+ chatId: string;
4047
+ entity: MemoryEntityUpsertDTO;
4048
+ chunkId?: string | null;
4049
+ createdAt?: number;
4050
+ userId?: string;
4051
+ } | {
4052
+ type: "memories_entities_update_status";
4053
+ requestId: string;
4054
+ entityId: string;
4055
+ patch: MemoryEntityStatusUpdateDTO;
4056
+ userId?: string;
4057
+ } | {
4058
+ type: "memories_entities_add_facts";
4059
+ requestId: string;
4060
+ entityId: string;
4061
+ facts: string[];
4062
+ userId?: string;
4063
+ } | {
4064
+ type: "memories_entities_get_facts";
4065
+ requestId: string;
4066
+ entityId: string;
4067
+ userId?: string;
4068
+ } | {
4069
+ type: "memories_entities_update_emotional_valence";
4070
+ requestId: string;
4071
+ entityId: string;
4072
+ valence: Record<string, number>;
4073
+ userId?: string;
4074
+ } | {
4075
+ type: "memories_relations_list";
4076
+ requestId: string;
4077
+ chatId: string;
4078
+ userId?: string;
4079
+ } | {
4080
+ type: "memories_relations_list_all";
4081
+ requestId: string;
4082
+ chatId: string;
4083
+ userId?: string;
4084
+ } | {
4085
+ type: "memories_relations_for_entity";
4086
+ requestId: string;
4087
+ chatId: string;
4088
+ entityId: string;
4089
+ userId?: string;
4090
+ } | {
4091
+ type: "memories_relations_for_entities";
4092
+ requestId: string;
4093
+ chatId: string;
4094
+ entityIds: string[];
4095
+ limit?: number;
4096
+ userId?: string;
4097
+ } | {
4098
+ type: "memories_relations_upsert";
4099
+ requestId: string;
4100
+ chatId: string;
4101
+ relation: MemoryRelationUpsertDTO;
4102
+ chunkId?: string | null;
4103
+ userId?: string;
4104
+ } | {
4105
+ type: "memories_consolidations_list";
4106
+ requestId: string;
4107
+ chatId: string;
4108
+ tier?: number;
4109
+ userId?: string;
4110
+ } | {
4111
+ type: "memories_consolidations_latest_arc";
4112
+ requestId: string;
4113
+ chatId: string;
4114
+ userId?: string;
4115
+ } | {
4116
+ type: "memories_consolidations_run";
4117
+ requestId: string;
4118
+ chatId: string;
4119
+ userId?: string;
4120
+ } | {
4121
+ type: "memories_salience_get";
4122
+ requestId: string;
4123
+ chatId: string;
4124
+ limit?: number;
4125
+ offset?: number;
4126
+ userId?: string;
4127
+ } | {
4128
+ type: "memories_vaults_list";
4129
+ requestId: string;
4130
+ userId?: string;
4131
+ } | {
4132
+ type: "memories_vaults_get";
4133
+ requestId: string;
4134
+ vaultId: string;
4135
+ userId?: string;
4136
+ } | {
4137
+ type: "memories_vaults_get_chunks";
4138
+ requestId: string;
4139
+ vaultId: string;
4140
+ userId?: string;
4141
+ } | {
4142
+ type: "memories_vaults_create";
4143
+ requestId: string;
4144
+ input: VaultCreateDTO;
4145
+ userId?: string;
4146
+ } | {
4147
+ type: "memories_vaults_rename";
4148
+ requestId: string;
4149
+ vaultId: string;
4150
+ name: string;
4151
+ userId?: string;
4152
+ } | {
4153
+ type: "memories_vaults_delete";
4154
+ requestId: string;
4155
+ vaultId: string;
4156
+ userId?: string;
4157
+ } | {
4158
+ type: "memories_vaults_reindex";
4159
+ requestId: string;
4160
+ vaultId: string;
4161
+ userId?: string;
4162
+ } | {
4163
+ type: "memories_links_list";
4164
+ requestId: string;
4165
+ chatId: string;
4166
+ userId?: string;
4167
+ } | {
4168
+ type: "memories_links_attach";
4169
+ requestId: string;
4170
+ input: ChatLinkAttachDTO;
4171
+ userId?: string;
4172
+ } | {
4173
+ type: "memories_links_remove";
4174
+ requestId: string;
4175
+ chatId: string;
4176
+ linkId: string;
4177
+ userId?: string;
4178
+ } | {
4179
+ type: "memories_links_toggle";
4180
+ requestId: string;
4181
+ chatId: string;
4182
+ linkId: string;
4183
+ enabled: boolean;
4184
+ userId?: string;
4185
+ } | {
4186
+ type: "memories_chat_chunks_list";
4187
+ requestId: string;
4188
+ chatId: string;
4189
+ userId?: string;
4190
+ } | {
4191
+ type: "memories_chat_memory_get";
4192
+ requestId: string;
4193
+ chatId: string;
4194
+ topK?: number;
4195
+ userId?: string;
4196
+ } | {
4197
+ type: "memories_chat_memory_warm";
4198
+ requestId: string;
4199
+ chatId: string;
4200
+ force?: boolean;
4201
+ userId?: string;
4202
+ } | {
4203
+ type: "memories_chat_memory_invalidate";
4204
+ requestId: string;
4205
+ chatId: string;
4206
+ userId?: string;
4207
+ } | {
4208
+ type: "memories_stats_usage";
4209
+ requestId: string;
4210
+ chatId: string;
4211
+ userId?: string;
4212
+ } | {
4213
+ type: "memories_stats_ingestion_status";
4214
+ requestId: string;
4215
+ chatId: string;
4216
+ userId?: string;
4217
+ } | {
4218
+ type: "memories_stats_ingestion_telemetry";
4219
+ requestId: string;
4220
+ chatId: string;
4221
+ userId?: string;
4222
+ } | {
4223
+ type: "toast_show";
4224
+ toastType: "success" | "warning" | "error" | "info";
4225
+ message: string;
4226
+ title?: string;
4227
+ duration?: number;
4228
+ userId?: string;
4229
+ } | {
4230
+ type: "push_send";
4231
+ requestId: string;
4232
+ title: string;
4233
+ body: string;
4234
+ tag?: string;
4235
+ url?: string;
4236
+ userId?: string;
4237
+ icon?: string;
4238
+ rawTitle?: boolean;
4239
+ image?: string;
4240
+ } | {
4241
+ type: "push_get_status";
4242
+ requestId: string;
4243
+ userId?: string;
4244
+ } | {
4245
+ type: "user_is_visible";
4246
+ requestId: string;
4247
+ userId?: string;
4248
+ } | {
4249
+ type: "user_get_role";
4250
+ requestId: string;
4251
+ userId?: string;
4252
+ } | {
4253
+ type: "text_editor_open";
4254
+ requestId: string;
4255
+ editorRequestId?: string;
4256
+ title?: string;
4257
+ value?: string;
4258
+ placeholder?: string;
4259
+ userId?: string;
4260
+ } | {
4261
+ type: "text_editor_close";
4262
+ requestId: string;
4263
+ editorRequestId: string;
4264
+ userId?: string;
4265
+ } | {
4266
+ type: "modal_open";
4267
+ requestId: string;
4268
+ modalRequestId?: string;
4269
+ title: string;
4270
+ items: SpindleModalItemDTO[];
4271
+ width?: number;
4272
+ maxHeight?: number;
4273
+ persistent?: boolean;
4274
+ userId?: string;
4275
+ } | {
4276
+ type: "modal_close";
4277
+ requestId: string;
4278
+ openRequestId: string;
4279
+ userId?: string;
4280
+ } | {
4281
+ type: "confirm_open";
4282
+ requestId: string;
4283
+ title: string;
4284
+ message: string;
4285
+ variant?: "info" | "warning" | "danger" | "success";
4286
+ confirmLabel?: string;
4287
+ cancelLabel?: string;
4288
+ userId?: string;
4289
+ } | {
4290
+ type: "input_prompt_open";
4291
+ requestId: string;
4292
+ title: string;
4293
+ message?: string;
4294
+ placeholder?: string;
4295
+ defaultValue?: string;
4296
+ submitLabel?: string;
4297
+ cancelLabel?: string;
4298
+ multiline?: boolean;
4299
+ userId?: string;
4300
+ } | {
4301
+ type: "frontend_process_spawn";
4302
+ requestId: string;
4303
+ options: FrontendProcessSpawnOptionsDTO;
4304
+ } | {
4305
+ type: "frontend_process_list";
4306
+ requestId: string;
4307
+ filter?: FrontendProcessListOptionsDTO;
4308
+ } | {
4309
+ type: "frontend_process_get";
4310
+ requestId: string;
4311
+ processId: string;
4312
+ } | {
4313
+ type: "frontend_process_stop";
4314
+ requestId: string;
4315
+ processId: string;
4316
+ options?: FrontendProcessStopOptionsDTO;
4317
+ } | {
4318
+ type: "frontend_process_send";
4319
+ processId: string;
4320
+ payload: unknown;
4321
+ userId?: string;
4322
+ } | {
4323
+ type: "backend_process_spawn";
4324
+ requestId: string;
4325
+ options: BackendProcessSpawnOptionsDTO;
4326
+ } | {
4327
+ type: "backend_process_list";
4328
+ requestId: string;
4329
+ filter?: BackendProcessListOptionsDTO;
4330
+ } | {
4331
+ type: "backend_process_get";
4332
+ requestId: string;
4333
+ processId: string;
4334
+ } | {
4335
+ type: "backend_process_stop";
4336
+ requestId: string;
4337
+ processId: string;
4338
+ options?: BackendProcessStopOptionsDTO;
4339
+ } | {
4340
+ type: "backend_process_send";
4341
+ processId: string;
4342
+ payload: unknown;
4343
+ userId?: string;
4344
+ } | {
4345
+ type: "macros_resolve";
4346
+ requestId: string;
4347
+ template: string;
4348
+ chatId?: string;
4349
+ characterId?: string;
4350
+ userId?: string;
4351
+ commit?: boolean;
4352
+ } | {
4353
+ type: "image_gen_generate";
4354
+ requestId: string;
4355
+ input: ImageGenRequestDTO;
4356
+ }
4357
+ /**
4358
+ * Start a WebSocket-backed image stream. Only providers that advertise
4359
+ * `websocketPreviewStreaming` accept this request.
4360
+ */
4361
+ | {
4362
+ type: "image_gen_generate_stream";
4363
+ requestId: string;
4364
+ input: Omit<ImageGenStreamRequestDTO, "signal">;
4365
+ } | {
4366
+ type: "image_gen_cancel_stream";
4367
+ requestId: string;
4368
+ } | {
4369
+ type: "image_gen_providers";
4370
+ requestId: string;
4371
+ userId?: string;
4372
+ } | {
4373
+ type: "image_gen_connections_list";
4374
+ requestId: string;
4375
+ userId?: string;
4376
+ } | {
4377
+ type: "image_gen_connections_get";
4378
+ requestId: string;
4379
+ connectionId: string;
4380
+ userId?: string;
4381
+ } | {
4382
+ type: "image_gen_models";
4383
+ requestId: string;
4384
+ connectionId: string;
4385
+ userId?: string;
4386
+ } | {
4387
+ type: "images_list";
4388
+ requestId: string;
4389
+ limit?: number;
4390
+ offset?: number;
4391
+ specificity?: ImageSpecificityDTO;
4392
+ onlyOwned?: boolean;
4393
+ characterId?: string;
4394
+ chatId?: string;
4395
+ userId?: string;
4396
+ } | {
4397
+ type: "images_get";
4398
+ requestId: string;
4399
+ imageId: string;
4400
+ specificity?: ImageSpecificityDTO;
4401
+ onlyOwned?: boolean;
4402
+ characterId?: string;
4403
+ chatId?: string;
4404
+ userId?: string;
4405
+ } | {
4406
+ type: "images_upload";
4407
+ requestId: string;
4408
+ input: ImageUploadDTO;
4409
+ userId?: string;
4410
+ } | {
4411
+ type: "images_upload_many";
4412
+ requestId: string;
4413
+ items: ImageUploadDTO[];
4414
+ userId?: string;
4415
+ concurrency?: number;
4416
+ } | {
4417
+ type: "images_upload_from_data_url";
4418
+ requestId: string;
4419
+ dataUrl: string;
4420
+ originalFilename?: string;
4421
+ owner_character_id?: string;
4422
+ owner_chat_id?: string;
4423
+ skip_thumbnail_processing?: boolean;
4424
+ userId?: string;
4425
+ } | {
4426
+ type: "images_delete";
4427
+ requestId: string;
4428
+ imageId: string;
4429
+ userId?: string;
4430
+ } | {
4431
+ type: "media_audio_convert";
4432
+ requestId: string;
4433
+ input: MediaConvertAudioRequestDTO;
4434
+ } | {
4435
+ type: "media_video_convert";
4436
+ requestId: string;
4437
+ input: MediaConvertVideoRequestDTO;
4438
+ } | {
4439
+ type: "media_video_transcode";
4440
+ requestId: string;
4441
+ input: MediaTranscodeVideoRequestDTO;
4442
+ } | {
4443
+ type: "media_video_remove_audio";
4444
+ requestId: string;
4445
+ input: MediaRemoveAudioFromVideoRequestDTO;
4446
+ } | {
4447
+ type: "media_video_add_audio";
4448
+ requestId: string;
4449
+ input: MediaAddAudioToVideoRequestDTO;
4450
+ } | {
4451
+ type: "media_video_from_image_audio";
4452
+ requestId: string;
4453
+ input: MediaCreateVideoFromImageAndAudioRequestDTO;
4454
+ } | {
4455
+ type: "theme_apply";
4456
+ requestId: string;
4457
+ overrides: ThemeOverrideDTO;
4458
+ userId?: string;
4459
+ } | {
4460
+ type: "theme_apply_palette";
4461
+ requestId: string;
4462
+ palette: ThemePaletteConfigDTO | null;
4463
+ userId?: string;
4464
+ } | {
4465
+ type: "theme_clear";
4466
+ requestId: string;
4467
+ userId?: string;
4468
+ } | {
4469
+ type: "theme_get_current";
4470
+ requestId: string;
4471
+ userId?: string;
4472
+ } | {
4473
+ type: "color_extract";
4474
+ requestId: string;
4475
+ imageId: string;
4476
+ userId?: string;
4477
+ } | {
4478
+ type: "theme_generate_variables";
4479
+ requestId: string;
4480
+ config: ThemeVariablesConfigDTO;
4481
+ } | {
4482
+ type: "commands_register";
4483
+ commands: SpindleCommandDTO[];
4484
+ } | {
4485
+ type: "commands_unregister";
4486
+ commandIds: string[];
4487
+ } | {
4488
+ type: "version_get_backend";
4489
+ requestId: string;
4490
+ } | {
4491
+ type: "version_get_frontend";
4492
+ requestId: string;
4493
+ } | {
4494
+ type: "tokens_count_text";
4495
+ requestId: string;
4496
+ text: string;
4497
+ model?: string;
4498
+ modelSource?: TokenModelSourceDTO;
4499
+ userId?: string;
4500
+ } | {
4501
+ type: "tokens_count_messages";
4502
+ requestId: string;
4503
+ messages: Array<Pick<LlmMessageDTO, "role" | "content">>;
4504
+ model?: string;
4505
+ modelSource?: TokenModelSourceDTO;
4506
+ userId?: string;
4507
+ } | {
4508
+ type: "tokens_count_chat";
4509
+ requestId: string;
4510
+ chatId: string;
4511
+ model?: string;
4512
+ modelSource?: TokenModelSourceDTO;
4513
+ userId?: string;
4514
+ } | {
4515
+ type: "web_search_query";
4516
+ requestId: string;
4517
+ query: string;
4518
+ count?: number;
4519
+ scrape?: boolean;
4520
+ userId?: string;
4521
+ } | {
4522
+ type: "web_search_get_settings";
4523
+ requestId: string;
4524
+ userId?: string;
4525
+ } | {
4526
+ type: "ui_get_drawer_tabs";
4527
+ requestId: string;
4528
+ userId?: string;
4529
+ } | {
4530
+ type: "ui_get_settings_tabs";
4531
+ requestId: string;
4532
+ userId?: string;
4533
+ } | {
4534
+ type: "ui_navigate";
4535
+ requestId: string;
4536
+ action: "open_drawer_tab" | "close_drawer" | "open_settings" | "close_settings" | "open_command_palette" | "close_command_palette";
4537
+ tabId?: string;
4538
+ viewId?: string;
4539
+ userId?: string;
4540
+ } | WorkerToHostProviderMessage;
4541
+ export type HostToWorker = {
4542
+ type: "init";
4543
+ manifest: SpindleManifest;
4544
+ storagePath: string;
4545
+ host: SpindleHostDescriptorV1;
4546
+ } | {
4547
+ type: "event";
4548
+ event: string;
4549
+ payload: unknown;
4550
+ userId?: string;
4551
+ } | {
4552
+ type: "rpc_pool_request";
4553
+ requestId: string;
4554
+ endpoint: string;
4555
+ requesterExtensionId: string;
4556
+ rpcPermissionScopeId: string;
4557
+ effectivePermissions: string[];
4558
+ } | {
4559
+ type: "intercept_request";
4560
+ requestId: string;
4561
+ registrationId: string;
4562
+ messages: LlmMessageDTO[];
4563
+ context: Omit<InterceptorContextDTO, "signal">;
4564
+ } | {
4565
+ type: "intercept_abort";
4566
+ requestId: string;
4567
+ registrationId: string;
4568
+ reason?: string;
4569
+ } | {
4570
+ type: "context_handler_request";
4571
+ requestId: string;
4572
+ context: unknown;
4573
+ } | {
4574
+ type: "macro_interceptor_request";
4575
+ requestId: string;
4576
+ ctx: MacroInterceptorCtxDTO;
4577
+ } | {
4578
+ type: "world_info_interceptor_request";
4579
+ requestId: string;
4580
+ ctx: WorldInfoInterceptorCtxDTO;
4581
+ } | {
4582
+ type: "message_content_processor_request";
4583
+ requestId: string;
4584
+ ctx: MessageContentProcessorCtxDTO;
4585
+ } | {
4586
+ type: "response";
4587
+ requestId: string;
4588
+ result?: unknown;
4589
+ error?: string | HostResponseErrorDTO;
4590
+ } | {
4591
+ type: "permission_denied";
4592
+ permission: string;
4593
+ operation: string;
4594
+ } | {
4595
+ type: "permission_changed";
4596
+ /** Extension identifier for the worker receiving this scoped change */
4597
+ extensionId?: string;
4598
+ permission: string;
4599
+ granted: boolean;
4600
+ allGranted: string[];
4601
+ } | {
4602
+ type: "tool_invocation";
4603
+ requestId: string;
4604
+ toolName: string;
4605
+ args: Record<string, unknown>;
4606
+ /**
4607
+ * Populated when the invocation originates from a council execution
4608
+ * cycle — carries the assigned council member's identity, role, chance,
4609
+ * avatar URL, and Lumia personality fields so the extension can tailor
4610
+ * its tool pipeline to the member on whose behalf it is running.
4611
+ *
4612
+ * Undefined for non-council invocation paths.
4613
+ */
4614
+ councilMember?: CouncilMemberContext;
4615
+ /**
4616
+ * Structured chat context for council invocations — the same messages
4617
+ * that populated `args.context` (flattened string), but with role
4618
+ * boundaries preserved so extensions can re-render or filter them
4619
+ * without parsing. Undefined for non-council invocation paths.
4620
+ */
4621
+ contextMessages?: LlmMessageDTO[];
4622
+ } | {
4623
+ type: "shutdown";
4624
+ } | {
4625
+ type: "frontend_message";
4626
+ payload: unknown;
4627
+ userId: string;
4628
+ } | {
4629
+ type: "frontend_process_lifecycle";
4630
+ event: FrontendProcessLifecycleEventDTO;
4631
+ } | {
4632
+ type: "frontend_process_message";
4633
+ processId: string;
4634
+ payload: unknown;
4635
+ userId: string;
4636
+ } | {
4637
+ type: "backend_process_lifecycle";
4638
+ event: BackendProcessLifecycleEventDTO;
4639
+ } | {
4640
+ type: "backend_process_message";
4641
+ processId: string;
4642
+ payload: unknown;
4643
+ userId: string;
4644
+ } | {
4645
+ type: "oauth_callback";
4646
+ requestId: string;
4647
+ params: Record<string, string>;
4648
+ } | {
4649
+ type: "command_invoked";
4650
+ commandId: string;
4651
+ context: SpindleCommandContextDTO;
4652
+ userId: string;
4653
+ }
4654
+ /**
4655
+ * One streamed chunk for a generation started via
4656
+ * `request_generation_stream`. Multiple `token` / `reasoning` chunks
4657
+ * may arrive, terminating with exactly one `done` chunk on success.
4658
+ */
4659
+ | {
4660
+ type: "generation_stream_chunk";
4661
+ requestId: string;
4662
+ chunk: StreamChunkDTO;
4663
+ }
4664
+ /**
4665
+ * Terminal failure for a generation started via
4666
+ * `request_generation_stream`. Mutually exclusive with the `done`
4667
+ * chunk in `generation_stream_chunk`. Aborts surface here too.
4668
+ */
4669
+ | {
4670
+ type: "generation_stream_error";
4671
+ requestId: string;
4672
+ error: string;
4673
+ } | {
4674
+ type: "image_gen_stream_chunk";
4675
+ requestId: string;
4676
+ event: ImageGenStreamEventDTO;
4677
+ } | {
4678
+ type: "image_gen_stream_error";
4679
+ requestId: string;
4680
+ error: string;
4681
+ } | HostToWorkerProviderMessage;
4682
+ export {};