@yaklabs-ai/plugin-sdk 0.10.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.
@@ -246,6 +258,20 @@ declare interface BackgroundTaskSnapshot {
246
258
 
247
259
  declare type BackgroundTaskStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
248
260
 
261
+ /** Fields shared by every catalog entry, regardless of what you can do with it. */
262
+ declare interface BaseModel<TApi extends string> {
263
+ id: string;
264
+ name: string;
265
+ api: TApi;
266
+ provider: ProviderId;
267
+ baseUrl: string;
268
+ input: ("text" | "image")[];
269
+ /** Provider input limits and cache-safe preprocessing metadata. */
270
+ inputLimits?: ModelInputLimits;
271
+ cost: ModelCost;
272
+ headers?: Record<string, string>;
273
+ }
274
+
249
275
  /** Compatibility settings for Amazon Bedrock models. */
250
276
  declare interface BedrockCompat {
251
277
  /** Whether the model supports Bedrock strict tool schemas. Default: false. */
@@ -371,6 +397,13 @@ declare interface DelegatedConnectContext {
371
397
  /** What a delegated scheme's `connect` resolves with: the material to vault + optional label/provenance. */
372
398
  declare interface DelegatedConnectResult {
373
399
  readonly material: DelegatedSecretMaterial;
400
+ /**
401
+ * A stable account id for this sign-in, used instead of a freshly minted one, so a reconnect
402
+ * overwrites the same record (and keeps it the default) rather than adding a sibling. 1-128
403
+ * characters: letters, digits, and `._:@+-`, starting with a letter or digit; any other value
404
+ * fails the connect. Absent mints a fresh id per connect.
405
+ */
406
+ readonly accountId?: string;
374
407
  /** Account display label; a caller-supplied `service_connect` label takes precedence. */
375
408
  readonly label?: string;
376
409
  /** Structured provenance the scheme derived during connect; persisted via the vault's write-path. */
@@ -498,24 +531,8 @@ declare interface ExtensionPaths {
498
531
  export declare function fauxAuthContext(overrides?: Partial<ExtensionAuthContext>): ExtensionAuthContext;
499
532
 
500
533
  /** The capability handles a `KayAgentPluginApi` always carries — authority is decided per call. */
501
- export declare type FauxKayAgentCapabilities = Pick<KayAgentPluginApi, "filesystem" | "network" | "webSession" | "process" | "whiteboard" | "interceptToolCall" | "withJev" | "events">;
534
+ export declare type FauxKayAgentCapabilities = Pick<KayAgentPluginApi, "filesystem" | "network" | "webSession" | "process" | "whiteboard" | "interaction" | "interceptToolCall" | "withJev" | "events">;
502
535
 
503
- /**
504
- * The brokered capability handles every `KayAgentPluginApi` carries, as doubles that refuse
505
- * to be used. Spread them into a hand-rolled activation fake:
506
- *
507
- * ```ts
508
- * createMyPluginKayAgent()({ ...fauxKayAgentCapabilities(), uri: { registerModule }, ... });
509
- * ```
510
- *
511
- * They exist because the handles are ALWAYS present on the real api — the daemon composes every
512
- * brokered capability unconditionally and decides authority at each call (`filesystem.read
513
- * {root}`, `network.connect {connect}`, ...), so presence is not a grant signal a plugin may
514
- * feature-detect, and a fake that omits one is not a `KayAgentPluginApi`. The four I/O brokers
515
- * reject on use; `interceptToolCall` is a registration, so like the fakes' own `registerTool` it
516
- * accepts and drops. A test that exercises a capability overrides that one handle after the
517
- * spread; the rest keep refusing.
518
- */
519
536
  export declare function fauxKayAgentCapabilities(): FauxKayAgentCapabilities;
520
537
 
521
538
  /**
@@ -531,6 +548,216 @@ declare interface ImageContent {
531
548
  mimeType: string;
532
549
  }
533
550
 
551
+ /**
552
+ * The value a Browser Interaction driver sets as `credentialGuard` to declare that every act runs
553
+ * the page-side credential guard from `@kay/interaction-page-scripts` (never type into or click a
554
+ * sign-in form). The engine refuses to register a browser driver without it.
555
+ */
556
+ declare const INTERACTION_CREDENTIAL_GUARD = "kay.credential-guard/v1";
557
+
558
+ declare interface InteractionActed {
559
+ /** The address of the target acted on, when it changed (an open landed in a new page). */
560
+ readonly target?: string;
561
+ readonly url: string;
562
+ readonly title: string;
563
+ /** What the action visibly caused: a dialog, an alert, a page opened beside it. */
564
+ readonly changes?: readonly string[];
565
+ readonly notes?: readonly string[];
566
+ /** Fresh browser sign-in state for the service the engine named in the open spec. */
567
+ readonly webSession?: {
568
+ readonly state: "signed-in" | "signed-out" | "unknown";
569
+ readonly checkedAt: number | null;
570
+ };
571
+ }
572
+
573
+ /**
574
+ * One action over a candidate from the latest observation of the same target. `fill` replaces the
575
+ * field's contents unless `append` is set, and presses Enter afterwards when `submit` is set.
576
+ */
577
+ declare type InteractionAction = {
578
+ readonly kind: "click";
579
+ readonly element: InteractionCandidate;
580
+ } | {
581
+ readonly kind: "fill";
582
+ readonly element: InteractionCandidate;
583
+ readonly text: string;
584
+ readonly submit?: boolean;
585
+ readonly append?: boolean;
586
+ } | {
587
+ readonly kind: "select";
588
+ readonly element: InteractionCandidate;
589
+ } | {
590
+ readonly kind: "activate";
591
+ readonly element: InteractionCandidate;
592
+ } | {
593
+ readonly kind: "press";
594
+ readonly element: InteractionCandidate;
595
+ }
596
+ /**
597
+ * Scroll by `delta` px (positive is down). With `element` (a `scroll` candidate that names a
598
+ * node), the scroll container that candidate scrolls; without, the whole page or window.
599
+ */
600
+ | {
601
+ readonly kind: "scroll";
602
+ readonly delta: number;
603
+ readonly element?: InteractionCandidate;
604
+ } | {
605
+ readonly kind: "wait";
606
+ };
607
+
608
+ /**
609
+ * One element (or control) an observation offers, with the kind of action it supports. `id` is
610
+ * the driver's per-observation id; the engine mints its own session-unique ids over it. Extra
611
+ * fields are the driver's native identity (a page node number, an accessibility token), returned
612
+ * to the driver untouched in {@link InteractionAction}.
613
+ */
614
+ declare interface InteractionCandidate {
615
+ readonly id: string;
616
+ readonly kind: InteractionCandidateKind;
617
+ readonly label: string;
618
+ readonly node?: number | undefined;
619
+ readonly role?: string | undefined;
620
+ readonly value?: string | undefined;
621
+ readonly current_value?: string | undefined;
622
+ readonly checked?: string | undefined;
623
+ readonly selected?: string | undefined;
624
+ readonly expanded?: string | undefined;
625
+ readonly delta?: number | undefined;
626
+ /** Where an element outside the visible area sits; absent means on screen. */
627
+ readonly position?: "above" | "below" | "beside" | undefined;
628
+ /** A `press` candidate's key (`return`, `escape`, `tab`); its id is `press_<key>` by convention. */
629
+ readonly key?: string | undefined;
630
+ /** The region the element sits in; see {@link InteractionRegion}. */
631
+ readonly region?: InteractionRegion | undefined;
632
+ /**
633
+ * The `node` of the scroll container (a list or pane that scrolls on its own) the element sits
634
+ * in, matching that container's `scroll` candidates; absent when it sits in none.
635
+ */
636
+ readonly scroller?: number | undefined;
637
+ readonly [native: string]: unknown;
638
+ }
639
+
640
+ /** The candidate kinds a driver may offer. */
641
+ declare type InteractionCandidateKind = "click" | "fill" | "select" | "scroll" | "wait" | "activate" | "press";
642
+
643
+ declare interface InteractionDriver {
644
+ /** Unique in the session. `builtin:` is reserved for Kay's own drivers. */
645
+ readonly id: string;
646
+ /** Human-readable name the agent sees beside the driver's targets. */
647
+ readonly label: string;
648
+ readonly kind: InteractionDriverKind;
649
+ /** Required on a `browser` driver; see {@link INTERACTION_CREDENTIAL_GUARD}. */
650
+ readonly credentialGuard?: typeof INTERACTION_CREDENTIAL_GUARD;
651
+ /** Whether the driver can act at all right now (platform, grants, a live connection). */
652
+ isAvailable(): Promise<boolean>;
653
+ targets(): Promise<InteractionOutcome<{
654
+ readonly targets: readonly InteractionTarget[];
655
+ }>>;
656
+ observe(target: string | undefined, options?: {
657
+ readonly fullText?: boolean;
658
+ }): Promise<InteractionOutcome<InteractionObserved>>;
659
+ act(target: string | undefined, action: InteractionAction): Promise<InteractionOutcome<InteractionActed>>;
660
+ screenshot(target: string | undefined): Promise<InteractionOutcome<{
661
+ readonly png: Uint8Array;
662
+ }>>;
663
+ open(spec: InteractionOpenSpec): Promise<InteractionOutcome<InteractionActed>>;
664
+ }
665
+
666
+ /**
667
+ * The Interaction driver contract: what a plugin implements to let Kay's agent drive something
668
+ * on the user's machine (a browser, an app, a remote screen) through the harness-owned
669
+ * Interaction engine.
670
+ *
671
+ * The engine owns everything that is policy: the `interaction_*` tools, the session-unique element
672
+ * ids the agent sees, the run loop that drives a goal step by step, the permission check before
673
+ * every action, screenshots, and reports. A driver owns only its own I/O: list what it can drive,
674
+ * observe one target as a list of actionable candidates, perform one action on a candidate from
675
+ * its latest observation, capture a screenshot, and open a page or app. It chooses no actions,
676
+ * maps no permissions, and retries nothing beyond its own transport. That split is what makes
677
+ * every tool work on every driver.
678
+ *
679
+ * Shape mirrors `SearchProvider` behind `web_search`: one tool set owned by the harness, many
680
+ * backends registered per session, the `builtin:` id namespace reserved for Kay's own.
681
+ */
682
+ /** "Browser Interaction" drives web pages; "Computer Use Interaction" drives native apps. */
683
+ declare type InteractionDriverKind = "browser" | "computer-use";
684
+
685
+ /** A failed operation. `unavailable`: nothing to act on; `refused`: will not run as asked. */
686
+ declare interface InteractionFailure {
687
+ readonly ok: false;
688
+ readonly reason: "unavailable" | "refused" | "error";
689
+ readonly message: string;
690
+ }
691
+
692
+ /** What a driver sees on one target. */
693
+ declare interface InteractionObservation {
694
+ readonly url: string;
695
+ readonly title: string;
696
+ /** Visible text, or the whole page's text when the observe asked for `fullText`; capped. */
697
+ readonly text: string;
698
+ readonly actions: readonly InteractionCandidate[];
699
+ /** Candidates the driver dropped to stay within its cap. */
700
+ readonly omitted_actions: number;
701
+ }
702
+
703
+ declare interface InteractionObserved {
704
+ /** The address of the target observed; absent when the default target has no address. */
705
+ readonly target?: string;
706
+ readonly observation: InteractionObservation;
707
+ /** Lines the engine shows the agent under the observation (where the target sits, siblings). */
708
+ readonly notes?: readonly string[];
709
+ }
710
+
711
+ /** Navigate a page to `url`, or launch / switch to the app `app` names (a bundle id or a name). */
712
+ declare interface InteractionOpenSpec {
713
+ readonly target?: string;
714
+ readonly url?: string;
715
+ readonly app?: string;
716
+ /** The service whose browser sign-in state to report after navigation, when the engine knows it. */
717
+ readonly webSessionServiceId?: string;
718
+ }
719
+
720
+ declare type InteractionOutcome<T> = ({
721
+ readonly ok: true;
722
+ } & T) | InteractionFailure;
723
+
724
+ /**
725
+ * The part of a page or window an element sits in: the open dialog, menu, or sheet it belongs to,
726
+ * else its landmark (navigation, main, a window). `front` marks the overlay covering the rest: a
727
+ * modal dialog (or one held by a full-screen overlay), a sheet, or a floating menu, the last such
728
+ * in the document; elements outside it
729
+ * usually cannot be clicked while it is open. A docked or always-visible dialog or menu is a
730
+ * region but never in front.
731
+ */
732
+ declare interface InteractionRegion {
733
+ readonly role: string;
734
+ readonly name: string;
735
+ readonly front?: boolean | undefined;
736
+ }
737
+
738
+ /**
739
+ * One surface Kay ships, in the driver contract's own shape, so a plugin's driver is the surface
740
+ * plus an identity: `{ id, label, kind, ...api.interaction.desktop }`. Each call is checked
741
+ * against the plugin's `interaction.surface {surface}` grant, and then against the session's
742
+ * permissions exactly as the agent's `interaction_*` tools are: an act is judged as the plugin
743
+ * permission it amounts to (a Send click in Gmail as a Gmail write), an open as `interaction_open`,
744
+ * an observation or screenshot as `interaction_read`. A refused or unavailable call answers
745
+ * `{ ok: false }` (and `isAvailable()` false) rather than throwing.
746
+ */
747
+ declare type InteractionSurface = Pick<InteractionDriver, "isAvailable" | "targets" | "observe" | "act" | "screenshot" | "open">;
748
+
749
+ /**
750
+ * One thing a driver can be pointed at. `id` is a driver-owned address, unique across every
751
+ * driver in the session (prefix it with your scheme), that the agent passes back as `target`.
752
+ * `active` marks the driver's default target.
753
+ */
754
+ declare interface InteractionTarget {
755
+ readonly id: string;
756
+ readonly title: string;
757
+ readonly url?: string;
758
+ readonly active: boolean;
759
+ }
760
+
534
761
  /**
535
762
  * Invoke a plugin tool definition's `execute` in a test, owning the **one sanctioned cast** past
536
763
  * `ToolExecutionAwareParams<unknown>` — the phantom-wire-shaped type every `ToolDefinition`'s
@@ -549,6 +776,118 @@ declare interface ImageContent {
549
776
  */
550
777
  export declare function invokeToolForTest<TDetails = unknown>(tool: ToolDefinition<any, TDetails>, params: Record<string, unknown>, ctx?: ToolContext): Promise<AgentToolResult<TDetails>>;
551
778
 
779
+ /** The answer shape one question gets back. */
780
+ declare type JevAnswerFor<Q> = Q extends JevBoolQuestion ? JevBoolAnswer : Q extends JevChoiceQuestion<infer L> ? JevChoiceAnswer<L> : Q extends JevScoreQuestion ? JevScoreAnswer : never;
781
+
782
+ declare interface JevBoolAnswer {
783
+ readonly type: "bool";
784
+ /** P(yes), from 0 to 1. */
785
+ readonly probability: number;
786
+ }
787
+
788
+ /** A yes/no question. `criteria` says what yes and what no mean; omit it to leave both open. */
789
+ declare interface JevBoolQuestion {
790
+ readonly type: "bool";
791
+ readonly instructions: string;
792
+ readonly criteria?: {
793
+ readonly true: string;
794
+ readonly false: string;
795
+ };
796
+ }
797
+
798
+ declare interface JevChoiceAnswer<L extends string = string> {
799
+ readonly type: "choice";
800
+ readonly choice: L;
801
+ readonly confidence: number;
802
+ /** P(label) for every offered label. */
803
+ readonly probabilities: Readonly<Record<L, number>>;
804
+ }
805
+
806
+ /** A choice among labels. Each label maps to what it means; an empty string leaves it open. */
807
+ declare interface JevChoiceQuestion<L extends string = string> {
808
+ readonly type: "choice";
809
+ readonly instructions: string;
810
+ readonly criteria: Readonly<Record<L, string>>;
811
+ }
812
+
813
+ /** One answered request: every asked question has an answer of its own kind. */
814
+ declare interface JevClassification<Q extends JevQuestions = JevQuestions> {
815
+ /** The Jev model the host asked. */
816
+ readonly model: string;
817
+ readonly answers: {
818
+ readonly [K in keyof Q]: JevAnswerFor<Q[K]>;
819
+ };
820
+ /** Token counts, when the route reported them. */
821
+ readonly usage?: {
822
+ readonly inputTokens: number;
823
+ readonly outputTokens: number;
824
+ };
825
+ }
826
+
827
+ declare interface JevClassifyRequest<Q extends JevQuestions = JevQuestions> {
828
+ readonly state: JevState;
829
+ readonly questions: Q;
830
+ }
831
+
832
+ /**
833
+ * The Jev client a `use` body receives. `classify` either resolves with an answer for every
834
+ * asked question, each of its question's kind, or rejects with a {@link JevCallError}; it never
835
+ * resolves with a partial or substituted answer.
836
+ */
837
+ declare interface JevClient {
838
+ classify<const Q extends JevQuestions>(request: JevClassifyRequest<Q>, options?: {
839
+ readonly signal?: AbortSignal;
840
+ }): Promise<JevClassification<Q>>;
841
+ }
842
+
843
+ declare type JevQuestion = JevBoolQuestion | JevChoiceQuestion | JevScoreQuestion;
844
+
845
+ /** A rubric: questions keyed by the name each answer comes back under. */
846
+ declare type JevQuestions = Readonly<Record<string, JevQuestion>>;
847
+
848
+ declare interface JevScoreAnswer {
849
+ readonly type: "score";
850
+ /** The expected score, from 0 to the number of rungs minus one. */
851
+ readonly score: number;
852
+ readonly confidence: number;
853
+ }
854
+
855
+ /** A score over ordered rungs: the first criterion scores 0, the next 1, and so on. */
856
+ declare interface JevScoreQuestion {
857
+ readonly type: "score";
858
+ readonly instructions: string;
859
+ readonly criteria: readonly string[];
860
+ }
861
+
862
+ /**
863
+ * ---
864
+ * responsibility: The plugin-facing CONTRACT for Jev typed-decision inference — the shape of
865
+ * `api.withJev` on both plugin SDK targets, the {@link JevClient} a `use` body receives, the
866
+ * question and answer vocabulary it speaks, and the fallback-only implementation a host
867
+ * without a Jev lane binds.
868
+ * owns:
869
+ * - {@link WithJev}, {@link WithJevOptions}, {@link createFallbackOnlyWithJev}.
870
+ * - {@link JevClient}, the {@link JevQuestion} and {@link JevAnswer} vocabulary, and
871
+ * {@link JevCallError}.
872
+ * does_not_own:
873
+ * - The wire. The host serves `classify` over whichever route the user's Jev provider order
874
+ * picks; a plugin never sees a vendor client, a vendor type, or a route.
875
+ * - Credential resolution, the `jev.use` decision, the Jev-branch budget, or usage accounting.
876
+ * Those are host-side (`@kay/agent-runtime`'s `kay/jev/`).
877
+ * ---
878
+ *
879
+ * The vocabulary is Kay's own (SDK 0.12.0). Before it, `use` received TypeSafe's `TypeSafeClient`
880
+ * and plugins coded against a vendor's types. It follows pi's classifier contract, which the host
881
+ * runs Jev through: a yes/no question is `bool` and answers a `probability`, every instruction and
882
+ * criterion is text, and the state is a JSON object.
883
+ */
884
+ /**
885
+ * The state a rubric reads: a JSON object, never text or an array. Every value must survive
886
+ * `JSON.stringify` unchanged (no functions or class instances), because it is sent as JSON.
887
+ * Typed as `object` so a state declared as an interface fits without an index signature.
888
+ */
889
+ declare type JevState = object;
890
+
552
891
  declare type JsonObject = {
553
892
  [key: string]: JsonValue_3;
554
893
  };
@@ -586,10 +925,18 @@ declare interface KayAgentEvents {
586
925
  publish(localId: string, payload: unknown): void;
587
926
  }
588
927
 
589
- /** Claim-checked filesystem operations. Paths are root-relative; canonical host paths never cross the seam. */
928
+ /**
929
+ * Claim-checked filesystem operations. Paths are root-relative; canonical host paths never cross
930
+ * the seam. Writes are atomic: readers see the old file or the new one, never a partial write. A
931
+ * replace keeps the file's mode but makes a new file, so owner and extended attributes are not
932
+ * carried over. On a volume without hard links an exclusive create can briefly show an empty file.
933
+ */
590
934
  declare interface KayAgentFilesystem {
591
935
  readText(location: KayAgentFilesystemLocation): Promise<string>;
592
- writeText(location: KayAgentFilesystemLocation, content: string): Promise<void>;
936
+ writeText(location: KayAgentFilesystemLocation, content: string, options?: KayAgentFilesystemWriteOptions): Promise<void>;
937
+ /** The whole file, or the requested window of it. */
938
+ readBytes(location: KayAgentFilesystemLocation, range?: KayAgentFilesystemByteRange): Promise<Uint8Array>;
939
+ writeBytes(location: KayAgentFilesystemLocation, bytes: Uint8Array, options?: KayAgentFilesystemWriteOptions): Promise<void>;
593
940
  mkdir(location: KayAgentFilesystemLocation): Promise<void>;
594
941
  readDir(location: KayAgentFilesystemLocation): Promise<readonly KayAgentFilesystemEntry[]>;
595
942
  stat(location: KayAgentFilesystemLocation): Promise<KayAgentFilesystemStat>;
@@ -598,6 +945,12 @@ declare interface KayAgentFilesystem {
598
945
  realpath(location: KayAgentFilesystemLocation): Promise<string>;
599
946
  }
600
947
 
948
+ /** A byte window of a file; `maxBytes` is capped by the host. */
949
+ declare interface KayAgentFilesystemByteRange {
950
+ readonly offset: number;
951
+ readonly maxBytes: number;
952
+ }
953
+
601
954
  declare interface KayAgentFilesystemEntry {
602
955
  readonly name: string;
603
956
  readonly kind: "file" | "directory" | "symlink" | "other";
@@ -615,6 +968,27 @@ declare interface KayAgentFilesystemStat {
615
968
  readonly modifiedAtMs: number;
616
969
  }
617
970
 
971
+ declare interface KayAgentFilesystemWriteOptions {
972
+ /** Fail with `already_exists` instead of replacing a file already at the location. */
973
+ readonly exclusive?: boolean;
974
+ }
975
+
976
+ declare interface KayAgentInteractionSurfaces {
977
+ /**
978
+ * Kay's built-in browser pane: the pages in the user's Bonsai tabs. The page-side credential
979
+ * guard runs on every act, so a driver over this surface may declare
980
+ * {@link INTERACTION_CREDENTIAL_GUARD}.
981
+ */
982
+ readonly browserPane: InteractionSurface;
983
+ /**
984
+ * The user's desktop through the platform's accessibility APIs: native apps as targets
985
+ * (`app://<bundle id>`), their windows' controls as elements. On macOS Kay drives it with its
986
+ * bundled accessibility worker under the app's Accessibility permission; on a platform with no
987
+ * backend it reports itself unavailable.
988
+ */
989
+ readonly desktop: InteractionSurface;
990
+ }
991
+
618
992
  /** One knowledge-index hit — a reference and within-list rank score, never a snippet. */
619
993
  declare interface KayAgentKnowledgeHit {
620
994
  readonly uri: string;
@@ -767,9 +1141,17 @@ declare interface KayAgentPluginApi {
767
1141
  * command it is about to run, and against the host's executable binding.
768
1142
  */
769
1143
  readonly process: KayAgentProcess;
1144
+ /**
1145
+ * Always present; the interaction surfaces Kay ships (its browser pane, and the desktop through
1146
+ * the platform accessibility APIs), each call checked against `interaction.surface {surface}`
1147
+ * and the session's permissions (see {@link InteractionSurface}).
1148
+ * Wrap one in an {@link InteractionDriver} and register it with
1149
+ * {@link KayAgentPluginApi.registerInteractionDriver} to make it drivable by the agent.
1150
+ */
1151
+ readonly interaction: KayAgentInteractionSurfaces;
770
1152
  /**
771
1153
  * Jev typed-decision inference, as an OPTIONAL accelerator with a REQUIRED fallback. Each call
772
- * is checked against `jev.use` — but a refused grant or a missing saved key both
1154
+ * is checked against `jev.use` — but a refused grant or no usable Jev provider both
773
1155
  * produce the same outcome: your `fallback` runs.
774
1156
  *
775
1157
  * Always present, on every host. That is the point: with no absent state to feature-detect,
@@ -798,6 +1180,14 @@ declare interface KayAgentPluginApi {
798
1180
  * from `ctx.auth`. A provider without a binding reuses inference credentials or needs none.
799
1181
  */
800
1182
  registerSearchProvider(provider: SearchProvider): void;
1183
+ /**
1184
+ * Register an Interaction driver (a backend behind the `interaction_*` tools: something the agent
1185
+ * can read, click, type into, and open). The engine owns the tools, element ids, run loop,
1186
+ * permission checks, and screenshots; the driver owns only its I/O. Declare the driver's `id`
1187
+ * under `contributes.interactionDrivers` in kay-plugin.json first. A `browser` driver must set
1188
+ * `credentialGuard` (see {@link INTERACTION_CREDENTIAL_GUARD}).
1189
+ */
1190
+ registerInteractionDriver(driver: InteractionDriver): void;
801
1191
  /**
802
1192
  * Register a content converter (turns raw bytes of a mime type into agent-facing content behind the
803
1193
  * read tool's finalize stage). Parallel to the kernel's `registerConverter`. Converters are
@@ -1000,7 +1390,7 @@ declare interface KayAgentWhiteboardUpdateInput {
1000
1390
 
1001
1391
  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";
1002
1392
 
1003
- 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";
1393
+ 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";
1004
1394
 
1005
1395
  declare interface LineRange {
1006
1396
  readonly start: number;
@@ -1051,29 +1441,26 @@ declare interface MistralConversationsCompat {
1051
1441
  supportsMidConvoSystemMessages?: boolean;
1052
1442
  }
1053
1443
 
1054
- declare interface Model<TApi extends Api> {
1055
- id: string;
1056
- name: string;
1057
- api: TApi;
1058
- provider: ProviderId;
1059
- baseUrl: string;
1444
+ /** Chat model: usable with `stream()` and friends. */
1445
+ declare interface Model<TApi extends Api> extends BaseModel<TApi> {
1446
+ /**
1447
+ * Optional: chat is the default model type, so models without `type` are chat
1448
+ * models. Narrow mixed model lists with `isModelType()` instead of comparing
1449
+ * `type` directly.
1450
+ */
1451
+ type?: "chat";
1060
1452
  reasoning: boolean;
1061
1453
  /**
1062
1454
  * Maps pi thinking levels to provider/model-specific values.
1063
1455
  * Missing keys use provider defaults. null marks a level as unsupported.
1064
1456
  */
1065
1457
  thinkingLevelMap?: ThinkingLevelMap;
1066
- input: ("text" | "image")[];
1067
- /** Provider input limits and cache-safe preprocessing metadata. */
1068
- inputLimits?: ModelInputLimits;
1069
- cost: ModelCost;
1070
1458
  /** Prompt cache lifetimes per retention tier. Unset when the provider's cache behavior is unknown. */
1071
1459
  promptCache?: ModelPromptCache;
1072
1460
  contextWindow: number;
1073
1461
  maxTokens: number;
1074
1462
  /** Default sampling parameters for this model. See {@link StreamOptions.samplingParams}; per-request keys override these. */
1075
1463
  samplingParams?: Record<string, unknown>;
1076
- headers?: Record<string, string>;
1077
1464
  /** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */
1078
1465
  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;
1079
1466
  }
@@ -1386,8 +1773,15 @@ declare interface PluginDelegatedScheme {
1386
1773
  /** Run the connect flow and mint the material to store. Throw to fail the connect. */
1387
1774
  readonly connect: (ctx: PluginDelegatedConnectContext) => Promise<PluginDelegatedConnectResult>;
1388
1775
  /**
1389
- * Mint fresh material from the current (expiring) material. Absent means non-refreshable:
1390
- * expired material resolves as-is (like an OAuth scheme without a refresh token).
1776
+ * Mint fresh material from the current (expiring) material, including the `data` the last
1777
+ * connect or refresh returned. Kay calls this only inside its credential authority, which holds
1778
+ * the refresh lease and saves the result; this hook performs the exchange and nothing else.
1779
+ * Returned `data` replaces the stored `data`; omitted `data` keeps it. Returned `expiresAt`
1780
+ * replaces the stored expiry; omitted `expiresAt` means the new material does not expire.
1781
+ * Throw {@link DelegatedRefreshRejectedError} when the provider refused the grant for good (the
1782
+ * user must reconnect); any other throw is retried with backoff and keeps the account connected.
1783
+ * Absent means non-refreshable: expired material resolves as-is (like an OAuth scheme without a
1784
+ * refresh token).
1391
1785
  */
1392
1786
  readonly refresh?: (material: PluginDelegatedSecretMaterial) => Promise<PluginDelegatedSecretMaterial>;
1393
1787
  /**
@@ -1498,6 +1892,58 @@ declare interface PluginServiceDescriptor {
1498
1892
  readonly capabilities: readonly PluginServiceCapability[];
1499
1893
  }
1500
1894
 
1895
+ /**
1896
+ * What a run did to a URI. The words match the automation run wire's artifact actions. When one
1897
+ * run both creates and updates a URI, the reader reports `created`.
1898
+ */
1899
+ declare type ProducedResourceAction = "created" | "updated";
1900
+
1901
+ /** One URI a tool call created or updated as a side effect. */
1902
+ declare interface ProducedResourceDeclaration {
1903
+ /** A `scheme://…` URI or an absolute local file path, compared as an exact string. */
1904
+ readonly uri: string;
1905
+ readonly action: ProducedResourceAction;
1906
+ }
1907
+
1908
+ /**
1909
+ * One tool result's declarations. `absent` covers every result that makes no claim, including a
1910
+ * non-object `details` and an empty list. `malformed` means a claim was made that cannot be
1911
+ * trusted; the reader reports a gap for it instead of keeping the parts that look valid.
1912
+ */
1913
+ export declare type ProducedResourceDeclarationsRead = {
1914
+ readonly kind: "absent";
1915
+ } | {
1916
+ readonly kind: "malformed";
1917
+ } | {
1918
+ readonly kind: "declared";
1919
+ readonly resources: readonly ProducedResourceDeclaration[];
1920
+ };
1921
+
1922
+ /**
1923
+ * What the automation run reader will see for one tool result's produced-resource declarations:
1924
+ * the result's details recorded by the same host step a session applies before persisting it,
1925
+ * then read through the `@herd/uri` contract. `declared` lists what `declareProducedResources`
1926
+ * attached, in order; `absent` means the result claims nothing, including a result whose own
1927
+ * details set the reserved key by hand, which the host drops.
1928
+ *
1929
+ * Stricter than the host on purpose: a declaration the contract refuses does not fail the tool
1930
+ * call in a session (its effect already happened; the run reports a gap instead), so a test is
1931
+ * where its author finds out.
1932
+ *
1933
+ * ```ts
1934
+ * const result = await invokeToolForTest(createPageTool, { title: "Plan" });
1935
+ * expect(producedResourcesForTest(result)).toEqual({
1936
+ * kind: "declared",
1937
+ * resources: [{ uri: "notion://pages/abc123", action: "created" }],
1938
+ * });
1939
+ * ```
1940
+ *
1941
+ * @throws TypeError when the result declares something the contract refuses, naming the claim.
1942
+ */
1943
+ export declare function producedResourcesForTest(result: AgentToolResult<unknown>): Exclude<ProducedResourceDeclarationsRead, {
1944
+ readonly kind: "malformed";
1945
+ }>;
1946
+
1501
1947
  declare type ProviderId = KnownProvider | string;
1502
1948
 
1503
1949
  /**
@@ -1944,6 +2390,19 @@ declare interface ToolCallModel {
1944
2390
  */
1945
2391
  declare type ToolCallOrigin = "model" | "host";
1946
2392
 
2393
+ /** A call to judge with {@link ToolContext.checkToolCallPermission}. */
2394
+ declare interface ToolCallPermissionCheck {
2395
+ readonly toolName: string;
2396
+ readonly input: Record<string, unknown>;
2397
+ }
2398
+
2399
+ declare type ToolCallPermissionVerdict = {
2400
+ readonly allowed: true;
2401
+ } | {
2402
+ readonly allowed: false;
2403
+ readonly reason: string;
2404
+ };
2405
+
1947
2406
  /**
1948
2407
  * What a tool call ADDRESSES, resolved by the session before execution from facts it already
1949
2408
  * holds for auth scoping: a URI verb's matched module (and the extension that registered it), or
@@ -1986,6 +2445,13 @@ declare interface ToolContext {
1986
2445
  * behind itself. Present only with a live session. One request may be pending at a time.
1987
2446
  */
1988
2447
  readonly requestMidTurnCompaction?: (customInstructions?: string, trigger?: "command" | "suggestion") => MidTurnCompactionRequest;
2448
+ /**
2449
+ * Put a call this tool is about to cause through the session's permission gate, as if the agent
2450
+ * had made it directly: classification, rules, mode, the auto approver, and the approval card.
2451
+ * The call is only judged, never executed. For a tool whose effect is only knowable at run time
2452
+ * (a click in a web page that turns out to send an email). Present only with a live session.
2453
+ */
2454
+ readonly checkToolCallPermission?: (call: ToolCallPermissionCheck) => Promise<ToolCallPermissionVerdict>;
1989
2455
  /**
1990
2456
  * Re-root the session into `newCwd` mid-run, recomposing the cwd-scoped substrate (builtin tools,
1991
2457
  * the capability registry, the system-prompt project line) in place so subsequent tool calls operate
@@ -2555,6 +3021,21 @@ declare interface UriModule {
2555
3021
  */
2556
3022
  readonly title?: string;
2557
3023
  readonly fallback?: boolean;
3024
+ /**
3025
+ * Whether this module parses the read tool's trailing line selector out of its own addresses.
3026
+ * Absent or false: the host owns the explicit forms (`:50-200`, `:50-`, `:50+10`, `:raw`). The
3027
+ * read tool strips one before dispatch, hands `read` the bare address, and applies the window
3028
+ * to what comes back, so a module never has to know the grammar for the read tool's own
3029
+ * `Continue with '<address>:N-M'` banner to work on its addresses (#10754). A bare trailing
3030
+ * `:<digits>` is never taken from such a module: it is too common inside real ids (a Figma
3031
+ * node `1:2`), so it reaches the module as written.
3032
+ *
3033
+ * Declare `true` only when the module must see the raw address: its grammar can end in an
3034
+ * explicit-range shape, it wants the bare `:50` form too, or it windows the content itself
3035
+ * (streaming a large file, skipping a cap it applies to an unwindowed read). Such a module
3036
+ * calls `parseLineSelector` itself and reports what it did through `RawResource.selector`.
3037
+ */
3038
+ readonly ownsLineSelectors?: boolean;
2558
3039
  readonly claims?: readonly UriClaim[];
2559
3040
  /** Migration-only prompt summary until structured help/catalog coverage replaces prose. */
2560
3041
  readonly description?: string;
@@ -2571,7 +3052,9 @@ declare interface UriModule {
2571
3052
  * The host owns framing, the whole-quote fit (no elision) and display-safety admission.
2572
3053
  *
2573
3054
  * Unlike preflight, this hook is async and may do I/O. Resolve names through `ctx`,
2574
- * the same dispatch context used by execution, never an ambient or cached account.
3055
+ * the same dispatch context used by execution, never an ambient or cached account. A name
3056
+ * the module resolved under that account may be cached for a bounded time, keyed by that
3057
+ * account's identity and never answered to another account or after a failed lookup.
2575
3058
  * The host calls it once before raising the ask, enforces the short `ctx.signal`
2576
3059
  * deadline and discards late answers. A throw yields no summary and a module-attributed
2577
3060
  * diagnostic; declining costs a click into the app, never an under-described approval.
@@ -2581,6 +3064,26 @@ declare interface UriModule {
2581
3064
  readonly uri: string;
2582
3065
  readonly payload: unknown;
2583
3066
  }, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
3067
+ /**
3068
+ * A short, render-only name for the resource a settled call on `request.uri` touched, which the
3069
+ * host stamps on the tool result's `details` so a transcript row can name it where it would
3070
+ * otherwise print the address (`#lane-analytics` and `thread`, not a channel id). Answer
3071
+ * `undefined` for any address you cannot honestly name.
3072
+ *
3073
+ * Called once per successful URI verb call, AFTER the verb handler has returned, with the same
3074
+ * dispatch context. Resolve names through `ctx` and nothing else, and prefer a cache your own
3075
+ * verb handlers warm, keyed by the executing account: a name one account saw is never answered
3076
+ * to another, and a failed, blank or aborted lookup is never cached. The host bounds the wait
3077
+ * (a finished result is held at most a quarter of a second), gives `ctx.signal` a hard deadline,
3078
+ * and drops a late, thrown or refused answer without failing the call; a throw is reported as a
3079
+ * module-attributed diagnostic. The label is a label, never an identity: the address remains the
3080
+ * click target, hover text and accessible name. It is UI-only, so it never enters the tool
3081
+ * result's content and no telemetry reads it.
3082
+ *
3083
+ * Unlike `describeOperation` this is not an approval surface: it carries no consequence, no
3084
+ * quote and no coverage claim, and nothing may gate a permission on it.
3085
+ */
3086
+ labelResource?(request: UriResourceLabelRequest, ctx: UriResolveContext): Promise<UriResourceLabel | undefined>;
2584
3087
  /**
2585
3088
  * Per-resource observability: does a `format: "observation"` read of THIS
2586
3089
  * uri render source-native material ({@link UriResolveContext.format})?
@@ -2964,6 +3467,24 @@ declare interface UriResourceDescription {
2964
3467
  readonly sourceUrl?: string;
2965
3468
  }
2966
3469
 
3470
+ /**
3471
+ * What a settled call touched, as a person would name it.
3472
+ *
3473
+ * `name` is the resource itself (`#lane-analytics`). `section` is the part of it the address
3474
+ * reaches into (`thread`), and is absent when the address is the resource.
3475
+ */
3476
+ declare interface UriResourceLabel {
3477
+ readonly name: string;
3478
+ readonly section?: string;
3479
+ }
3480
+
3481
+ /** The call a module is asked to label. */
3482
+ declare interface UriResourceLabelRequest {
3483
+ readonly operation: UriOperation;
3484
+ /** The dispatch address: selector-stripped, the exact form the module's verb handler received. */
3485
+ readonly uri: string;
3486
+ }
3487
+
2967
3488
  declare interface UriSchemeClaim {
2968
3489
  readonly kind: "scheme";
2969
3490
  readonly scheme: string;
@@ -3280,8 +3801,8 @@ declare type WhiteboardValue = z.infer<typeof WhiteboardValue>;
3280
3801
  /**
3281
3802
  * Run `use` against a live Jev client, falling back to `fallback` whenever Jev cannot serve the
3282
3803
  * call. The wrapper exists so a feature can ASK for a typed decision without ever depending on
3283
- * one: `fallback` is required, and it runs whenever no host-managed key is saved,
3284
- * the user has not granted `jev.use`, the branch exceeds its budget, `use` throws, or
3804
+ * one: `fallback` is required, and it runs whenever no provider in the user's Jev provider
3805
+ * order holds a usable credential, the user has not granted `jev.use`, the branch exceeds its budget, `use` throws, or
3285
3806
  * `use` declines by returning `undefined`. With valid options, this rejects on an explicit
3286
3807
  * `signal` abort, reaching `deadlineAt`, or `fallback` itself throwing.
3287
3808
  *
@@ -3289,10 +3810,9 @@ declare type WhiteboardValue = z.infer<typeof WhiteboardValue>;
3289
3810
  * {@link createFallbackOnlyWithJev}, so a caller never branches on availability — branching is
3290
3811
  * exactly how a fallback gets skipped.
3291
3812
  *
3292
- * `jev` is the vendor's own `TypeSafeClient`, configured with the host's default model and base
3293
- * URL. Native `request.model` overrides remain supported. It
3294
- * is valid only for the duration of the callback: a reference retained past `use`'s scope is
3295
- * refused at the transport, and an in-flight request is aborted when the call stops waiting.
3813
+ * `jev` is a {@link JevClient} on the host's Jev model. It is valid only for the duration of
3814
+ * the callback: a reference retained past `use`'s scope is refused at the transport, and an
3815
+ * in-flight request is aborted when the call stops waiting.
3296
3816
  * The callback itself is ABANDONED, not interrupted — a host cannot stop arbitrary JavaScript
3297
3817
  * that is already running, so `use` must not perform side effects it would mind completing
3298
3818
  * unobserved. The same applies to a running fallback: abort/deadline stops waiting for it but
@@ -3301,7 +3821,7 @@ declare type WhiteboardValue = z.infer<typeof WhiteboardValue>;
3301
3821
  declare type WithJev = <T>(
3302
3822
  /** Return `undefined` to decline (the fallback runs). A feature whose real result can itself be
3303
3823
  * `undefined` should return `null` or its own wrapper type instead. */
3304
- use: (jev: TypeSafeClient) => T | undefined | PromiseLike<T | undefined>,
3824
+ use: (jev: JevClient) => T | undefined | PromiseLike<T | undefined>,
3305
3825
  /** Required. The behaviour the feature has without Jev. */
3306
3826
  fallback: () => T | PromiseLike<T>, options?: WithJevOptions) => Promise<T>;
3307
3827