@yaklabs-ai/plugin-sdk 0.11.0 → 0.12.2

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.
@@ -1,4 +1,3 @@
1
- import type { TypeSafeClient } from '@typesafe-ai/sdk';
2
1
  import { z } from 'zod';
3
2
 
4
3
  /**
@@ -23,6 +22,9 @@ declare interface AccountIdentity {
23
22
  readonly avatarUrl?: string;
24
23
  /** Issuer-asserted beta-tester flag; only Kay's product-account service sets it. */
25
24
  readonly betaTester?: boolean;
25
+ readonly activeOrganizationId?: string;
26
+ readonly organizationSelectionState?: "selected" | "unresolved";
27
+ readonly organizationScopeEpoch?: number;
26
28
  /**
27
29
  * Provider-native session id claim (WorkOS's `sid`), needed to call the provider's own
28
30
  * session-revocation endpoint at sign-out (`POST /user_management/sessions/revoke`, not yet
@@ -54,8 +56,18 @@ declare interface AgentToolResult<T = JsonValue_3 | undefined> {
54
56
  content: (TextContent | ImageContent)[];
55
57
  /** Arbitrary structured details for logs or UI rendering. */
56
58
  details: T;
59
+ /**
60
+ * Machine-readable result matching the tool's `outputSchema`, for programmatic callers. Not sent
61
+ * to the model; `content` remains the model-facing result.
62
+ */
63
+ structuredContent?: JsonValue_3;
57
64
  /** Usage from the final tool execution itself, if available. Not used for main LLM context accounting. */
58
65
  usage?: Usage;
66
+ /**
67
+ * Report a failure without throwing. The model sees `content` as an error result, like a thrown
68
+ * error, but `details` and `structuredContent` are kept for the UI and programmatic callers.
69
+ */
70
+ isError?: boolean;
59
71
  /**
60
72
  * Hint that the agent should stop after the current tool batch.
61
73
  * Early termination only happens when every finalized tool result in the batch sets this to true.
@@ -291,6 +303,20 @@ declare interface BackgroundTaskSnapshot {
291
303
 
292
304
  declare type BackgroundTaskStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
293
305
 
306
+ /** Fields shared by every catalog entry, regardless of what you can do with it. */
307
+ declare interface BaseModel<TApi extends string> {
308
+ id: string;
309
+ name: string;
310
+ api: TApi;
311
+ provider: ProviderId;
312
+ baseUrl: string;
313
+ input: ("text" | "image")[];
314
+ /** Provider input limits and cache-safe preprocessing metadata. */
315
+ inputLimits?: ModelInputLimits;
316
+ cost: ModelCost;
317
+ headers?: Record<string, string>;
318
+ }
319
+
294
320
  /** Compatibility settings for Amazon Bedrock models. */
295
321
  declare interface BedrockCompat {
296
322
  /** Whether the model supports Bedrock strict tool schemas. Default: false. */
@@ -354,7 +380,7 @@ export declare function collectAuthDeclarationsFromKayAgentActivate(activate: Ka
354
380
  /**
355
381
  * Runs `activate` once against a fake, capture-only {@link KayAgentPluginApi} and returns
356
382
  * every `UriExtensionModule` it registered, in registration order. Every non-`uri.registerModule`
357
- * call (`registerTool`, `registerSearchProvider`, `registerConverter`, `auth.*`) is a silent
383
+ * call (`registerTool`, `registerSearchProvider`, `registerInteractionDriver`, `registerConverter`, `auth.*`) is a silent
358
384
  * no-op: those surfaces are irrelevant to disclosure, and a plugin that also calls them must not
359
385
  * throw or leak a side effect from this probe.
360
386
  */
@@ -486,6 +512,13 @@ declare interface DelegatedConnectContext {
486
512
  /** What a delegated scheme's `connect` resolves with: the material to vault + optional label/provenance. */
487
513
  declare interface DelegatedConnectResult {
488
514
  readonly material: DelegatedSecretMaterial;
515
+ /**
516
+ * A stable account id for this sign-in, used instead of a freshly minted one, so a reconnect
517
+ * overwrites the same record (and keeps it the default) rather than adding a sibling. 1-128
518
+ * characters: letters, digits, and `._:@+-`, starting with a letter or digit; any other value
519
+ * fails the connect. Absent mints a fresh id per connect.
520
+ */
521
+ readonly accountId?: string;
489
522
  /** Account display label; a caller-supplied `service_connect` label takes precedence. */
490
523
  readonly label?: string;
491
524
  /** Structured provenance the scheme derived during connect; persisted via the vault's write-path. */
@@ -628,6 +661,328 @@ declare interface ImageContent {
628
661
  mimeType: string;
629
662
  }
630
663
 
664
+ /**
665
+ * The value a Browser Interaction driver sets as `credentialGuard` to declare that every act runs
666
+ * the page-side credential guard from `@kay/interaction-page-scripts` (never type into or click a
667
+ * sign-in form). The engine refuses to register a browser driver without it.
668
+ */
669
+ declare const INTERACTION_CREDENTIAL_GUARD = "kay.credential-guard/v1";
670
+
671
+ declare interface InteractionActed {
672
+ /** The address of the target acted on, when it changed (an open landed in a new page). */
673
+ readonly target?: string;
674
+ readonly url: string;
675
+ readonly title: string;
676
+ /** What the action visibly caused: a dialog, an alert, a page opened beside it. */
677
+ readonly changes?: readonly string[];
678
+ readonly notes?: readonly string[];
679
+ /** Fresh browser sign-in state for the service the engine named in the open spec. */
680
+ readonly webSession?: {
681
+ readonly state: "signed-in" | "signed-out" | "unknown";
682
+ readonly checkedAt: number | null;
683
+ };
684
+ }
685
+
686
+ /**
687
+ * One action over a candidate from the latest observation of the same target. `fill` replaces the
688
+ * field's contents unless `append` is set, and presses Enter afterwards when `submit` is set.
689
+ */
690
+ declare type InteractionAction = {
691
+ readonly kind: "click";
692
+ readonly element: InteractionCandidate;
693
+ } | {
694
+ readonly kind: "fill";
695
+ readonly element: InteractionCandidate;
696
+ readonly text: string;
697
+ readonly submit?: boolean;
698
+ readonly append?: boolean;
699
+ } | {
700
+ readonly kind: "select";
701
+ readonly element: InteractionCandidate;
702
+ } | {
703
+ readonly kind: "activate";
704
+ readonly element: InteractionCandidate;
705
+ } | {
706
+ readonly kind: "press";
707
+ readonly element: InteractionCandidate;
708
+ }
709
+ /**
710
+ * Scroll by `delta` px (positive is down). With `element` (a `scroll` candidate that names a
711
+ * node), the scroll container that candidate scrolls; without, the whole page or window.
712
+ */
713
+ | {
714
+ readonly kind: "scroll";
715
+ readonly delta: number;
716
+ readonly element?: InteractionCandidate;
717
+ } | {
718
+ readonly kind: "wait";
719
+ };
720
+
721
+ /**
722
+ * One element (or control) an observation offers, with the kind of action it supports. `id` is
723
+ * the driver's per-observation id; the engine mints its own session-unique ids over it. Extra
724
+ * fields are the driver's native identity (a page node number, an accessibility token), returned
725
+ * to the driver untouched in {@link InteractionAction}.
726
+ */
727
+ declare interface InteractionCandidate {
728
+ readonly id: string;
729
+ readonly kind: InteractionCandidateKind;
730
+ readonly label: string;
731
+ readonly node?: number | undefined;
732
+ readonly role?: string | undefined;
733
+ readonly value?: string | undefined;
734
+ readonly current_value?: string | undefined;
735
+ readonly checked?: string | undefined;
736
+ readonly selected?: string | undefined;
737
+ readonly expanded?: string | undefined;
738
+ readonly delta?: number | undefined;
739
+ /** Where an element outside the visible area sits; absent means on screen. */
740
+ readonly position?: "above" | "below" | "beside" | undefined;
741
+ /** A `press` candidate's key (`return`, `escape`, `tab`); its id is `press_<key>` by convention. */
742
+ readonly key?: string | undefined;
743
+ /** The region the element sits in; see {@link InteractionRegion}. */
744
+ readonly region?: InteractionRegion | undefined;
745
+ /**
746
+ * The `node` of the scroll container (a list or pane that scrolls on its own) the element sits
747
+ * in, matching that container's `scroll` candidates; absent when it sits in none.
748
+ */
749
+ readonly scroller?: number | undefined;
750
+ readonly [native: string]: unknown;
751
+ }
752
+
753
+ /** The candidate kinds a driver may offer. */
754
+ declare type InteractionCandidateKind = "click" | "fill" | "select" | "scroll" | "wait" | "activate" | "press";
755
+
756
+ declare interface InteractionDriver {
757
+ /** Unique in the session. `builtin:` is reserved for Kay's own drivers. */
758
+ readonly id: string;
759
+ /** Human-readable name the agent sees beside the driver's targets. */
760
+ readonly label: string;
761
+ readonly kind: InteractionDriverKind;
762
+ /** Required on a `browser` driver; see {@link INTERACTION_CREDENTIAL_GUARD}. */
763
+ readonly credentialGuard?: typeof INTERACTION_CREDENTIAL_GUARD;
764
+ /** Whether the driver can act at all right now (platform, grants, a live connection). */
765
+ isAvailable(): Promise<boolean>;
766
+ targets(): Promise<InteractionOutcome<{
767
+ readonly targets: readonly InteractionTarget[];
768
+ }>>;
769
+ observe(target: string | undefined, options?: {
770
+ readonly fullText?: boolean;
771
+ }): Promise<InteractionOutcome<InteractionObserved>>;
772
+ act(target: string | undefined, action: InteractionAction): Promise<InteractionOutcome<InteractionActed>>;
773
+ screenshot(target: string | undefined): Promise<InteractionOutcome<{
774
+ readonly png: Uint8Array;
775
+ }>>;
776
+ open(spec: InteractionOpenSpec): Promise<InteractionOutcome<InteractionActed>>;
777
+ }
778
+
779
+ /**
780
+ * The Interaction driver contract: what a plugin implements to let Kay's agent drive something
781
+ * on the user's machine (a browser, an app, a remote screen) through the harness-owned
782
+ * Interaction engine.
783
+ *
784
+ * The engine owns everything that is policy: the `interaction_*` tools, the session-unique element
785
+ * ids the agent sees, the run loop that drives a goal step by step, the permission check before
786
+ * every action, screenshots, and reports. A driver owns only its own I/O: list what it can drive,
787
+ * observe one target as a list of actionable candidates, perform one action on a candidate from
788
+ * its latest observation, capture a screenshot, and open a page or app. It chooses no actions,
789
+ * maps no permissions, and retries nothing beyond its own transport. That split is what makes
790
+ * every tool work on every driver.
791
+ *
792
+ * Shape mirrors `SearchProvider` behind `web_search`: one tool set owned by the harness, many
793
+ * backends registered per session, the `builtin:` id namespace reserved for Kay's own.
794
+ */
795
+ /** "Browser Interaction" drives web pages; "Computer Use Interaction" drives native apps. */
796
+ declare type InteractionDriverKind = "browser" | "computer-use";
797
+
798
+ /** A failed operation. `unavailable`: nothing to act on; `refused`: will not run as asked. */
799
+ declare interface InteractionFailure {
800
+ readonly ok: false;
801
+ readonly reason: "unavailable" | "refused" | "error";
802
+ readonly message: string;
803
+ }
804
+
805
+ /** What a driver sees on one target. */
806
+ declare interface InteractionObservation {
807
+ readonly url: string;
808
+ readonly title: string;
809
+ /** Visible text, or the whole page's text when the observe asked for `fullText`; capped. */
810
+ readonly text: string;
811
+ readonly actions: readonly InteractionCandidate[];
812
+ /** Candidates the driver dropped to stay within its cap. */
813
+ readonly omitted_actions: number;
814
+ }
815
+
816
+ declare interface InteractionObserved {
817
+ /** The address of the target observed; absent when the default target has no address. */
818
+ readonly target?: string;
819
+ readonly observation: InteractionObservation;
820
+ /** Lines the engine shows the agent under the observation (where the target sits, siblings). */
821
+ readonly notes?: readonly string[];
822
+ }
823
+
824
+ /** Navigate a page to `url`, or launch / switch to the app `app` names (a bundle id or a name). */
825
+ declare interface InteractionOpenSpec {
826
+ readonly target?: string;
827
+ readonly url?: string;
828
+ readonly app?: string;
829
+ /** The service whose browser sign-in state to report after navigation, when the engine knows it. */
830
+ readonly webSessionServiceId?: string;
831
+ }
832
+
833
+ declare type InteractionOutcome<T> = ({
834
+ readonly ok: true;
835
+ } & T) | InteractionFailure;
836
+
837
+ /**
838
+ * The part of a page or window an element sits in: the open dialog, menu, or sheet it belongs to,
839
+ * else its landmark (navigation, main, a window). `front` marks the overlay covering the rest: a
840
+ * modal dialog (or one held by a full-screen overlay), a sheet, or a floating menu, the last such
841
+ * in the document; elements outside it
842
+ * usually cannot be clicked while it is open. A docked or always-visible dialog or menu is a
843
+ * region but never in front.
844
+ */
845
+ declare interface InteractionRegion {
846
+ readonly role: string;
847
+ readonly name: string;
848
+ readonly front?: boolean | undefined;
849
+ }
850
+
851
+ /**
852
+ * One surface Kay ships, in the driver contract's own shape, so a plugin's driver is the surface
853
+ * plus an identity: `{ id, label, kind, ...api.interaction.desktop }`. Each call is checked
854
+ * against the plugin's `interaction.surface {surface}` grant, and then against the session's
855
+ * permissions exactly as the agent's `interaction_*` tools are: an act is judged as the plugin
856
+ * permission it amounts to (a Send click in Gmail as a Gmail write), an open as `interaction_open`,
857
+ * an observation or screenshot as `interaction_read`. A refused or unavailable call answers
858
+ * `{ ok: false }` (and `isAvailable()` false) rather than throwing.
859
+ */
860
+ declare type InteractionSurface = Pick<InteractionDriver, "isAvailable" | "targets" | "observe" | "act" | "screenshot" | "open">;
861
+
862
+ /**
863
+ * One thing a driver can be pointed at. `id` is a driver-owned address, unique across every
864
+ * driver in the session (prefix it with your scheme), that the agent passes back as `target`.
865
+ * `active` marks the driver's default target.
866
+ */
867
+ declare interface InteractionTarget {
868
+ readonly id: string;
869
+ readonly title: string;
870
+ readonly url?: string;
871
+ readonly active: boolean;
872
+ }
873
+
874
+ /** The answer shape one question gets back. */
875
+ declare type JevAnswerFor<Q> = Q extends JevBoolQuestion ? JevBoolAnswer : Q extends JevChoiceQuestion<infer L> ? JevChoiceAnswer<L> : Q extends JevScoreQuestion ? JevScoreAnswer : never;
876
+
877
+ declare interface JevBoolAnswer {
878
+ readonly type: "bool";
879
+ /** P(yes), from 0 to 1. */
880
+ readonly probability: number;
881
+ }
882
+
883
+ /** A yes/no question. `criteria` says what yes and what no mean; omit it to leave both open. */
884
+ declare interface JevBoolQuestion {
885
+ readonly type: "bool";
886
+ readonly instructions: string;
887
+ readonly criteria?: {
888
+ readonly true: string;
889
+ readonly false: string;
890
+ };
891
+ }
892
+
893
+ declare interface JevChoiceAnswer<L extends string = string> {
894
+ readonly type: "choice";
895
+ readonly choice: L;
896
+ readonly confidence: number;
897
+ /** P(label) for every offered label. */
898
+ readonly probabilities: Readonly<Record<L, number>>;
899
+ }
900
+
901
+ /** A choice among labels. Each label maps to what it means; an empty string leaves it open. */
902
+ declare interface JevChoiceQuestion<L extends string = string> {
903
+ readonly type: "choice";
904
+ readonly instructions: string;
905
+ readonly criteria: Readonly<Record<L, string>>;
906
+ }
907
+
908
+ /** One answered request: every asked question has an answer of its own kind. */
909
+ declare interface JevClassification<Q extends JevQuestions = JevQuestions> {
910
+ /** The Jev model the host asked. */
911
+ readonly model: string;
912
+ readonly answers: {
913
+ readonly [K in keyof Q]: JevAnswerFor<Q[K]>;
914
+ };
915
+ /** Token counts, when the route reported them. */
916
+ readonly usage?: {
917
+ readonly inputTokens: number;
918
+ readonly outputTokens: number;
919
+ };
920
+ }
921
+
922
+ declare interface JevClassifyRequest<Q extends JevQuestions = JevQuestions> {
923
+ readonly state: JevState;
924
+ readonly questions: Q;
925
+ }
926
+
927
+ /**
928
+ * The Jev client a `use` body receives. `classify` either resolves with an answer for every
929
+ * asked question, each of its question's kind, or rejects with a {@link JevCallError}; it never
930
+ * resolves with a partial or substituted answer.
931
+ */
932
+ declare interface JevClient {
933
+ classify<const Q extends JevQuestions>(request: JevClassifyRequest<Q>, options?: {
934
+ readonly signal?: AbortSignal;
935
+ }): Promise<JevClassification<Q>>;
936
+ }
937
+
938
+ declare type JevQuestion = JevBoolQuestion | JevChoiceQuestion | JevScoreQuestion;
939
+
940
+ /** A rubric: questions keyed by the name each answer comes back under. */
941
+ declare type JevQuestions = Readonly<Record<string, JevQuestion>>;
942
+
943
+ declare interface JevScoreAnswer {
944
+ readonly type: "score";
945
+ /** The expected score, from 0 to the number of rungs minus one. */
946
+ readonly score: number;
947
+ readonly confidence: number;
948
+ }
949
+
950
+ /** A score over ordered rungs: the first criterion scores 0, the next 1, and so on. */
951
+ declare interface JevScoreQuestion {
952
+ readonly type: "score";
953
+ readonly instructions: string;
954
+ readonly criteria: readonly string[];
955
+ }
956
+
957
+ /**
958
+ * ---
959
+ * responsibility: The plugin-facing CONTRACT for Jev typed-decision inference — the shape of
960
+ * `api.withJev` on both plugin SDK targets, the {@link JevClient} a `use` body receives, the
961
+ * question and answer vocabulary it speaks, and the fallback-only implementation a host
962
+ * without a Jev lane binds.
963
+ * owns:
964
+ * - {@link WithJev}, {@link WithJevOptions}, {@link createFallbackOnlyWithJev}.
965
+ * - {@link JevClient}, the {@link JevQuestion} and {@link JevAnswer} vocabulary, and
966
+ * {@link JevCallError}.
967
+ * does_not_own:
968
+ * - The wire. The host serves `classify` over whichever route the user's Jev provider order
969
+ * picks; a plugin never sees a vendor client, a vendor type, or a route.
970
+ * - Credential resolution, the `jev.use` decision, the Jev-branch budget, or usage accounting.
971
+ * Those are host-side (`@kay/agent-runtime`'s `kay/jev/`).
972
+ * ---
973
+ *
974
+ * The vocabulary is Kay's own (SDK 0.12.0). Before it, `use` received TypeSafe's `TypeSafeClient`
975
+ * and plugins coded against a vendor's types. It follows pi's classifier contract, which the host
976
+ * runs Jev through: a yes/no question is `bool` and answers a `probability`, every instruction and
977
+ * criterion is text, and the state is a JSON object.
978
+ */
979
+ /**
980
+ * The state a rubric reads: a JSON object, never text or an array. Every value must survive
981
+ * `JSON.stringify` unchanged (no functions or class instances), because it is sent as JSON.
982
+ * Typed as `object` so a state declared as an interface fits without an index signature.
983
+ */
984
+ declare type JevState = object;
985
+
631
986
  declare type JsonObject = {
632
987
  [key: string]: JsonValue_3;
633
988
  };
@@ -668,10 +1023,18 @@ declare interface KayAgentEvents {
668
1023
  publish(localId: string, payload: unknown): void;
669
1024
  }
670
1025
 
671
- /** Claim-checked filesystem operations. Paths are root-relative; canonical host paths never cross the seam. */
1026
+ /**
1027
+ * Claim-checked filesystem operations. Paths are root-relative; canonical host paths never cross
1028
+ * the seam. Writes are atomic: readers see the old file or the new one, never a partial write. A
1029
+ * replace keeps the file's mode but makes a new file, so owner and extended attributes are not
1030
+ * carried over. On a volume without hard links an exclusive create can briefly show an empty file.
1031
+ */
672
1032
  declare interface KayAgentFilesystem {
673
1033
  readText(location: KayAgentFilesystemLocation): Promise<string>;
674
- writeText(location: KayAgentFilesystemLocation, content: string): Promise<void>;
1034
+ writeText(location: KayAgentFilesystemLocation, content: string, options?: KayAgentFilesystemWriteOptions): Promise<void>;
1035
+ /** The whole file, or the requested window of it. */
1036
+ readBytes(location: KayAgentFilesystemLocation, range?: KayAgentFilesystemByteRange): Promise<Uint8Array>;
1037
+ writeBytes(location: KayAgentFilesystemLocation, bytes: Uint8Array, options?: KayAgentFilesystemWriteOptions): Promise<void>;
675
1038
  mkdir(location: KayAgentFilesystemLocation): Promise<void>;
676
1039
  readDir(location: KayAgentFilesystemLocation): Promise<readonly KayAgentFilesystemEntry[]>;
677
1040
  stat(location: KayAgentFilesystemLocation): Promise<KayAgentFilesystemStat>;
@@ -680,6 +1043,12 @@ declare interface KayAgentFilesystem {
680
1043
  realpath(location: KayAgentFilesystemLocation): Promise<string>;
681
1044
  }
682
1045
 
1046
+ /** A byte window of a file; `maxBytes` is capped by the host. */
1047
+ declare interface KayAgentFilesystemByteRange {
1048
+ readonly offset: number;
1049
+ readonly maxBytes: number;
1050
+ }
1051
+
683
1052
  declare interface KayAgentFilesystemEntry {
684
1053
  readonly name: string;
685
1054
  readonly kind: "file" | "directory" | "symlink" | "other";
@@ -697,6 +1066,27 @@ declare interface KayAgentFilesystemStat {
697
1066
  readonly modifiedAtMs: number;
698
1067
  }
699
1068
 
1069
+ declare interface KayAgentFilesystemWriteOptions {
1070
+ /** Fail with `already_exists` instead of replacing a file already at the location. */
1071
+ readonly exclusive?: boolean;
1072
+ }
1073
+
1074
+ declare interface KayAgentInteractionSurfaces {
1075
+ /**
1076
+ * Kay's built-in browser pane: the pages in the user's Bonsai tabs. The page-side credential
1077
+ * guard runs on every act, so a driver over this surface may declare
1078
+ * {@link INTERACTION_CREDENTIAL_GUARD}.
1079
+ */
1080
+ readonly browserPane: InteractionSurface;
1081
+ /**
1082
+ * The user's desktop through the platform's accessibility APIs: native apps as targets
1083
+ * (`app://<bundle id>`), their windows' controls as elements. On macOS Kay drives it with its
1084
+ * bundled accessibility worker under the app's Accessibility permission; on a platform with no
1085
+ * backend it reports itself unavailable.
1086
+ */
1087
+ readonly desktop: InteractionSurface;
1088
+ }
1089
+
700
1090
  /** One knowledge-index hit — a reference and within-list rank score, never a snippet. */
701
1091
  declare interface KayAgentKnowledgeHit {
702
1092
  readonly uri: string;
@@ -849,9 +1239,17 @@ declare interface KayAgentPluginApi {
849
1239
  * command it is about to run, and against the host's executable binding.
850
1240
  */
851
1241
  readonly process: KayAgentProcess;
1242
+ /**
1243
+ * Always present; the interaction surfaces Kay ships (its browser pane, and the desktop through
1244
+ * the platform accessibility APIs), each call checked against `interaction.surface {surface}`
1245
+ * and the session's permissions (see {@link InteractionSurface}).
1246
+ * Wrap one in an {@link InteractionDriver} and register it with
1247
+ * {@link KayAgentPluginApi.registerInteractionDriver} to make it drivable by the agent.
1248
+ */
1249
+ readonly interaction: KayAgentInteractionSurfaces;
852
1250
  /**
853
1251
  * Jev typed-decision inference, as an OPTIONAL accelerator with a REQUIRED fallback. Each call
854
- * is checked against `jev.use` — but a refused grant or a missing saved key both
1252
+ * is checked against `jev.use` — but a refused grant or no usable Jev provider both
855
1253
  * produce the same outcome: your `fallback` runs.
856
1254
  *
857
1255
  * Always present, on every host. That is the point: with no absent state to feature-detect,
@@ -880,6 +1278,14 @@ declare interface KayAgentPluginApi {
880
1278
  * from `ctx.auth`. A provider without a binding reuses inference credentials or needs none.
881
1279
  */
882
1280
  registerSearchProvider(provider: SearchProvider): void;
1281
+ /**
1282
+ * Register an Interaction driver (a backend behind the `interaction_*` tools: something the agent
1283
+ * can read, click, type into, and open). The engine owns the tools, element ids, run loop,
1284
+ * permission checks, and screenshots; the driver owns only its I/O. Declare the driver's `id`
1285
+ * under `contributes.interactionDrivers` in kay-plugin.json first. A `browser` driver must set
1286
+ * `credentialGuard` (see {@link INTERACTION_CREDENTIAL_GUARD}).
1287
+ */
1288
+ registerInteractionDriver(driver: InteractionDriver): void;
883
1289
  /**
884
1290
  * Register a content converter (turns raw bytes of a mime type into agent-facing content behind the
885
1291
  * read tool's finalize stage). Parallel to the kernel's `registerConverter`. Converters are
@@ -1082,7 +1488,7 @@ declare interface KayAgentWhiteboardUpdateInput {
1082
1488
 
1083
1489
  declare type KnownApi = "openai-completions" | "mistral-conversations" | "openai-responses" | "azure-openai-responses" | "openai-codex-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-vertex" | "pi-messages";
1084
1490
 
1085
- declare type KnownProvider = "amazon-bedrock" | "ant-ling" | "anthropic" | "google" | "google-vertex" | "openai" | "azure-openai-responses" | "openai-codex" | "radius" | "nvidia" | "deepseek" | "github-copilot" | "xai" | "groq" | "cerebras" | "openrouter" | "vercel-ai-gateway" | "zai" | "zai-coding-cn" | "mistral" | "minimax" | "minimax-cn" | "moonshotai" | "moonshotai-cn" | "huggingface" | "fireworks" | "together" | "baseten" | "opencode" | "opencode-go" | "kimi-coding" | "meta" | "cloudflare-workers-ai" | "cloudflare-ai-gateway" | "qwen-token-plan" | "qwen-token-plan-cn" | "qwen-token-plan-individual" | "xiaomi" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-sgp";
1491
+ declare type KnownProvider = "amazon-bedrock" | "ant-ling" | "anthropic" | "google" | "google-vertex" | "openai" | "azure-openai-responses" | "openai-codex" | "radius" | "typesafe" | "nvidia" | "deepseek" | "github-copilot" | "xai" | "groq" | "cerebras" | "openrouter" | "vercel-ai-gateway" | "zai" | "zai-coding-cn" | "mistral" | "minimax" | "minimax-cn" | "moonshotai" | "moonshotai-cn" | "huggingface" | "fireworks" | "together" | "baseten" | "opencode" | "opencode-go" | "kimi-coding" | "meta" | "cloudflare-workers-ai" | "cloudflare-ai-gateway" | "qwen-token-plan" | "qwen-token-plan-cn" | "qwen-token-plan-individual" | "xiaomi" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-sgp";
1086
1492
 
1087
1493
  declare interface LineRange {
1088
1494
  readonly start: number;
@@ -1133,29 +1539,26 @@ declare interface MistralConversationsCompat {
1133
1539
  supportsMidConvoSystemMessages?: boolean;
1134
1540
  }
1135
1541
 
1136
- declare interface Model<TApi extends Api> {
1137
- id: string;
1138
- name: string;
1139
- api: TApi;
1140
- provider: ProviderId;
1141
- baseUrl: string;
1542
+ /** Chat model: usable with `stream()` and friends. */
1543
+ declare interface Model<TApi extends Api> extends BaseModel<TApi> {
1544
+ /**
1545
+ * Optional: chat is the default model type, so models without `type` are chat
1546
+ * models. Narrow mixed model lists with `isModelType()` instead of comparing
1547
+ * `type` directly.
1548
+ */
1549
+ type?: "chat";
1142
1550
  reasoning: boolean;
1143
1551
  /**
1144
1552
  * Maps pi thinking levels to provider/model-specific values.
1145
1553
  * Missing keys use provider defaults. null marks a level as unsupported.
1146
1554
  */
1147
1555
  thinkingLevelMap?: ThinkingLevelMap;
1148
- input: ("text" | "image")[];
1149
- /** Provider input limits and cache-safe preprocessing metadata. */
1150
- inputLimits?: ModelInputLimits;
1151
- cost: ModelCost;
1152
1556
  /** Prompt cache lifetimes per retention tier. Unset when the provider's cache behavior is unknown. */
1153
1557
  promptCache?: ModelPromptCache;
1154
1558
  contextWindow: number;
1155
1559
  maxTokens: number;
1156
1560
  /** Default sampling parameters for this model. See {@link StreamOptions.samplingParams}; per-request keys override these. */
1157
1561
  samplingParams?: Record<string, unknown>;
1158
- headers?: Record<string, string>;
1159
1562
  /** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */
1160
1563
  compat?: TApi extends "openai-completions" ? OpenAICompletionsCompat : TApi extends "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? OpenAIResponsesCompat : TApi extends "anthropic-messages" ? AnthropicMessagesCompat : TApi extends "bedrock-converse-stream" ? BedrockCompat : TApi extends "mistral-conversations" ? MistralConversationsCompat : never;
1161
1564
  }
@@ -1501,8 +1904,15 @@ declare interface PluginDelegatedScheme {
1501
1904
  /** Run the connect flow and mint the material to store. Throw to fail the connect. */
1502
1905
  readonly connect: (ctx: PluginDelegatedConnectContext) => Promise<PluginDelegatedConnectResult>;
1503
1906
  /**
1504
- * Mint fresh material from the current (expiring) material. Absent means non-refreshable:
1505
- * expired material resolves as-is (like an OAuth scheme without a refresh token).
1907
+ * Mint fresh material from the current (expiring) material, including the `data` the last
1908
+ * connect or refresh returned. Kay calls this only inside its credential authority, which holds
1909
+ * the refresh lease and saves the result; this hook performs the exchange and nothing else.
1910
+ * Returned `data` replaces the stored `data`; omitted `data` keeps it. Returned `expiresAt`
1911
+ * replaces the stored expiry; omitted `expiresAt` means the new material does not expire.
1912
+ * Throw {@link DelegatedRefreshRejectedError} when the provider refused the grant for good (the
1913
+ * user must reconnect); any other throw is retried with backoff and keeps the account connected.
1914
+ * Absent means non-refreshable: expired material resolves as-is (like an OAuth scheme without a
1915
+ * refresh token).
1506
1916
  */
1507
1917
  readonly refresh?: (material: PluginDelegatedSecretMaterial) => Promise<PluginDelegatedSecretMaterial>;
1508
1918
  /**
@@ -2081,6 +2491,19 @@ declare interface ToolCallModel {
2081
2491
  */
2082
2492
  declare type ToolCallOrigin = "model" | "host";
2083
2493
 
2494
+ /** A call to judge with {@link ToolContext.checkToolCallPermission}. */
2495
+ declare interface ToolCallPermissionCheck {
2496
+ readonly toolName: string;
2497
+ readonly input: Record<string, unknown>;
2498
+ }
2499
+
2500
+ declare type ToolCallPermissionVerdict = {
2501
+ readonly allowed: true;
2502
+ } | {
2503
+ readonly allowed: false;
2504
+ readonly reason: string;
2505
+ };
2506
+
2084
2507
  /**
2085
2508
  * What a tool call ADDRESSES, resolved by the session before execution from facts it already
2086
2509
  * holds for auth scoping: a URI verb's matched module (and the extension that registered it), or
@@ -2123,6 +2546,13 @@ declare interface ToolContext {
2123
2546
  * behind itself. Present only with a live session. One request may be pending at a time.
2124
2547
  */
2125
2548
  readonly requestMidTurnCompaction?: (customInstructions?: string, trigger?: "command" | "suggestion") => MidTurnCompactionRequest;
2549
+ /**
2550
+ * Put a call this tool is about to cause through the session's permission gate, as if the agent
2551
+ * had made it directly: classification, rules, mode, the auto approver, and the approval card.
2552
+ * The call is only judged, never executed. For a tool whose effect is only knowable at run time
2553
+ * (a click in a web page that turns out to send an email). Present only with a live session.
2554
+ */
2555
+ readonly checkToolCallPermission?: (call: ToolCallPermissionCheck) => Promise<ToolCallPermissionVerdict>;
2126
2556
  /**
2127
2557
  * Re-root the session into `newCwd` mid-run, recomposing the cwd-scoped substrate (builtin tools,
2128
2558
  * the capability registry, the system-prompt project line) in place so subsequent tool calls operate
@@ -2692,6 +3122,21 @@ declare interface UriModule {
2692
3122
  */
2693
3123
  readonly title?: string;
2694
3124
  readonly fallback?: boolean;
3125
+ /**
3126
+ * Whether this module parses the read tool's trailing line selector out of its own addresses.
3127
+ * Absent or false: the host owns the explicit forms (`:50-200`, `:50-`, `:50+10`, `:raw`). The
3128
+ * read tool strips one before dispatch, hands `read` the bare address, and applies the window
3129
+ * to what comes back, so a module never has to know the grammar for the read tool's own
3130
+ * `Continue with '<address>:N-M'` banner to work on its addresses (#10754). A bare trailing
3131
+ * `:<digits>` is never taken from such a module: it is too common inside real ids (a Figma
3132
+ * node `1:2`), so it reaches the module as written.
3133
+ *
3134
+ * Declare `true` only when the module must see the raw address: its grammar can end in an
3135
+ * explicit-range shape, it wants the bare `:50` form too, or it windows the content itself
3136
+ * (streaming a large file, skipping a cap it applies to an unwindowed read). Such a module
3137
+ * calls `parseLineSelector` itself and reports what it did through `RawResource.selector`.
3138
+ */
3139
+ readonly ownsLineSelectors?: boolean;
2695
3140
  readonly claims?: readonly UriClaim[];
2696
3141
  /** Migration-only prompt summary until structured help/catalog coverage replaces prose. */
2697
3142
  readonly description?: string;
@@ -2708,7 +3153,9 @@ declare interface UriModule {
2708
3153
  * The host owns framing, the whole-quote fit (no elision) and display-safety admission.
2709
3154
  *
2710
3155
  * Unlike preflight, this hook is async and may do I/O. Resolve names through `ctx`,
2711
- * the same dispatch context used by execution, never an ambient or cached account.
3156
+ * the same dispatch context used by execution, never an ambient or cached account. A name
3157
+ * the module resolved under that account may be cached for a bounded time, keyed by that
3158
+ * account's identity and never answered to another account or after a failed lookup.
2712
3159
  * The host calls it once before raising the ask, enforces the short `ctx.signal`
2713
3160
  * deadline and discards late answers. A throw yields no summary and a module-attributed
2714
3161
  * diagnostic; declining costs a click into the app, never an under-described approval.
@@ -2718,6 +3165,26 @@ declare interface UriModule {
2718
3165
  readonly uri: string;
2719
3166
  readonly payload: unknown;
2720
3167
  }, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
3168
+ /**
3169
+ * A short, render-only name for the resource a settled call on `request.uri` touched, which the
3170
+ * host stamps on the tool result's `details` so a transcript row can name it where it would
3171
+ * otherwise print the address (`#lane-analytics` and `thread`, not a channel id). Answer
3172
+ * `undefined` for any address you cannot honestly name.
3173
+ *
3174
+ * Called once per successful URI verb call, AFTER the verb handler has returned, with the same
3175
+ * dispatch context. Resolve names through `ctx` and nothing else, and prefer a cache your own
3176
+ * verb handlers warm, keyed by the executing account: a name one account saw is never answered
3177
+ * to another, and a failed, blank or aborted lookup is never cached. The host bounds the wait
3178
+ * (a finished result is held at most a quarter of a second), gives `ctx.signal` a hard deadline,
3179
+ * and drops a late, thrown or refused answer without failing the call; a throw is reported as a
3180
+ * module-attributed diagnostic. The label is a label, never an identity: the address remains the
3181
+ * click target, hover text and accessible name. It is UI-only, so it never enters the tool
3182
+ * result's content and no telemetry reads it.
3183
+ *
3184
+ * Unlike `describeOperation` this is not an approval surface: it carries no consequence, no
3185
+ * quote and no coverage claim, and nothing may gate a permission on it.
3186
+ */
3187
+ labelResource?(request: UriResourceLabelRequest, ctx: UriResolveContext): Promise<UriResourceLabel | undefined>;
2721
3188
  /**
2722
3189
  * Per-resource observability: does a `format: "observation"` read of THIS
2723
3190
  * uri render source-native material ({@link UriResolveContext.format})?
@@ -3101,6 +3568,24 @@ declare interface UriResourceDescription {
3101
3568
  readonly sourceUrl?: string;
3102
3569
  }
3103
3570
 
3571
+ /**
3572
+ * What a settled call touched, as a person would name it.
3573
+ *
3574
+ * `name` is the resource itself (`#lane-analytics`). `section` is the part of it the address
3575
+ * reaches into (`thread`), and is absent when the address is the resource.
3576
+ */
3577
+ declare interface UriResourceLabel {
3578
+ readonly name: string;
3579
+ readonly section?: string;
3580
+ }
3581
+
3582
+ /** The call a module is asked to label. */
3583
+ declare interface UriResourceLabelRequest {
3584
+ readonly operation: UriOperation;
3585
+ /** The dispatch address: selector-stripped, the exact form the module's verb handler received. */
3586
+ readonly uri: string;
3587
+ }
3588
+
3104
3589
  declare interface UriSchemeClaim {
3105
3590
  readonly kind: "scheme";
3106
3591
  readonly scheme: string;
@@ -3417,8 +3902,8 @@ declare type WhiteboardValue = z.infer<typeof WhiteboardValue>;
3417
3902
  /**
3418
3903
  * Run `use` against a live Jev client, falling back to `fallback` whenever Jev cannot serve the
3419
3904
  * call. The wrapper exists so a feature can ASK for a typed decision without ever depending on
3420
- * one: `fallback` is required, and it runs whenever no host-managed key is saved,
3421
- * the user has not granted `jev.use`, the branch exceeds its budget, `use` throws, or
3905
+ * one: `fallback` is required, and it runs whenever no provider in the user's Jev provider
3906
+ * order holds a usable credential, the user has not granted `jev.use`, the branch exceeds its budget, `use` throws, or
3422
3907
  * `use` declines by returning `undefined`. With valid options, this rejects on an explicit
3423
3908
  * `signal` abort, reaching `deadlineAt`, or `fallback` itself throwing.
3424
3909
  *
@@ -3426,10 +3911,9 @@ declare type WhiteboardValue = z.infer<typeof WhiteboardValue>;
3426
3911
  * {@link createFallbackOnlyWithJev}, so a caller never branches on availability — branching is
3427
3912
  * exactly how a fallback gets skipped.
3428
3913
  *
3429
- * `jev` is the vendor's own `TypeSafeClient`, configured with the host's default model and base
3430
- * URL. Native `request.model` overrides remain supported. It
3431
- * is valid only for the duration of the callback: a reference retained past `use`'s scope is
3432
- * refused at the transport, and an in-flight request is aborted when the call stops waiting.
3914
+ * `jev` is a {@link JevClient} on the host's Jev model. It is valid only for the duration of
3915
+ * the callback: a reference retained past `use`'s scope is refused at the transport, and an
3916
+ * in-flight request is aborted when the call stops waiting.
3433
3917
  * The callback itself is ABANDONED, not interrupted — a host cannot stop arbitrary JavaScript
3434
3918
  * that is already running, so `use` must not perform side effects it would mind completing
3435
3919
  * unobserved. The same applies to a running fallback: abort/deadline stops waiting for it but
@@ -3438,7 +3922,7 @@ declare type WhiteboardValue = z.infer<typeof WhiteboardValue>;
3438
3922
  declare type WithJev = <T>(
3439
3923
  /** Return `undefined` to decline (the fallback runs). A feature whose real result can itself be
3440
3924
  * `undefined` should return `null` or its own wrapper type instead. */
3441
- use: (jev: TypeSafeClient) => T | undefined | PromiseLike<T | undefined>,
3925
+ use: (jev: JevClient) => T | undefined | PromiseLike<T | undefined>,
3442
3926
  /** Required. The behaviour the feature has without Jev. */
3443
3927
  fallback: () => T | PromiseLike<T>, options?: WithJevOptions) => Promise<T>;
3444
3928