lumiverse-spindle-types 0.4.37 → 0.4.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/api.ts +142 -2
- package/src/index.ts +7 -0
- package/src/permissions.ts +12 -9
- package/src/spindle-api.ts +80 -0
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -67,6 +67,112 @@ export interface MacroResolveResultDTO {
|
|
|
67
67
|
diagnostics: Array<{ message: string; offset: number; length: number }>;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
// ─── Macro Interceptor (permission: "macro_interceptor") ────────────────
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Where a macro evaluation originated. Useful for interceptors that only
|
|
74
|
+
* want to fire for certain call sites (e.g. prompt assembly vs. response
|
|
75
|
+
* post-processing vs. display-time resolution).
|
|
76
|
+
*/
|
|
77
|
+
export type MacroInterceptorPhase =
|
|
78
|
+
| "prompt"
|
|
79
|
+
| "display"
|
|
80
|
+
| "response"
|
|
81
|
+
| "other";
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Structured-clone snapshot of the live macro evaluation environment,
|
|
85
|
+
* passed to a macro interceptor. All values are read-only copies — mutating
|
|
86
|
+
* them has no effect on the real environment. Persist state via
|
|
87
|
+
* `spindle.variables.*` helpers instead.
|
|
88
|
+
*/
|
|
89
|
+
export interface MacroInterceptorEnvDTO {
|
|
90
|
+
readonly commit: boolean;
|
|
91
|
+
readonly names: Record<string, string>;
|
|
92
|
+
readonly character: Record<string, unknown>;
|
|
93
|
+
readonly chat: Record<string, unknown>;
|
|
94
|
+
readonly system: Record<string, unknown>;
|
|
95
|
+
readonly variables: {
|
|
96
|
+
readonly local: Record<string, string>;
|
|
97
|
+
readonly global: Record<string, string>;
|
|
98
|
+
readonly chat: Record<string, string>;
|
|
99
|
+
};
|
|
100
|
+
readonly extra: Record<string, unknown>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Context passed to a macro interceptor handler on every iteration of
|
|
105
|
+
* `MacroEvaluator.evaluate()`. The handler receives the current raw
|
|
106
|
+
* template (already transformed by any earlier interceptors in the chain)
|
|
107
|
+
* and returns either a transformed template string or `void` to pass through.
|
|
108
|
+
*/
|
|
109
|
+
export interface MacroInterceptorCtxDTO {
|
|
110
|
+
readonly template: string;
|
|
111
|
+
readonly env: MacroInterceptorEnvDTO;
|
|
112
|
+
readonly commit: boolean;
|
|
113
|
+
readonly phase: MacroInterceptorPhase;
|
|
114
|
+
readonly sourceHint?: string;
|
|
115
|
+
/**
|
|
116
|
+
* User ID that initiated the macro resolution (when available). Relevant
|
|
117
|
+
* for operator-scoped extensions that need to route work through other
|
|
118
|
+
* Spindle APIs on that user's behalf.
|
|
119
|
+
*/
|
|
120
|
+
readonly userId?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Return value of a macro interceptor handler.
|
|
125
|
+
* - `string` replaces the template for subsequent interceptors + parsing.
|
|
126
|
+
* - `void` / `undefined` passes the template through unchanged.
|
|
127
|
+
*/
|
|
128
|
+
export type MacroInterceptorResultDTO = string | void;
|
|
129
|
+
|
|
130
|
+
// ─── Message Content Processor (permission: "chat_mutation") ───────────
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Which content-write path triggered a message content processor run.
|
|
134
|
+
* `"create"` covers both user-initiated `POST .../messages` writes and
|
|
135
|
+
* auto-inserted greeting rows.
|
|
136
|
+
*/
|
|
137
|
+
export type MessageContentProcessorOrigin =
|
|
138
|
+
| "create"
|
|
139
|
+
| "update"
|
|
140
|
+
| "swipe_add"
|
|
141
|
+
| "swipe_update";
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Context passed to a message content processor before a user-initiated
|
|
145
|
+
* message write reaches SQLite. Handlers can inspect this and return a
|
|
146
|
+
* patch (new `content` / merged `extra`) to transform what is stored and
|
|
147
|
+
* what WebSocket subscribers observe on first paint.
|
|
148
|
+
*/
|
|
149
|
+
export interface MessageContentProcessorCtxDTO {
|
|
150
|
+
chatId: string;
|
|
151
|
+
/** Undefined for `"create"` origins (the row doesn't exist yet). */
|
|
152
|
+
messageId?: string;
|
|
153
|
+
content: string;
|
|
154
|
+
extra?: Record<string, unknown>;
|
|
155
|
+
origin: MessageContentProcessorOrigin;
|
|
156
|
+
/** Set for `"swipe_update"` only — the zero-based index of the swipe being rewritten. */
|
|
157
|
+
swipeIndex?: number;
|
|
158
|
+
/** Owning user for the write. Pass this through to operator-scoped Spindle calls. */
|
|
159
|
+
userId: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Return value for a message content processor handler. Return `undefined`
|
|
164
|
+
* / `void` to pass through, or a partial patch to modify the write:
|
|
165
|
+
* - `content` (if present) replaces the content for downstream processors
|
|
166
|
+
* and the DB write.
|
|
167
|
+
* - `extra` (if present) shallow-merges into the existing `extra` — keys
|
|
168
|
+
* you omit are preserved. Ignored on swipe origins (swipes share the
|
|
169
|
+
* parent message's `extra`).
|
|
170
|
+
*/
|
|
171
|
+
export interface MessageContentProcessorResultDTO {
|
|
172
|
+
content?: string;
|
|
173
|
+
extra?: Record<string, unknown>;
|
|
174
|
+
}
|
|
175
|
+
|
|
70
176
|
export interface ToolRegistrationDTO {
|
|
71
177
|
name: string;
|
|
72
178
|
display_name: string;
|
|
@@ -267,8 +373,8 @@ export interface ImageGenResultDTO {
|
|
|
267
373
|
|
|
268
374
|
/**
|
|
269
375
|
* Safe representation of a character exposed to extensions.
|
|
270
|
-
*
|
|
271
|
-
*
|
|
376
|
+
* Includes the full `extensions` blob so extensions can read and write
|
|
377
|
+
* their own namespaced keys alongside the allowlisted `world_book_ids`.
|
|
272
378
|
*/
|
|
273
379
|
export interface CharacterDTO {
|
|
274
380
|
id: string;
|
|
@@ -290,6 +396,8 @@ export interface CharacterDTO {
|
|
|
290
396
|
* single-id form is auto-migrated, so consumers can rely on the array.
|
|
291
397
|
*/
|
|
292
398
|
world_book_ids: string[];
|
|
399
|
+
/** The raw extensions object. Extensions should namespace their keys. */
|
|
400
|
+
extensions: Record<string, any>;
|
|
293
401
|
created_at: number;
|
|
294
402
|
updated_at: number;
|
|
295
403
|
}
|
|
@@ -309,6 +417,8 @@ export interface CharacterCreateDTO {
|
|
|
309
417
|
creator?: string;
|
|
310
418
|
/** Optional initial world book attachments. */
|
|
311
419
|
world_book_ids?: string[];
|
|
420
|
+
/** Optional initial extension data. */
|
|
421
|
+
extensions?: Record<string, any>;
|
|
312
422
|
}
|
|
313
423
|
|
|
314
424
|
export interface CharacterUpdateDTO {
|
|
@@ -329,6 +439,12 @@ export interface CharacterUpdateDTO {
|
|
|
329
439
|
* detach all books. Omit the field to leave attachments unchanged.
|
|
330
440
|
*/
|
|
331
441
|
world_book_ids?: string[];
|
|
442
|
+
/**
|
|
443
|
+
* Shallow-merged into the character's existing extensions.
|
|
444
|
+
* Extension-provided keys overwrite existing ones; omitting a key leaves it
|
|
445
|
+
* untouched. Pass an empty object to make no changes, or omit entirely.
|
|
446
|
+
*/
|
|
447
|
+
extensions?: Record<string, any>;
|
|
332
448
|
}
|
|
333
449
|
|
|
334
450
|
export interface CharacterAvatarUploadDTO {
|
|
@@ -1304,6 +1420,20 @@ export type WorkerToHost =
|
|
|
1304
1420
|
requestId: string;
|
|
1305
1421
|
context: unknown;
|
|
1306
1422
|
}
|
|
1423
|
+
// ─── Macro Interceptor (gated: "macro_interceptor") ────────────────
|
|
1424
|
+
| { type: "register_macro_interceptor"; priority?: number }
|
|
1425
|
+
| {
|
|
1426
|
+
type: "macro_interceptor_result";
|
|
1427
|
+
requestId: string;
|
|
1428
|
+
result: MacroInterceptorResultDTO;
|
|
1429
|
+
}
|
|
1430
|
+
// ─── Message Content Processor (gated: "chat_mutation") ────────────
|
|
1431
|
+
| { type: "register_message_content_processor"; priority?: number }
|
|
1432
|
+
| {
|
|
1433
|
+
type: "message_content_processor_result";
|
|
1434
|
+
requestId: string;
|
|
1435
|
+
result: MessageContentProcessorResultDTO | void;
|
|
1436
|
+
}
|
|
1307
1437
|
| {
|
|
1308
1438
|
type: "macro_result";
|
|
1309
1439
|
requestId: string;
|
|
@@ -1477,6 +1607,16 @@ export type HostToWorker =
|
|
|
1477
1607
|
requestId: string;
|
|
1478
1608
|
context: unknown;
|
|
1479
1609
|
}
|
|
1610
|
+
| {
|
|
1611
|
+
type: "macro_interceptor_request";
|
|
1612
|
+
requestId: string;
|
|
1613
|
+
ctx: MacroInterceptorCtxDTO;
|
|
1614
|
+
}
|
|
1615
|
+
| {
|
|
1616
|
+
type: "message_content_processor_request";
|
|
1617
|
+
requestId: string;
|
|
1618
|
+
ctx: MessageContentProcessorCtxDTO;
|
|
1619
|
+
}
|
|
1480
1620
|
| {
|
|
1481
1621
|
type: "response";
|
|
1482
1622
|
requestId: string;
|
package/src/index.ts
CHANGED
|
@@ -81,6 +81,13 @@ export type {
|
|
|
81
81
|
TokenModelSourceDTO,
|
|
82
82
|
TokenCountOptionsDTO,
|
|
83
83
|
TokenCountResultDTO,
|
|
84
|
+
MacroInterceptorPhase,
|
|
85
|
+
MacroInterceptorEnvDTO,
|
|
86
|
+
MacroInterceptorCtxDTO,
|
|
87
|
+
MacroInterceptorResultDTO,
|
|
88
|
+
MessageContentProcessorOrigin,
|
|
89
|
+
MessageContentProcessorCtxDTO,
|
|
90
|
+
MessageContentProcessorResultDTO,
|
|
84
91
|
WorkerToHost,
|
|
85
92
|
HostToWorker,
|
|
86
93
|
} from "./api";
|
package/src/permissions.ts
CHANGED
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
* Free tier (no declaration needed): events, storage, macros, dom, variables
|
|
5
5
|
*
|
|
6
6
|
* Gated tier (must declare):
|
|
7
|
-
* - "generation"
|
|
8
|
-
* - "interceptor"
|
|
9
|
-
* - "tools"
|
|
10
|
-
* - "cors_proxy"
|
|
11
|
-
* - "context_handler"
|
|
7
|
+
* - "generation" — fire generations on behalf of user
|
|
8
|
+
* - "interceptor" — pre-generation prompt modification
|
|
9
|
+
* - "tools" — register LLM tools
|
|
10
|
+
* - "cors_proxy" — use CORS proxy
|
|
11
|
+
* - "context_handler" — register global context middleware
|
|
12
12
|
* - "generation_parameters" — inject parameters into in-flight generations via interceptors
|
|
13
|
-
* - "characters"
|
|
14
|
-
* - "chats"
|
|
15
|
-
* - "personas"
|
|
13
|
+
* - "characters" — CRUD on character cards
|
|
14
|
+
* - "chats" — CRUD on chat sessions
|
|
15
|
+
* - "personas" — CRUD on personas
|
|
16
|
+
* - "macro_interceptor" — transform raw templates before macro parsing/dispatch
|
|
16
17
|
*/
|
|
17
18
|
export type SpindlePermission =
|
|
18
19
|
| "generation"
|
|
@@ -32,7 +33,8 @@ export type SpindlePermission =
|
|
|
32
33
|
| "personas"
|
|
33
34
|
| "push_notification"
|
|
34
35
|
| "image_gen"
|
|
35
|
-
| "generation_parameters"
|
|
36
|
+
| "generation_parameters"
|
|
37
|
+
| "macro_interceptor";
|
|
36
38
|
|
|
37
39
|
export const ALL_PERMISSIONS: readonly SpindlePermission[] = [
|
|
38
40
|
"generation",
|
|
@@ -53,6 +55,7 @@ export const ALL_PERMISSIONS: readonly SpindlePermission[] = [
|
|
|
53
55
|
"push_notification",
|
|
54
56
|
"image_gen",
|
|
55
57
|
"generation_parameters",
|
|
58
|
+
"macro_interceptor",
|
|
56
59
|
] as const;
|
|
57
60
|
|
|
58
61
|
export function isValidPermission(p: string): p is SpindlePermission {
|
package/src/spindle-api.ts
CHANGED
|
@@ -53,6 +53,10 @@ import type {
|
|
|
53
53
|
StreamChunkDTO,
|
|
54
54
|
TokenCountOptionsDTO,
|
|
55
55
|
TokenCountResultDTO,
|
|
56
|
+
MacroInterceptorCtxDTO,
|
|
57
|
+
MacroInterceptorResultDTO,
|
|
58
|
+
MessageContentProcessorCtxDTO,
|
|
59
|
+
MessageContentProcessorResultDTO,
|
|
56
60
|
} from "./api";
|
|
57
61
|
|
|
58
62
|
/** The global `spindle` object available in backend extension workers */
|
|
@@ -676,6 +680,82 @@ export interface SpindleAPI {
|
|
|
676
680
|
priority?: number
|
|
677
681
|
): void;
|
|
678
682
|
|
|
683
|
+
/**
|
|
684
|
+
* Register a macro interceptor (permission: `macro_interceptor`).
|
|
685
|
+
*
|
|
686
|
+
* Runs at the top of `MacroEvaluator.evaluate()`, once per fixed-point
|
|
687
|
+
* iteration, before Lumiverse parses the template. Receives the raw
|
|
688
|
+
* template plus a read-only env snapshot and returns either a transformed
|
|
689
|
+
* template or `void` to pass through.
|
|
690
|
+
*
|
|
691
|
+
* Use this when per-macro RPC cost dominates iteration-heavy templates
|
|
692
|
+
* (e.g. `{{#each LARGE_LIST}}…{{my_macro}}…{{/each}}`). For single macros
|
|
693
|
+
* without iteration, prefer {@link SpindleAPI.registerMacro}.
|
|
694
|
+
*
|
|
695
|
+
* Each invocation runs inside a 10-second wall-clock budget on the host.
|
|
696
|
+
* On timeout or thrown error the chain logs the failure and forwards the
|
|
697
|
+
* previous template to the next handler — macro evaluation itself never
|
|
698
|
+
* aborts. A second registration from the same extension replaces the
|
|
699
|
+
* previous handler.
|
|
700
|
+
*
|
|
701
|
+
* @param handler Returns the transformed template, or `void` to pass through.
|
|
702
|
+
* @param priority Lower values run first. Default `100`.
|
|
703
|
+
*
|
|
704
|
+
* @example
|
|
705
|
+
* ```ts
|
|
706
|
+
* spindle.registerMacroInterceptor(async (ctx) => {
|
|
707
|
+
* if (!ctx.template.includes('{{my_macro')) return
|
|
708
|
+
* return resolveInWorker(ctx.template, ctx.env)
|
|
709
|
+
* }, 100)
|
|
710
|
+
* ```
|
|
711
|
+
*/
|
|
712
|
+
registerMacroInterceptor(
|
|
713
|
+
handler: (
|
|
714
|
+
ctx: MacroInterceptorCtxDTO
|
|
715
|
+
) => Promise<MacroInterceptorResultDTO>,
|
|
716
|
+
priority?: number
|
|
717
|
+
): void;
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Register a message content processor (permission: `chat_mutation`).
|
|
721
|
+
*
|
|
722
|
+
* Runs synchronously inside every user-initiated message-write REST
|
|
723
|
+
* route (create, update, swipe add/update) and the auto-greeting path,
|
|
724
|
+
* before the row reaches SQLite. The returned patch transforms both the
|
|
725
|
+
* stored row and every `MESSAGE_SENT` / `MESSAGE_EDITED` / `MESSAGE_SWIPED`
|
|
726
|
+
* subscriber on first paint.
|
|
727
|
+
*
|
|
728
|
+
* Not invoked for `spindle.chat.*` mutations — those paths intentionally
|
|
729
|
+
* bypass the processor chain to avoid loops on an extension's own writes.
|
|
730
|
+
*
|
|
731
|
+
* Each invocation runs inside a 10-second wall-clock budget on the host.
|
|
732
|
+
* On timeout or thrown error the chain logs the failure and forwards the
|
|
733
|
+
* previous content to the next handler — the write still proceeds. A
|
|
734
|
+
* second registration from the same extension replaces the previous
|
|
735
|
+
* handler.
|
|
736
|
+
*
|
|
737
|
+
* Every millisecond of handler work is visible latency on send/edit/swipe.
|
|
738
|
+
* Keep handlers tight.
|
|
739
|
+
*
|
|
740
|
+
* @param handler Receives the about-to-be-committed content; returns a
|
|
741
|
+
* patch, or `void` to pass through.
|
|
742
|
+
* @param priority Lower values run first. Default `100`.
|
|
743
|
+
*
|
|
744
|
+
* @example
|
|
745
|
+
* ```ts
|
|
746
|
+
* spindle.registerMessageContentProcessor(async (ctx) => {
|
|
747
|
+
* if (!ctx.content.includes('{{my_macro}}')) return
|
|
748
|
+
* return { content: ctx.content.replaceAll('{{my_macro}}', 'resolved') }
|
|
749
|
+
* }, 50)
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
752
|
+
registerMessageContentProcessor(
|
|
753
|
+
handler: (
|
|
754
|
+
ctx: MessageContentProcessorCtxDTO
|
|
755
|
+
) => Promise<MessageContentProcessorResultDTO | void>,
|
|
756
|
+
priority?: number
|
|
757
|
+
): void;
|
|
758
|
+
|
|
679
759
|
/**
|
|
680
760
|
* Send a message to the frontend module.
|
|
681
761
|
*
|