lumiverse-spindle-types 0.4.26 → 0.4.28

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lumiverse-spindle-types",
3
- "version": "0.4.26",
3
+ "version": "0.4.28",
4
4
  "types": "./src/index.ts",
5
5
  "keywords": [
6
6
  "lumiverse",
package/src/api.ts CHANGED
@@ -947,6 +947,28 @@ export interface MessageSwipedPayloadDTO {
947
947
  previousSwipeId?: number;
948
948
  }
949
949
 
950
+ /**
951
+ * Payload for `SWIPE_EDITED` events.
952
+ *
953
+ * Fires when `spindle.chat.updateMessage()` explicitly supplies one or more
954
+ * swipe-shaped fields (`swipes`, `swipe_id`, or `swipe_dates`). Plain content
955
+ * edits that mirror into the active swipe slot continue to emit only
956
+ * `MESSAGE_EDITED` — this event is for extension-driven rewrites of the
957
+ * swipe array itself, index navigation, or date-array rewrites.
958
+ *
959
+ * `MESSAGE_SWIPED` still fires for the dedicated swipe REST routes
960
+ * (`addSwipe`, `updateSwipe`, `deleteSwipe`, `cycleSwipe`) with its
961
+ * `action` discriminator. Consumers that need fine-grained action semantics
962
+ * should prefer `MESSAGE_SWIPED`; `SWIPE_EDITED` is intentionally coarser.
963
+ */
964
+ export interface SwipeEditedPayloadDTO {
965
+ chatId: string;
966
+ /** The full message after the mutation. */
967
+ message: ChatMessageDTO;
968
+ /** Active swipe index before the mutation. Equals `message.swipe_id` when navigation did not occur. */
969
+ previousSwipeId: number;
970
+ }
971
+
950
972
  /**
951
973
  * Payload delivered to `spindle.on("TOOL_INVOCATION", ...)` handlers.
952
974
  *
package/src/events.ts CHANGED
@@ -17,6 +17,7 @@ export enum CoreEventType {
17
17
  MESSAGE_EDITED = "MESSAGE_EDITED",
18
18
  MESSAGE_DELETED = "MESSAGE_DELETED",
19
19
  MESSAGE_SWIPED = "MESSAGE_SWIPED",
20
+ SWIPE_EDITED = "SWIPE_EDITED",
20
21
  CHARACTER_MESSAGE_RENDERED = "CHARACTER_MESSAGE_RENDERED",
21
22
  USER_MESSAGE_RENDERED = "USER_MESSAGE_RENDERED",
22
23
  GENERATION_STARTED = "GENERATION_STARTED",
package/src/index.ts CHANGED
@@ -71,6 +71,7 @@ export type {
71
71
  ChatMessageDTO,
72
72
  MessageSwipeAction,
73
73
  MessageSwipedPayloadDTO,
74
+ SwipeEditedPayloadDTO,
74
75
  ToolInvocationPayloadDTO,
75
76
  WorkerToHost,
76
77
  HostToWorker,
package/src/manifest.ts CHANGED
@@ -23,6 +23,13 @@ export interface SpindleManifest {
23
23
  minimum_lumiverse_version?: string;
24
24
  /** Files/directories to seed into extension storage on install/import/update */
25
25
  storage_seed_files?: SpindleStorageSeedFile[];
26
+ /**
27
+ * Maximum wall-clock time (milliseconds) the host will wait for this
28
+ * extension's interceptor to return a result before aborting with an error.
29
+ * Overrides the user-level `spindleSettings.interceptorTimeoutMs` setting.
30
+ * Clamped to [1000, 300000] by the host.
31
+ */
32
+ interceptorTimeoutMs?: number;
26
33
  }
27
34
 
28
35
  export interface SpindleStorageSeedFile {
@@ -45,6 +45,7 @@ import type {
45
45
  GenerationStoppedPayloadDTO,
46
46
  GenerationObserver,
47
47
  MessageSwipedPayloadDTO,
48
+ SwipeEditedPayloadDTO,
48
49
  ToolInvocationPayloadDTO,
49
50
  StreamChunkDTO,
50
51
  } from "./api";
@@ -65,6 +66,15 @@ export interface SpindleAPI {
65
66
  * `swipeId` identifies which slot the event concerns.
66
67
  */
67
68
  on(event: "MESSAGE_SWIPED", handler: (payload: MessageSwipedPayloadDTO, userId?: string) => void): () => void;
69
+ /**
70
+ * Subscribe to `SWIPE_EDITED` events. Emitted when {@link SpindleAPI.chat.updateMessage}
71
+ * explicitly rewrites one or more swipe-shaped fields (`swipes`, `swipe_id`,
72
+ * or `swipe_dates`). Plain content-only edits continue to emit `MESSAGE_EDITED`
73
+ * only. This event is coarser than `MESSAGE_SWIPED` by design — use
74
+ * `MESSAGE_SWIPED` when you need the `added`/`updated`/`deleted`/`navigated`
75
+ * discriminator.
76
+ */
77
+ on(event: "SWIPE_EDITED", handler: (payload: SwipeEditedPayloadDTO, userId?: string) => void): () => void;
68
78
  /**
69
79
  * Receive invocations for tools registered via {@link SpindleAPI.registerTool}.
70
80
  *
@@ -308,6 +318,12 @@ export interface SpindleAPI {
308
318
  id: string;
309
319
  role: "system" | "user" | "assistant";
310
320
  content: string;
321
+ /**
322
+ * The free-form `extra` bag minus `spindle_metadata` (which is surfaced
323
+ * separately on `metadata`). Carries reasoning text/duration, attachments,
324
+ * and any host-owned housekeeping fields.
325
+ */
326
+ extra: Record<string, unknown>;
311
327
  metadata?: Record<string, unknown>;
312
328
  /** Index of the active swipe in `swipes`. `0` when the message has no alternates. */
313
329
  swipe_id: number;
@@ -316,6 +332,8 @@ export interface SpindleAPI {
316
332
  * `swipes[swipe_id]` always equals `content`.
317
333
  */
318
334
  swipes: string[];
335
+ /** Per-swipe creation timestamps (unix epoch seconds), aligned with `swipes`. */
336
+ swipe_dates: number[];
319
337
  }>>;
320
338
  appendMessage(
321
339
  chatId: string,
@@ -325,12 +343,36 @@ export interface SpindleAPI {
325
343
  metadata?: Record<string, unknown>;
326
344
  }
327
345
  ): Promise<{ id: string }>;
346
+ /**
347
+ * Patch an existing message. All fields are optional; `undefined` leaves
348
+ * the field untouched. Precedence rules:
349
+ *
350
+ * - If `content` is supplied, it overwrites both `messages.content` and
351
+ * `swipes[swipe_id]` (the active slot).
352
+ * - If `swipes` is supplied without `content`, the new active content is
353
+ * derived from `swipes[swipe_id]` (either the supplied `swipe_id` or
354
+ * the existing one).
355
+ * - If only `swipe_id` is supplied, it acts as a navigation and content
356
+ * is re-derived from the existing swipes array.
357
+ * - `reasoning.text === null` clears `extra.reasoning`; `reasoning.duration
358
+ * === null` clears `extra.reasoning_duration`. The two are independent.
359
+ *
360
+ * When any of `swipes`/`swipe_id`/`swipe_dates` are supplied, a
361
+ * `SWIPE_EDITED` event is emitted alongside (or instead of) `MESSAGE_EDITED`.
362
+ */
328
363
  updateMessage(
329
364
  chatId: string,
330
365
  messageId: string,
331
366
  patch: {
332
367
  content?: string;
333
368
  metadata?: Record<string, unknown>;
369
+ swipes?: string[];
370
+ swipe_id?: number;
371
+ swipe_dates?: number[];
372
+ reasoning?: {
373
+ text?: string | null;
374
+ duration?: number | null;
375
+ };
334
376
  }
335
377
  ): Promise<void>;
336
378
  deleteMessage(chatId: string, messageId: string): Promise<void>;