@stina/extension-api 1.3.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/{chunk-ZB7GJUPS.js → chunk-S3YP4QPF.js} +1 -1
  2. package/dist/{chunk-ZB7GJUPS.js.map → chunk-S3YP4QPF.js.map} +1 -1
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +19 -4
  5. package/dist/index.d.ts +19 -4
  6. package/dist/index.js +1 -1
  7. package/dist/runtime.cjs +53 -6
  8. package/dist/runtime.cjs.map +1 -1
  9. package/dist/runtime.d.cts +2 -2
  10. package/dist/runtime.d.ts +2 -2
  11. package/dist/runtime.js +54 -7
  12. package/dist/runtime.js.map +1 -1
  13. package/dist/schemas/index.cjs +34 -2
  14. package/dist/schemas/index.cjs.map +1 -1
  15. package/dist/schemas/index.d.cts +91 -5
  16. package/dist/schemas/index.d.ts +91 -5
  17. package/dist/schemas/index.js +30 -2
  18. package/dist/schemas/index.js.map +1 -1
  19. package/dist/{types.tools-C0GqXQlu.d.cts → types.tools-DcFBsfRV.d.cts} +223 -17
  20. package/dist/{types.tools-C0GqXQlu.d.ts → types.tools-DcFBsfRV.d.ts} +223 -17
  21. package/package.json +1 -1
  22. package/schema/extension-manifest.schema.json +40 -0
  23. package/src/background.test.ts +33 -0
  24. package/src/background.ts +9 -0
  25. package/src/index.ts +8 -0
  26. package/src/messages.ts +5 -0
  27. package/src/runtime/accountsApi.ts +26 -0
  28. package/src/runtime/executionContext.test.ts +113 -0
  29. package/src/runtime/executionContext.ts +28 -2
  30. package/src/runtime/index.ts +1 -0
  31. package/src/runtime.ts +38 -3
  32. package/src/schemas/accounts.schema.test.ts +58 -0
  33. package/src/schemas/contributions.schema.ts +48 -0
  34. package/src/schemas/index.ts +6 -0
  35. package/src/schemas/permissions.schema.ts +11 -1
  36. package/src/types.context.ts +111 -0
  37. package/src/types.contributions.ts +47 -0
  38. package/src/types.permissions.ts +15 -0
  39. package/src/types.provider.ts +51 -1
  40. package/src/types.tools.ts +29 -15
  41. package/src/types.ts +9 -0
@@ -789,6 +789,8 @@ interface ExtensionContributions {
789
789
  commands?: CommandDefinition[];
790
790
  /** Prompt contributions for the system prompt */
791
791
  prompts?: PromptContribution[];
792
+ /** External accounts the extension works through, signed in once in Stina's settings */
793
+ accounts?: AccountContribution[];
792
794
  /** Storage collection declarations */
793
795
  storage?: {
794
796
  collections: {
@@ -799,6 +801,45 @@ interface ExtensionContributions {
799
801
  };
800
802
  };
801
803
  }
804
+ /**
805
+ * An external account provider the host can sign in to on the user's behalf.
806
+ *
807
+ * Only Microsoft for now, and only through Microsoft Graph: mail and calendar
808
+ * then share one sign-in and one token, instead of an IMAP token and a Graph
809
+ * token that an admin has to approve separately.
810
+ */
811
+ type AccountProvider = 'microsoft';
812
+ /**
813
+ * What an extension needs from an account the user connects to Stina.
814
+ *
815
+ * Stina asks the provider for every scope that the enabled extensions declare,
816
+ * all at once, so that the user - and, at work, their administrator - approves
817
+ * one request instead of one per extension. Declaring a scope here is therefore
818
+ * also what makes it show up in that request, and in the list of what is asked
819
+ * for in the settings.
820
+ *
821
+ * @example
822
+ * ```json
823
+ * "accounts": [
824
+ * {
825
+ * "provider": "microsoft",
826
+ * "scopes": ["Calendars.ReadWrite"],
827
+ * "reason": { "en": "Read and book events in your calendar", "sv": "Läsa och boka i din kalender" }
828
+ * }
829
+ * ]
830
+ * ```
831
+ */
832
+ interface AccountContribution {
833
+ provider: AccountProvider;
834
+ /**
835
+ * Microsoft Graph delegated permissions, by name: `Mail.Read`,
836
+ * `Calendars.ReadWrite`. The host adds `offline_access` and `User.Read`
837
+ * itself, so they are not declared here.
838
+ */
839
+ scopes: string[];
840
+ /** Why the extension needs them, shown next to the scopes in the settings. */
841
+ reason?: LocalizedString;
842
+ }
802
843
  /**
803
844
  * Tool settings view definition (UI schema)
804
845
  */
@@ -1178,9 +1219,22 @@ interface ModelCapabilities {
1178
1219
  * Report this per model *and* per auth mode, like `voiceDuplex`: the same
1179
1220
  * provider often serves both a vision model and a text-only one, and a picture
1180
1221
  * sent to the latter is at best ignored and at worst an error mid-conversation.
1181
- * Stina uses it to decide whether the paperclip is offered at all.
1182
1222
  */
1183
1223
  vision?: boolean;
1224
+ /**
1225
+ * The model can be given files to read alongside the text of a message, and
1226
+ * the provider folds {@link ChatMessage.files} into whatever its API calls them.
1227
+ *
1228
+ * Separate from `vision` because the two come apart in both directions: a model
1229
+ * that reads a PDF natively is not necessarily one that looks at a photograph,
1230
+ * and an OpenAI-compatible server with a vision model behind it may take images
1231
+ * and nothing else. Report it per model and per auth mode for the same reason
1232
+ * `vision` is reported that way.
1233
+ *
1234
+ * A provider that says nothing here keeps behaving exactly as before: it is
1235
+ * handed the text, and `files` is simply a field it does not read.
1236
+ */
1237
+ documents?: boolean;
1184
1238
  }
1185
1239
  /**
1186
1240
  * How the client wants to connect to the voice session.
@@ -1280,6 +1334,18 @@ interface ChatMessage {
1280
1334
  * Only ever `image/jpeg` or `image/png` — see `ChatAttachmentDTO` for why.
1281
1335
  */
1282
1336
  images?: ChatImage[];
1337
+ /**
1338
+ * Files the user attached for the model to read: PDFs and plain text.
1339
+ *
1340
+ * Additive in the same way `images` is, and split from it for the same reason
1341
+ * the two capabilities are separate — a provider folds a document into a
1342
+ * different content block than a picture, and many can do one and not the other.
1343
+ * A provider that ignores the field behaves exactly as it did before.
1344
+ *
1345
+ * Only present on user messages, because that is where both hosted providers
1346
+ * require a document to sit.
1347
+ */
1348
+ files?: ChatFile[];
1283
1349
  /** For assistant messages: tool calls made by the model */
1284
1350
  tool_calls?: ToolCall[];
1285
1351
  /** For tool messages: the ID of the tool call this is a response to */
@@ -1299,6 +1365,29 @@ interface ChatImage {
1299
1365
  /** The image itself, base64 with no data-URI prefix. */
1300
1366
  data: string;
1301
1367
  }
1368
+ /**
1369
+ * One file the user attached for the model to read, rather than to look at.
1370
+ *
1371
+ * `application/pdf` and `text/plain` are what the host stores, so those are what
1372
+ * arrive. Both hosted providers read a PDF natively and want it as its own content
1373
+ * block; plain text needs no such thing and can simply be put in the prompt, which
1374
+ * is why a provider with no document support at all can still do something useful
1375
+ * with a `text/plain` file if it chooses to.
1376
+ */
1377
+ interface ChatFile {
1378
+ /** `application/pdf` or `text/plain`. */
1379
+ mime: string;
1380
+ /** The file itself, base64 with no data-URI prefix. */
1381
+ data: string;
1382
+ /**
1383
+ * The name the file arrived under, when it had one.
1384
+ *
1385
+ * Worth passing on rather than dropping: `faktura-1042.pdf` is most of what is
1386
+ * known about a file before it is opened, and both providers have somewhere to
1387
+ * put it — a file name on the one, a document title on the other.
1388
+ */
1389
+ name?: string;
1390
+ }
1302
1391
  /**
1303
1392
  * A tool call made by the model
1304
1393
  */
@@ -1714,6 +1803,109 @@ interface ExecutionContext {
1714
1803
  readonly secrets: SecretsAPI;
1715
1804
  /** User-scoped secrets */
1716
1805
  readonly userSecrets: SecretsAPI;
1806
+ /**
1807
+ * The files attached to this user's conversations.
1808
+ *
1809
+ * Present only for an extension holding `attachments.read`, and only on a request
1810
+ * that knows whose it is. Absent otherwise, so a tool that wants files has to say
1811
+ * so in its manifest and check before reaching for them.
1812
+ */
1813
+ readonly attachments?: AttachmentsAPI;
1814
+ /**
1815
+ * The external accounts this user has connected to Stina.
1816
+ *
1817
+ * Present only for an extension holding `accounts.use`, and only on a request
1818
+ * that knows whose it is - an account belongs to somebody, like an attachment.
1819
+ */
1820
+ readonly accounts?: ConnectedAccountsAPI;
1821
+ }
1822
+ /**
1823
+ * Working through an account the user signed in to in Stina's settings.
1824
+ *
1825
+ * The user connects a Microsoft account once, and every extension that declares
1826
+ * Microsoft under `contributes.accounts` can use it. The extension never sees the
1827
+ * refresh token; it asks for an access token when it needs one and gets a fresh
1828
+ * one back, refreshed by the host when the old one has run out.
1829
+ *
1830
+ * One thing the host cannot narrow: Microsoft issues a Graph token carrying every
1831
+ * permission the user has granted Stina, not only the ones this extension
1832
+ * declared. The declaration decides whether an extension gets a token at all,
1833
+ * and what the user is asked to approve - not what the token can do.
1834
+ */
1835
+ interface ConnectedAccountsAPI {
1836
+ /**
1837
+ * The accounts this user has connected for a provider, newest first.
1838
+ *
1839
+ * An empty list is the normal answer for a user who has not connected one yet:
1840
+ * the extension should point them at Stina's settings rather than start a
1841
+ * sign-in of its own.
1842
+ */
1843
+ list(provider: 'microsoft'): Promise<ConnectedAccount[]>;
1844
+ /**
1845
+ * An access token for one of those accounts, for the scopes this extension
1846
+ * declared.
1847
+ *
1848
+ * Rejects when the account is gone, when the user has not granted every
1849
+ * declared scope yet, or when the provider has revoked the sign-in. The
1850
+ * message says which, in words the user can act on - it is fine to show it.
1851
+ */
1852
+ getAccessToken(accountId: string): Promise<AccountAccessToken>;
1853
+ }
1854
+ /** An account the user has connected, as an extension sees it. */
1855
+ interface ConnectedAccount {
1856
+ /** Stable id, to store alongside whatever the extension keeps for this account. */
1857
+ id: string;
1858
+ provider: 'microsoft';
1859
+ /** The address the user signs in with. */
1860
+ email: string;
1861
+ displayName?: string;
1862
+ /**
1863
+ * Whether the user has granted every scope this extension declared. False when
1864
+ * the extension was installed after the account was connected, and the user has
1865
+ * yet to reconnect it in the settings.
1866
+ */
1867
+ hasRequiredScopes: boolean;
1868
+ /** Set when the provider has refused the stored sign-in and it has to be redone. */
1869
+ needsReconnect: boolean;
1870
+ }
1871
+ /** An access token, and when it stops working. */
1872
+ interface AccountAccessToken {
1873
+ /** Bearer token for Microsoft Graph. */
1874
+ token: string;
1875
+ /** ISO timestamp. Ask again after this; the host refreshes. */
1876
+ expiresAt: string;
1877
+ }
1878
+ /**
1879
+ * Reading a file that is already in a conversation.
1880
+ *
1881
+ * For handing one on: mailing back the PDF she was just shown, printing it, putting
1882
+ * it somewhere. Not for finding out what it says — `core_read_attachment` does that
1883
+ * without an extension, and getting the text is nearly always what she actually
1884
+ * wants.
1885
+ */
1886
+ interface AttachmentsAPI {
1887
+ /**
1888
+ * Read one attachment by id.
1889
+ *
1890
+ * The id comes from Stina, which is the whole point: she gets it from the message
1891
+ * a file arrived on, or from the result of the tool that handed it over, and
1892
+ * passes it to a tool as a parameter.
1893
+ *
1894
+ * Resolves `null` when no such attachment belongs to this user — deleted, or
1895
+ * never theirs. The two are the same answer on purpose.
1896
+ */
1897
+ read(attachmentId: string): Promise<AttachmentContent | null>;
1898
+ }
1899
+ /** One attachment's bytes, with what is known about them. */
1900
+ interface AttachmentContent {
1901
+ id: string;
1902
+ /** `image/jpeg`, `image/png`, `application/pdf` or `text/plain`, read from the bytes. */
1903
+ mime: string;
1904
+ /** base64, no data-URI prefix. */
1905
+ data: string;
1906
+ byteSize: number;
1907
+ /** The name it was stored under, when it had one. */
1908
+ name?: string;
1717
1909
  }
1718
1910
  /**
1719
1911
  * Context provided to extension's activate function.
@@ -2353,30 +2545,44 @@ interface ToolResult {
2353
2545
  */
2354
2546
  cardSuggestion?: string;
2355
2547
  /**
2356
- * Pictures to show the user, rendered in the conversation where the tool ran.
2548
+ * Files to put in front of the user, in the conversation where the tool ran.
2357
2549
  *
2358
- * For what a card cannot hold and the model cannot reproduce: a generated
2359
- * image, a rendered document, a photo fetched on the user's behalf. Like
2360
- * {@link ToolResult.display}, this is for the user only — it is lifted out
2361
- * before the result reaches the model, which would have nothing to do with
2362
- * the bytes but spend tokens on them. Say in `data` that a picture was shown,
2363
- * so she can talk about it without describing it back.
2550
+ * For what a card cannot hold and the model cannot reproduce: a generated image,
2551
+ * a photo fetched on the user's behalf, the PDF that came attached to a mail.
2552
+ * Like {@link ToolResult.display}, this is for the user — it is lifted out before
2553
+ * the result reaches the model, which would have nothing to do with the bytes but
2554
+ * spend tokens on them. Say in `data` that a file was attached, so she can talk
2555
+ * about it without reciting it.
2364
2556
  *
2365
- * The host stores each one as an attachment of the conversation, the same way
2366
- * a picture the user sends is stored, so it is served to every client and can
2367
- * be saved or shared from there. JPEG and PNG only, and 20 MB at most, since
2368
- * those are the limits the attachment store already holds users to. One that
2369
- * fails them is dropped with a warning rather than half-shown.
2557
+ * The host stores each one as an attachment of the conversation, exactly as a
2558
+ * file the user sends is stored, so it is served to every client and can be saved
2559
+ * or shared from there. The store's own rules apply: JPEG and PNG, PDF, and plain
2560
+ * text, up to 20 MB (1 MB for text). What the file *is* is read from the bytes,
2561
+ * not from `name`, so a PDF mislabelled `.png` still lands as a PDF. One that
2562
+ * fails the rules is dropped with a warning rather than half-shown.
2563
+ *
2564
+ * Attaching a file does not read it to the model. What comes back in the result
2565
+ * the model sees is a reference apiece — `{ id, mime, name? }` under this same
2566
+ * key, in place of the bytes — and she reads one with `core_read_attachment` if
2567
+ * she decides to. That is the intended flow for a mail's attachment: hand it over
2568
+ * here, and let her choose whether to open it.
2370
2569
  */
2371
2570
  attachments?: ToolAttachment[];
2372
2571
  }
2373
2572
  /**
2374
- * One picture a tool wants shown. See {@link ToolResult.attachments}.
2573
+ * One file a tool wants to put in the conversation. See {@link ToolResult.attachments}.
2375
2574
  */
2376
2575
  interface ToolAttachment {
2377
- /** The image bytes, base64 encoded. The format is read from the bytes. */
2576
+ /** The bytes, base64 encoded. What they are is read from them, not from `name`. */
2378
2577
  data: string;
2379
- /** A file name to offer when the user saves it, e.g. `friday.png`. */
2578
+ /**
2579
+ * A file name to offer when the user saves it, e.g. `friday.png` or
2580
+ * `faktura-1042.pdf`.
2581
+ *
2582
+ * Worth sending for a picture and close to required for a document: it is the
2583
+ * whole label the user sees in the conversation, and it is what Stina has to go
2584
+ * on when she decides whether to read it.
2585
+ */
2380
2586
  name?: string;
2381
2587
  }
2382
2588
  /**
@@ -2405,4 +2611,4 @@ interface ActionResult {
2405
2611
  error?: string;
2406
2612
  }
2407
2613
 
2408
- export { type BackgroundTaskContext as $, type ActionResult as A, type ActionsAPI as B, type ChatMessage as C, type Disposable as D, type ExtensionContributions as E, type EventsAPI as F, type GetModelsOptions as G, type HugeIconName as H, type SchedulerAPI as I, type SchedulerJobRequest as J, type SchedulerSchedule as K, type LocalizedString as L, type ModelInfo as M, type NetworkAPI as N, type UserProfile as O, type PanelDefinition as P, type ChatAPI as Q, type ChatInstructionMessage as R, type SchedulerFirePayload as S, type ToolResult as T, type UserAPI as U, type VoiceSessionOptions as V, type ConversationPresentation as W, type LogAPI as X, type BackgroundWorkersAPI as Y, type BackgroundTaskConfig as Z, type BackgroundTaskCallback as _, type ChatOptions as a, type WeatherForecastProps as a$, type BackgroundTaskHealth as a0, type BackgroundRestartPolicy as a1, type Query as a2, type QueryOptions as a3, type StorageAPI as a4, type SecretsAPI as a5, type StorageCollectionConfig as a6, type StorageContributions as a7, type AIProvider as a8, type ModelCapabilities as a9, type SelectProps as aA, type IconPickerProps as aB, type VerticalStackProps as aC, type HorizontalStackProps as aD, type GridProps as aE, type DividerProps as aF, type IconProps as aG, type IconButtonType as aH, type IconButtonProps as aI, type PanelAction as aJ, type PanelProps as aK, type ToggleProps as aL, type CollapsibleProps as aM, type FrameVariant as aN, type FrameProps as aO, type ListProps as aP, type PillVariant as aQ, type PillProps as aR, type CheckboxProps as aS, type MarkdownProps as aT, type TextPreviewProps as aU, type ModalProps as aV, type ConditionalGroupProps as aW, type WeatherCondition as aX, type WeatherWind as aY, type WeatherNowProps as aZ, type WeatherForecastStep as a_, type ChatImage as aa, type ToolCall as ab, type VoiceTransportRequest as ac, type Tool as ad, type ToolAttachment as ae, type Action as af, type ExtensionModule as ag, type AllowedCSSProperty as ah, type ExtensionComponentStyle as ai, type ExtensionComponentData as aj, type ExtensionComponentIterator as ak, type ExtensionComponentChildren as al, type ExtensionActionCall as am, type ExtensionActionRef as an, type ExtensionDataSource as ao, type ExtensionPanelDefinition as ap, type HeaderProps as aq, type LabelProps as ar, type ClockProps as as, type ParagraphProps as at, type ButtonProps as au, type TextInputProps as av, type PasswordInputProps as aw, type NumberInputProps as ax, type TextAreaProps as ay, type DateTimeInputProps as az, type StreamEvent as b, type ChartKind as b0, type ChartSeries as b1, type ChartProps as b2, type StatTrend as b3, type StatTileProps as b4, type ProgressShape as b5, type ProgressColor as b6, type ProgressBarProps as b7, type KeyValueRow as b8, type KeyValueListProps as b9, type TimelineVariant as ba, type TimelineEntry as bb, type TimelineProps as bc, type NoteVariant as bd, type NoteProps as be, type CalendarEventStatus as bf, type CalendarEventProps as bg, type ExecutionContext as bh, type VoiceSessionDescriptor as c, type ToolSettingsViewDefinition as d, type ToolSettingsView as e, type ToolSettingsListView as f, type ToolSettingsListMapping as g, type ToolSettingsComponentView as h, type ToolSettingsActionDataSource as i, type StatusCardDefinition as j, type PanelView as k, type PanelComponentView as l, type PanelActionDataSource as m, type PanelUnknownView as n, type ProviderDefinition as o, type ProviderConfigView as p, type PromptContribution as q, resolveLocalizedString as r, type PromptSection as s, type ToolDefinition as t, type ToolConfirmationConfig as u, type CommandDefinition as v, type ExtensionContext as w, type SettingsAPI as x, type ProvidersAPI as y, type ToolsAPI as z };
2614
+ export { type ConnectedAccountsAPI as $, type ActionResult as A, type ProvidersAPI as B, type ChatMessage as C, type Disposable as D, type ExtensionContributions as E, type ToolsAPI as F, type GetModelsOptions as G, type HugeIconName as H, type ActionsAPI as I, type EventsAPI as J, type SchedulerAPI as K, type LocalizedString as L, type ModelInfo as M, type NetworkAPI as N, type SchedulerJobRequest as O, type PanelDefinition as P, type SchedulerSchedule as Q, type UserProfile as R, type SchedulerFirePayload as S, type ToolResult as T, type UserAPI as U, type VoiceSessionOptions as V, type ChatAPI as W, type ChatInstructionMessage as X, type ConversationPresentation as Y, type AttachmentsAPI as Z, type AttachmentContent as _, type ChatOptions as a, type MarkdownProps as a$, type ConnectedAccount as a0, type AccountAccessToken as a1, type LogAPI as a2, type BackgroundWorkersAPI as a3, type BackgroundTaskConfig as a4, type BackgroundTaskCallback as a5, type BackgroundTaskContext as a6, type BackgroundTaskHealth as a7, type BackgroundRestartPolicy as a8, type Query as a9, type ClockProps as aA, type ParagraphProps as aB, type ButtonProps as aC, type TextInputProps as aD, type PasswordInputProps as aE, type NumberInputProps as aF, type TextAreaProps as aG, type DateTimeInputProps as aH, type SelectProps as aI, type IconPickerProps as aJ, type VerticalStackProps as aK, type HorizontalStackProps as aL, type GridProps as aM, type DividerProps as aN, type IconProps as aO, type IconButtonType as aP, type IconButtonProps as aQ, type PanelAction as aR, type PanelProps as aS, type ToggleProps as aT, type CollapsibleProps as aU, type FrameVariant as aV, type FrameProps as aW, type ListProps as aX, type PillVariant as aY, type PillProps as aZ, type CheckboxProps as a_, type QueryOptions as aa, type StorageAPI as ab, type SecretsAPI as ac, type StorageCollectionConfig as ad, type StorageContributions as ae, type AIProvider as af, type ModelCapabilities as ag, type ChatImage as ah, type ChatFile as ai, type ToolCall as aj, type VoiceTransportRequest as ak, type Tool as al, type ToolAttachment as am, type Action as an, type ExtensionModule as ao, type AllowedCSSProperty as ap, type ExtensionComponentStyle as aq, type ExtensionComponentData as ar, type ExtensionComponentIterator as as, type ExtensionComponentChildren as at, type ExtensionActionCall as au, type ExtensionActionRef as av, type ExtensionDataSource as aw, type ExtensionPanelDefinition as ax, type HeaderProps as ay, type LabelProps as az, type StreamEvent as b, type TextPreviewProps as b0, type ModalProps as b1, type ConditionalGroupProps as b2, type WeatherCondition as b3, type WeatherWind as b4, type WeatherNowProps as b5, type WeatherForecastStep as b6, type WeatherForecastProps as b7, type ChartKind as b8, type ChartSeries as b9, type ChartProps as ba, type StatTrend as bb, type StatTileProps as bc, type ProgressShape as bd, type ProgressColor as be, type ProgressBarProps as bf, type KeyValueRow as bg, type KeyValueListProps as bh, type TimelineVariant as bi, type TimelineEntry as bj, type TimelineProps as bk, type NoteVariant as bl, type NoteProps as bm, type CalendarEventStatus as bn, type CalendarEventProps as bo, type ExecutionContext as bp, type VoiceSessionDescriptor as c, type ToolSettingsViewDefinition as d, type ToolSettingsView as e, type ToolSettingsListView as f, type ToolSettingsListMapping as g, type ToolSettingsComponentView as h, type ToolSettingsActionDataSource as i, type StatusCardDefinition as j, type PanelView as k, type PanelComponentView as l, type PanelActionDataSource as m, type PanelUnknownView as n, type ProviderDefinition as o, type ProviderConfigView as p, type PromptContribution as q, resolveLocalizedString as r, type PromptSection as s, type ToolDefinition as t, type ToolConfirmationConfig as u, type CommandDefinition as v, type AccountProvider as w, type AccountContribution as x, type ExtensionContext as y, type SettingsAPI as z };
@@ -789,6 +789,8 @@ interface ExtensionContributions {
789
789
  commands?: CommandDefinition[];
790
790
  /** Prompt contributions for the system prompt */
791
791
  prompts?: PromptContribution[];
792
+ /** External accounts the extension works through, signed in once in Stina's settings */
793
+ accounts?: AccountContribution[];
792
794
  /** Storage collection declarations */
793
795
  storage?: {
794
796
  collections: {
@@ -799,6 +801,45 @@ interface ExtensionContributions {
799
801
  };
800
802
  };
801
803
  }
804
+ /**
805
+ * An external account provider the host can sign in to on the user's behalf.
806
+ *
807
+ * Only Microsoft for now, and only through Microsoft Graph: mail and calendar
808
+ * then share one sign-in and one token, instead of an IMAP token and a Graph
809
+ * token that an admin has to approve separately.
810
+ */
811
+ type AccountProvider = 'microsoft';
812
+ /**
813
+ * What an extension needs from an account the user connects to Stina.
814
+ *
815
+ * Stina asks the provider for every scope that the enabled extensions declare,
816
+ * all at once, so that the user - and, at work, their administrator - approves
817
+ * one request instead of one per extension. Declaring a scope here is therefore
818
+ * also what makes it show up in that request, and in the list of what is asked
819
+ * for in the settings.
820
+ *
821
+ * @example
822
+ * ```json
823
+ * "accounts": [
824
+ * {
825
+ * "provider": "microsoft",
826
+ * "scopes": ["Calendars.ReadWrite"],
827
+ * "reason": { "en": "Read and book events in your calendar", "sv": "Läsa och boka i din kalender" }
828
+ * }
829
+ * ]
830
+ * ```
831
+ */
832
+ interface AccountContribution {
833
+ provider: AccountProvider;
834
+ /**
835
+ * Microsoft Graph delegated permissions, by name: `Mail.Read`,
836
+ * `Calendars.ReadWrite`. The host adds `offline_access` and `User.Read`
837
+ * itself, so they are not declared here.
838
+ */
839
+ scopes: string[];
840
+ /** Why the extension needs them, shown next to the scopes in the settings. */
841
+ reason?: LocalizedString;
842
+ }
802
843
  /**
803
844
  * Tool settings view definition (UI schema)
804
845
  */
@@ -1178,9 +1219,22 @@ interface ModelCapabilities {
1178
1219
  * Report this per model *and* per auth mode, like `voiceDuplex`: the same
1179
1220
  * provider often serves both a vision model and a text-only one, and a picture
1180
1221
  * sent to the latter is at best ignored and at worst an error mid-conversation.
1181
- * Stina uses it to decide whether the paperclip is offered at all.
1182
1222
  */
1183
1223
  vision?: boolean;
1224
+ /**
1225
+ * The model can be given files to read alongside the text of a message, and
1226
+ * the provider folds {@link ChatMessage.files} into whatever its API calls them.
1227
+ *
1228
+ * Separate from `vision` because the two come apart in both directions: a model
1229
+ * that reads a PDF natively is not necessarily one that looks at a photograph,
1230
+ * and an OpenAI-compatible server with a vision model behind it may take images
1231
+ * and nothing else. Report it per model and per auth mode for the same reason
1232
+ * `vision` is reported that way.
1233
+ *
1234
+ * A provider that says nothing here keeps behaving exactly as before: it is
1235
+ * handed the text, and `files` is simply a field it does not read.
1236
+ */
1237
+ documents?: boolean;
1184
1238
  }
1185
1239
  /**
1186
1240
  * How the client wants to connect to the voice session.
@@ -1280,6 +1334,18 @@ interface ChatMessage {
1280
1334
  * Only ever `image/jpeg` or `image/png` — see `ChatAttachmentDTO` for why.
1281
1335
  */
1282
1336
  images?: ChatImage[];
1337
+ /**
1338
+ * Files the user attached for the model to read: PDFs and plain text.
1339
+ *
1340
+ * Additive in the same way `images` is, and split from it for the same reason
1341
+ * the two capabilities are separate — a provider folds a document into a
1342
+ * different content block than a picture, and many can do one and not the other.
1343
+ * A provider that ignores the field behaves exactly as it did before.
1344
+ *
1345
+ * Only present on user messages, because that is where both hosted providers
1346
+ * require a document to sit.
1347
+ */
1348
+ files?: ChatFile[];
1283
1349
  /** For assistant messages: tool calls made by the model */
1284
1350
  tool_calls?: ToolCall[];
1285
1351
  /** For tool messages: the ID of the tool call this is a response to */
@@ -1299,6 +1365,29 @@ interface ChatImage {
1299
1365
  /** The image itself, base64 with no data-URI prefix. */
1300
1366
  data: string;
1301
1367
  }
1368
+ /**
1369
+ * One file the user attached for the model to read, rather than to look at.
1370
+ *
1371
+ * `application/pdf` and `text/plain` are what the host stores, so those are what
1372
+ * arrive. Both hosted providers read a PDF natively and want it as its own content
1373
+ * block; plain text needs no such thing and can simply be put in the prompt, which
1374
+ * is why a provider with no document support at all can still do something useful
1375
+ * with a `text/plain` file if it chooses to.
1376
+ */
1377
+ interface ChatFile {
1378
+ /** `application/pdf` or `text/plain`. */
1379
+ mime: string;
1380
+ /** The file itself, base64 with no data-URI prefix. */
1381
+ data: string;
1382
+ /**
1383
+ * The name the file arrived under, when it had one.
1384
+ *
1385
+ * Worth passing on rather than dropping: `faktura-1042.pdf` is most of what is
1386
+ * known about a file before it is opened, and both providers have somewhere to
1387
+ * put it — a file name on the one, a document title on the other.
1388
+ */
1389
+ name?: string;
1390
+ }
1302
1391
  /**
1303
1392
  * A tool call made by the model
1304
1393
  */
@@ -1714,6 +1803,109 @@ interface ExecutionContext {
1714
1803
  readonly secrets: SecretsAPI;
1715
1804
  /** User-scoped secrets */
1716
1805
  readonly userSecrets: SecretsAPI;
1806
+ /**
1807
+ * The files attached to this user's conversations.
1808
+ *
1809
+ * Present only for an extension holding `attachments.read`, and only on a request
1810
+ * that knows whose it is. Absent otherwise, so a tool that wants files has to say
1811
+ * so in its manifest and check before reaching for them.
1812
+ */
1813
+ readonly attachments?: AttachmentsAPI;
1814
+ /**
1815
+ * The external accounts this user has connected to Stina.
1816
+ *
1817
+ * Present only for an extension holding `accounts.use`, and only on a request
1818
+ * that knows whose it is - an account belongs to somebody, like an attachment.
1819
+ */
1820
+ readonly accounts?: ConnectedAccountsAPI;
1821
+ }
1822
+ /**
1823
+ * Working through an account the user signed in to in Stina's settings.
1824
+ *
1825
+ * The user connects a Microsoft account once, and every extension that declares
1826
+ * Microsoft under `contributes.accounts` can use it. The extension never sees the
1827
+ * refresh token; it asks for an access token when it needs one and gets a fresh
1828
+ * one back, refreshed by the host when the old one has run out.
1829
+ *
1830
+ * One thing the host cannot narrow: Microsoft issues a Graph token carrying every
1831
+ * permission the user has granted Stina, not only the ones this extension
1832
+ * declared. The declaration decides whether an extension gets a token at all,
1833
+ * and what the user is asked to approve - not what the token can do.
1834
+ */
1835
+ interface ConnectedAccountsAPI {
1836
+ /**
1837
+ * The accounts this user has connected for a provider, newest first.
1838
+ *
1839
+ * An empty list is the normal answer for a user who has not connected one yet:
1840
+ * the extension should point them at Stina's settings rather than start a
1841
+ * sign-in of its own.
1842
+ */
1843
+ list(provider: 'microsoft'): Promise<ConnectedAccount[]>;
1844
+ /**
1845
+ * An access token for one of those accounts, for the scopes this extension
1846
+ * declared.
1847
+ *
1848
+ * Rejects when the account is gone, when the user has not granted every
1849
+ * declared scope yet, or when the provider has revoked the sign-in. The
1850
+ * message says which, in words the user can act on - it is fine to show it.
1851
+ */
1852
+ getAccessToken(accountId: string): Promise<AccountAccessToken>;
1853
+ }
1854
+ /** An account the user has connected, as an extension sees it. */
1855
+ interface ConnectedAccount {
1856
+ /** Stable id, to store alongside whatever the extension keeps for this account. */
1857
+ id: string;
1858
+ provider: 'microsoft';
1859
+ /** The address the user signs in with. */
1860
+ email: string;
1861
+ displayName?: string;
1862
+ /**
1863
+ * Whether the user has granted every scope this extension declared. False when
1864
+ * the extension was installed after the account was connected, and the user has
1865
+ * yet to reconnect it in the settings.
1866
+ */
1867
+ hasRequiredScopes: boolean;
1868
+ /** Set when the provider has refused the stored sign-in and it has to be redone. */
1869
+ needsReconnect: boolean;
1870
+ }
1871
+ /** An access token, and when it stops working. */
1872
+ interface AccountAccessToken {
1873
+ /** Bearer token for Microsoft Graph. */
1874
+ token: string;
1875
+ /** ISO timestamp. Ask again after this; the host refreshes. */
1876
+ expiresAt: string;
1877
+ }
1878
+ /**
1879
+ * Reading a file that is already in a conversation.
1880
+ *
1881
+ * For handing one on: mailing back the PDF she was just shown, printing it, putting
1882
+ * it somewhere. Not for finding out what it says — `core_read_attachment` does that
1883
+ * without an extension, and getting the text is nearly always what she actually
1884
+ * wants.
1885
+ */
1886
+ interface AttachmentsAPI {
1887
+ /**
1888
+ * Read one attachment by id.
1889
+ *
1890
+ * The id comes from Stina, which is the whole point: she gets it from the message
1891
+ * a file arrived on, or from the result of the tool that handed it over, and
1892
+ * passes it to a tool as a parameter.
1893
+ *
1894
+ * Resolves `null` when no such attachment belongs to this user — deleted, or
1895
+ * never theirs. The two are the same answer on purpose.
1896
+ */
1897
+ read(attachmentId: string): Promise<AttachmentContent | null>;
1898
+ }
1899
+ /** One attachment's bytes, with what is known about them. */
1900
+ interface AttachmentContent {
1901
+ id: string;
1902
+ /** `image/jpeg`, `image/png`, `application/pdf` or `text/plain`, read from the bytes. */
1903
+ mime: string;
1904
+ /** base64, no data-URI prefix. */
1905
+ data: string;
1906
+ byteSize: number;
1907
+ /** The name it was stored under, when it had one. */
1908
+ name?: string;
1717
1909
  }
1718
1910
  /**
1719
1911
  * Context provided to extension's activate function.
@@ -2353,30 +2545,44 @@ interface ToolResult {
2353
2545
  */
2354
2546
  cardSuggestion?: string;
2355
2547
  /**
2356
- * Pictures to show the user, rendered in the conversation where the tool ran.
2548
+ * Files to put in front of the user, in the conversation where the tool ran.
2357
2549
  *
2358
- * For what a card cannot hold and the model cannot reproduce: a generated
2359
- * image, a rendered document, a photo fetched on the user's behalf. Like
2360
- * {@link ToolResult.display}, this is for the user only — it is lifted out
2361
- * before the result reaches the model, which would have nothing to do with
2362
- * the bytes but spend tokens on them. Say in `data` that a picture was shown,
2363
- * so she can talk about it without describing it back.
2550
+ * For what a card cannot hold and the model cannot reproduce: a generated image,
2551
+ * a photo fetched on the user's behalf, the PDF that came attached to a mail.
2552
+ * Like {@link ToolResult.display}, this is for the user — it is lifted out before
2553
+ * the result reaches the model, which would have nothing to do with the bytes but
2554
+ * spend tokens on them. Say in `data` that a file was attached, so she can talk
2555
+ * about it without reciting it.
2364
2556
  *
2365
- * The host stores each one as an attachment of the conversation, the same way
2366
- * a picture the user sends is stored, so it is served to every client and can
2367
- * be saved or shared from there. JPEG and PNG only, and 20 MB at most, since
2368
- * those are the limits the attachment store already holds users to. One that
2369
- * fails them is dropped with a warning rather than half-shown.
2557
+ * The host stores each one as an attachment of the conversation, exactly as a
2558
+ * file the user sends is stored, so it is served to every client and can be saved
2559
+ * or shared from there. The store's own rules apply: JPEG and PNG, PDF, and plain
2560
+ * text, up to 20 MB (1 MB for text). What the file *is* is read from the bytes,
2561
+ * not from `name`, so a PDF mislabelled `.png` still lands as a PDF. One that
2562
+ * fails the rules is dropped with a warning rather than half-shown.
2563
+ *
2564
+ * Attaching a file does not read it to the model. What comes back in the result
2565
+ * the model sees is a reference apiece — `{ id, mime, name? }` under this same
2566
+ * key, in place of the bytes — and she reads one with `core_read_attachment` if
2567
+ * she decides to. That is the intended flow for a mail's attachment: hand it over
2568
+ * here, and let her choose whether to open it.
2370
2569
  */
2371
2570
  attachments?: ToolAttachment[];
2372
2571
  }
2373
2572
  /**
2374
- * One picture a tool wants shown. See {@link ToolResult.attachments}.
2573
+ * One file a tool wants to put in the conversation. See {@link ToolResult.attachments}.
2375
2574
  */
2376
2575
  interface ToolAttachment {
2377
- /** The image bytes, base64 encoded. The format is read from the bytes. */
2576
+ /** The bytes, base64 encoded. What they are is read from them, not from `name`. */
2378
2577
  data: string;
2379
- /** A file name to offer when the user saves it, e.g. `friday.png`. */
2578
+ /**
2579
+ * A file name to offer when the user saves it, e.g. `friday.png` or
2580
+ * `faktura-1042.pdf`.
2581
+ *
2582
+ * Worth sending for a picture and close to required for a document: it is the
2583
+ * whole label the user sees in the conversation, and it is what Stina has to go
2584
+ * on when she decides whether to read it.
2585
+ */
2380
2586
  name?: string;
2381
2587
  }
2382
2588
  /**
@@ -2405,4 +2611,4 @@ interface ActionResult {
2405
2611
  error?: string;
2406
2612
  }
2407
2613
 
2408
- export { type BackgroundTaskContext as $, type ActionResult as A, type ActionsAPI as B, type ChatMessage as C, type Disposable as D, type ExtensionContributions as E, type EventsAPI as F, type GetModelsOptions as G, type HugeIconName as H, type SchedulerAPI as I, type SchedulerJobRequest as J, type SchedulerSchedule as K, type LocalizedString as L, type ModelInfo as M, type NetworkAPI as N, type UserProfile as O, type PanelDefinition as P, type ChatAPI as Q, type ChatInstructionMessage as R, type SchedulerFirePayload as S, type ToolResult as T, type UserAPI as U, type VoiceSessionOptions as V, type ConversationPresentation as W, type LogAPI as X, type BackgroundWorkersAPI as Y, type BackgroundTaskConfig as Z, type BackgroundTaskCallback as _, type ChatOptions as a, type WeatherForecastProps as a$, type BackgroundTaskHealth as a0, type BackgroundRestartPolicy as a1, type Query as a2, type QueryOptions as a3, type StorageAPI as a4, type SecretsAPI as a5, type StorageCollectionConfig as a6, type StorageContributions as a7, type AIProvider as a8, type ModelCapabilities as a9, type SelectProps as aA, type IconPickerProps as aB, type VerticalStackProps as aC, type HorizontalStackProps as aD, type GridProps as aE, type DividerProps as aF, type IconProps as aG, type IconButtonType as aH, type IconButtonProps as aI, type PanelAction as aJ, type PanelProps as aK, type ToggleProps as aL, type CollapsibleProps as aM, type FrameVariant as aN, type FrameProps as aO, type ListProps as aP, type PillVariant as aQ, type PillProps as aR, type CheckboxProps as aS, type MarkdownProps as aT, type TextPreviewProps as aU, type ModalProps as aV, type ConditionalGroupProps as aW, type WeatherCondition as aX, type WeatherWind as aY, type WeatherNowProps as aZ, type WeatherForecastStep as a_, type ChatImage as aa, type ToolCall as ab, type VoiceTransportRequest as ac, type Tool as ad, type ToolAttachment as ae, type Action as af, type ExtensionModule as ag, type AllowedCSSProperty as ah, type ExtensionComponentStyle as ai, type ExtensionComponentData as aj, type ExtensionComponentIterator as ak, type ExtensionComponentChildren as al, type ExtensionActionCall as am, type ExtensionActionRef as an, type ExtensionDataSource as ao, type ExtensionPanelDefinition as ap, type HeaderProps as aq, type LabelProps as ar, type ClockProps as as, type ParagraphProps as at, type ButtonProps as au, type TextInputProps as av, type PasswordInputProps as aw, type NumberInputProps as ax, type TextAreaProps as ay, type DateTimeInputProps as az, type StreamEvent as b, type ChartKind as b0, type ChartSeries as b1, type ChartProps as b2, type StatTrend as b3, type StatTileProps as b4, type ProgressShape as b5, type ProgressColor as b6, type ProgressBarProps as b7, type KeyValueRow as b8, type KeyValueListProps as b9, type TimelineVariant as ba, type TimelineEntry as bb, type TimelineProps as bc, type NoteVariant as bd, type NoteProps as be, type CalendarEventStatus as bf, type CalendarEventProps as bg, type ExecutionContext as bh, type VoiceSessionDescriptor as c, type ToolSettingsViewDefinition as d, type ToolSettingsView as e, type ToolSettingsListView as f, type ToolSettingsListMapping as g, type ToolSettingsComponentView as h, type ToolSettingsActionDataSource as i, type StatusCardDefinition as j, type PanelView as k, type PanelComponentView as l, type PanelActionDataSource as m, type PanelUnknownView as n, type ProviderDefinition as o, type ProviderConfigView as p, type PromptContribution as q, resolveLocalizedString as r, type PromptSection as s, type ToolDefinition as t, type ToolConfirmationConfig as u, type CommandDefinition as v, type ExtensionContext as w, type SettingsAPI as x, type ProvidersAPI as y, type ToolsAPI as z };
2614
+ export { type ConnectedAccountsAPI as $, type ActionResult as A, type ProvidersAPI as B, type ChatMessage as C, type Disposable as D, type ExtensionContributions as E, type ToolsAPI as F, type GetModelsOptions as G, type HugeIconName as H, type ActionsAPI as I, type EventsAPI as J, type SchedulerAPI as K, type LocalizedString as L, type ModelInfo as M, type NetworkAPI as N, type SchedulerJobRequest as O, type PanelDefinition as P, type SchedulerSchedule as Q, type UserProfile as R, type SchedulerFirePayload as S, type ToolResult as T, type UserAPI as U, type VoiceSessionOptions as V, type ChatAPI as W, type ChatInstructionMessage as X, type ConversationPresentation as Y, type AttachmentsAPI as Z, type AttachmentContent as _, type ChatOptions as a, type MarkdownProps as a$, type ConnectedAccount as a0, type AccountAccessToken as a1, type LogAPI as a2, type BackgroundWorkersAPI as a3, type BackgroundTaskConfig as a4, type BackgroundTaskCallback as a5, type BackgroundTaskContext as a6, type BackgroundTaskHealth as a7, type BackgroundRestartPolicy as a8, type Query as a9, type ClockProps as aA, type ParagraphProps as aB, type ButtonProps as aC, type TextInputProps as aD, type PasswordInputProps as aE, type NumberInputProps as aF, type TextAreaProps as aG, type DateTimeInputProps as aH, type SelectProps as aI, type IconPickerProps as aJ, type VerticalStackProps as aK, type HorizontalStackProps as aL, type GridProps as aM, type DividerProps as aN, type IconProps as aO, type IconButtonType as aP, type IconButtonProps as aQ, type PanelAction as aR, type PanelProps as aS, type ToggleProps as aT, type CollapsibleProps as aU, type FrameVariant as aV, type FrameProps as aW, type ListProps as aX, type PillVariant as aY, type PillProps as aZ, type CheckboxProps as a_, type QueryOptions as aa, type StorageAPI as ab, type SecretsAPI as ac, type StorageCollectionConfig as ad, type StorageContributions as ae, type AIProvider as af, type ModelCapabilities as ag, type ChatImage as ah, type ChatFile as ai, type ToolCall as aj, type VoiceTransportRequest as ak, type Tool as al, type ToolAttachment as am, type Action as an, type ExtensionModule as ao, type AllowedCSSProperty as ap, type ExtensionComponentStyle as aq, type ExtensionComponentData as ar, type ExtensionComponentIterator as as, type ExtensionComponentChildren as at, type ExtensionActionCall as au, type ExtensionActionRef as av, type ExtensionDataSource as aw, type ExtensionPanelDefinition as ax, type HeaderProps as ay, type LabelProps as az, type StreamEvent as b, type TextPreviewProps as b0, type ModalProps as b1, type ConditionalGroupProps as b2, type WeatherCondition as b3, type WeatherWind as b4, type WeatherNowProps as b5, type WeatherForecastStep as b6, type WeatherForecastProps as b7, type ChartKind as b8, type ChartSeries as b9, type ChartProps as ba, type StatTrend as bb, type StatTileProps as bc, type ProgressShape as bd, type ProgressColor as be, type ProgressBarProps as bf, type KeyValueRow as bg, type KeyValueListProps as bh, type TimelineVariant as bi, type TimelineEntry as bj, type TimelineProps as bk, type NoteVariant as bl, type NoteProps as bm, type CalendarEventStatus as bn, type CalendarEventProps as bo, type ExecutionContext as bp, type VoiceSessionDescriptor as c, type ToolSettingsViewDefinition as d, type ToolSettingsView as e, type ToolSettingsListView as f, type ToolSettingsListMapping as g, type ToolSettingsComponentView as h, type ToolSettingsActionDataSource as i, type StatusCardDefinition as j, type PanelView as k, type PanelComponentView as l, type PanelActionDataSource as m, type PanelUnknownView as n, type ProviderDefinition as o, type ProviderConfigView as p, type PromptContribution as q, resolveLocalizedString as r, type PromptSection as s, type ToolDefinition as t, type ToolConfirmationConfig as u, type CommandDefinition as v, type AccountProvider as w, type AccountContribution as x, type ExtensionContext as y, type SettingsAPI as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stina/extension-api",
3
- "version": "1.3.1",
3
+ "version": "1.7.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",