@yaklabs-ai/plugin-sdk 0.55.1 → 0.55.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@ it under the name your plugin code actually imports:
13
13
  ```json
14
14
  {
15
15
  "dependencies": {
16
- "@bonsai/plugin-sdk": "npm:@yaklabs-ai/plugin-sdk@^0.55.1"
16
+ "@bonsai/plugin-sdk": "npm:@yaklabs-ai/plugin-sdk@^0.55.3"
17
17
  }
18
18
  }
19
19
  ```
@@ -62,7 +62,7 @@ Every tool the entrypoint registers is declared in the manifest too:
62
62
  "description": "Greets people by name.",
63
63
  "version": "0.1.0",
64
64
  "namespace": "widgets",
65
- "compatibility": { "sdk": "^0.55.1" },
65
+ "compatibility": { "sdk": "^0.55.3" },
66
66
  "targets": {
67
67
  "kay": {
68
68
  "agent": {
@@ -153,7 +153,7 @@ naming both versions, instead of loading it and finding no `activate`.
153
153
  | `@bonsai/plugin-sdk/system-one` | The types and error class for `api.systemOne` / `app.systemOne`, from 0.13.8 |
154
154
  | `@bonsai/plugin-sdk/zod` | `z` (the host's in the daemon), on an entry with no other import, from 0.13.9 |
155
155
 
156
- A plugin that imports these subpaths declares the current supported SDK range (`^0.55.1`).
156
+ A plugin that imports these subpaths declares the current supported SDK range (`^0.55.3`).
157
157
  The rows record when each subpath appeared, not retained runtime compatibility. `vendored-mcp` builds the plugin's URI module (its link cards and help); hosting the
158
158
  vendor's MCP server itself is a separate host decision: Bonsai hosts it once a person approves
159
159
  the plugin's `mcp.host-server` grant for that server's name and URL. A server that needs an OAuth
@@ -161,7 +161,7 @@ sign-in names one of the plugin's auth services in `authService`; that service m
161
161
  one `delegated` scheme, which Bonsai uses to sign the person in to the server. Bonsai declares that
162
162
  scheme at runtime; the plugin's code must not declare it again. From 0.16.2 a plugin's code can also
163
163
  call the server's tools through `api.vendoredMcp.port(<server>)`, typed by this subpath's
164
- `VendoredMcpPort` and `VendoredMcpOutcome` (declare `^0.55.1`).
164
+ `VendoredMcpPort` and `VendoredMcpOutcome` (declare `^0.55.3`).
165
165
 
166
166
  From 0.13.10 the manifest schema carries two facts Bonsai reads for its own plugins only; another
167
167
  plugin's are ignored, with a load diagnostic. An `external-cli` auth scheme's `cli` says how Bonsai
@@ -243,7 +243,7 @@ Provider schemas remain authoritative; declaring a dependency grants no authorit
243
243
  through `app.uri`, parse returned Markdown in the consuming plugin, and retain the read's
244
244
  revision (or `contentDigest` from `/uri`) for edits. The old `memory.read` and `memory.write`
245
245
  grants do not authorize the replacement scheme-and-verb calls. Contracts before 0.49.0 are
246
- refused; migrate and declare `^0.55.1`. The current host also serves the 0.49.0 resource contract.
246
+ refused; migrate and declare `^0.55.3`. The current host also serves the 0.49.0 resource contract.
247
247
 
248
248
  ## What this package is, and is not, at runtime
249
249
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "@yaklabs-ai/plugin-sdk",
4
- "version": "0.55.1",
4
+ "version": "0.55.3",
5
5
  "target": "npm",
6
6
  "firstPartyLicense": "MIT",
7
7
  "bundled": [
@@ -1723,7 +1723,7 @@ declare type InteractionSurface = Pick<InteractionDriver, "isAvailable" | "targe
1723
1723
 
1724
1724
  /**
1725
1725
  * One thing a driver can be pointed at: a browser page, a running app. `id` is the driver-owned
1726
- * address the agent passes back as `target` (`kay://tabs/.../items/<item>`, `app://<bundle id>`).
1726
+ * address the agent passes back as `target` (`bonsai://tabs/.../items/<item>`, `app://<bundle id>`).
1727
1727
  * `active` marks the default target (the page the user is looking at, the front app).
1728
1728
  */
1729
1729
  declare const InteractionTarget: z.ZodObject<{
@@ -2680,8 +2680,15 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
2680
2680
  * 0.55.1 adds canonical path resources on declared tool permissions, asynchronous display-only
2681
2681
  * tool previews with cancellation, app-scoped availability snapshots and app-target output
2682
2682
  * artifact usage declarations. A plugin using these declares ^0.55.1.
2683
+ *
2684
+ * 0.55.2 adds opt-in query and connection-selector preservation during URI claim normalization.
2685
+ *
2686
+ * 0.55.3 lets a signal carry the item's own text. `PluginSignal` gains an optional `body`: plain
2687
+ * text, at most `PLUGIN_SIGNAL_BODY_MAX` (20,000) characters. Gmail puts a mail's text there, so
2688
+ * Flows can keep the messages a task came from. Additive: a plugin that publishes a body declares
2689
+ * ^0.55.3.
2683
2690
  */
2684
- declare const KAY_PLUGIN_SDK_VERSION: "0.55.1";
2691
+ declare const KAY_PLUGIN_SDK_VERSION: "0.55.3";
2685
2692
 
2686
2693
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2687
2694
 
@@ -10225,7 +10232,7 @@ declare interface UriParameter {
10225
10232
  /**
10226
10233
  * A claim on a module's own custom-scheme addresses. The pattern matches with or without an
10227
10234
  * account prefix: `gcal://@work/events/x` is matched as `gcal://events/x`, and the prefix is put
10228
- * back on the official spelling, so one claim covers both spellings.
10235
+ * back on the same-scheme official spelling, so one claim covers both spellings.
10229
10236
  */
10230
10237
  declare interface UriPatternClaim {
10231
10238
  readonly kind: "uriPattern";
@@ -10236,9 +10243,13 @@ declare interface UriPatternClaim {
10236
10243
  * carried over verbatim so a route flag such as `?permanent=true` survives
10237
10244
  * canonicalization (kay#7859); a template that re-targets to another scheme
10238
10245
  * (a projection to a web link) drops it, as does a pattern that consumed the
10239
- * query itself or a template that spells its own.
10246
+ * query itself or a template that spells its own. `preserveQuery` opts out of those defaults.
10240
10247
  */
10241
10248
  readonly normalizeTo?: UriClaimTemplate;
10249
+ /** Carry the matched query verbatim across normalization; incompatible with a target query. */
10250
+ readonly preserveQuery?: true;
10251
+ /** Carry the input's account selector even when normalization changes scheme. */
10252
+ readonly preserveConnection?: true;
10242
10253
  /**
10243
10254
  * Names the route this claim matches, so data keyed by route (a module's sign-in action rules)
10244
10255
  * can say which addresses it means. Several claims may name the same route.
@@ -1759,7 +1759,7 @@ declare type InteractionSurface = Pick<InteractionDriver, "isAvailable" | "targe
1759
1759
 
1760
1760
  /**
1761
1761
  * One thing a driver can be pointed at: a browser page, a running app. `id` is the driver-owned
1762
- * address the agent passes back as `target` (`kay://tabs/.../items/<item>`, `app://<bundle id>`).
1762
+ * address the agent passes back as `target` (`bonsai://tabs/.../items/<item>`, `app://<bundle id>`).
1763
1763
  * `active` marks the default target (the page the user is looking at, the front app).
1764
1764
  */
1765
1765
  declare const InteractionTarget: z.ZodObject<{
@@ -2734,8 +2734,15 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
2734
2734
  * 0.55.1 adds canonical path resources on declared tool permissions, asynchronous display-only
2735
2735
  * tool previews with cancellation, app-scoped availability snapshots and app-target output
2736
2736
  * artifact usage declarations. A plugin using these declares ^0.55.1.
2737
+ *
2738
+ * 0.55.2 adds opt-in query and connection-selector preservation during URI claim normalization.
2739
+ *
2740
+ * 0.55.3 lets a signal carry the item's own text. `PluginSignal` gains an optional `body`: plain
2741
+ * text, at most `PLUGIN_SIGNAL_BODY_MAX` (20,000) characters. Gmail puts a mail's text there, so
2742
+ * Flows can keep the messages a task came from. Additive: a plugin that publishes a body declares
2743
+ * ^0.55.3.
2737
2744
  */
2738
- declare const KAY_PLUGIN_SDK_VERSION: "0.55.1";
2745
+ declare const KAY_PLUGIN_SDK_VERSION: "0.55.3";
2739
2746
 
2740
2747
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2741
2748
 
@@ -5763,6 +5770,8 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
5763
5770
  search: z.ZodExactOptional<z.ZodString>;
5764
5771
  }, z.core.$strict>;
5765
5772
  normalizeTo: z.ZodExactOptional<z.ZodString>;
5773
+ preserveQuery: z.ZodExactOptional<z.ZodLiteral<true>>;
5774
+ preserveConnection: z.ZodExactOptional<z.ZodLiteral<true>>;
5766
5775
  route: z.ZodExactOptional<z.ZodString>;
5767
5776
  sourceUrl: z.ZodExactOptional<z.ZodString>;
5768
5777
  title: z.ZodExactOptional<z.ZodString>;
@@ -6049,6 +6058,8 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
6049
6058
  search: z.ZodExactOptional<z.ZodString>;
6050
6059
  }, z.core.$strict>;
6051
6060
  normalizeTo: z.ZodExactOptional<z.ZodString>;
6061
+ preserveQuery: z.ZodExactOptional<z.ZodLiteral<true>>;
6062
+ preserveConnection: z.ZodExactOptional<z.ZodLiteral<true>>;
6052
6063
  route: z.ZodExactOptional<z.ZodString>;
6053
6064
  sourceUrl: z.ZodExactOptional<z.ZodString>;
6054
6065
  title: z.ZodExactOptional<z.ZodString>;
@@ -11849,7 +11860,7 @@ declare interface UriParameter {
11849
11860
  /**
11850
11861
  * A claim on a module's own custom-scheme addresses. The pattern matches with or without an
11851
11862
  * account prefix: `gcal://@work/events/x` is matched as `gcal://events/x`, and the prefix is put
11852
- * back on the official spelling, so one claim covers both spellings.
11863
+ * back on the same-scheme official spelling, so one claim covers both spellings.
11853
11864
  */
11854
11865
  declare interface UriPatternClaim {
11855
11866
  readonly kind: "uriPattern";
@@ -11860,9 +11871,13 @@ declare interface UriPatternClaim {
11860
11871
  * carried over verbatim so a route flag such as `?permanent=true` survives
11861
11872
  * canonicalization (kay#7859); a template that re-targets to another scheme
11862
11873
  * (a projection to a web link) drops it, as does a pattern that consumed the
11863
- * query itself or a template that spells its own.
11874
+ * query itself or a template that spells its own. `preserveQuery` opts out of those defaults.
11864
11875
  */
11865
11876
  readonly normalizeTo?: UriClaimTemplate;
11877
+ /** Carry the matched query verbatim across normalization; incompatible with a target query. */
11878
+ readonly preserveQuery?: true;
11879
+ /** Carry the input's account selector even when normalization changes scheme. */
11880
+ readonly preserveConnection?: true;
11866
11881
  /**
11867
11882
  * Names the route this claim matches, so data keyed by route (a module's sign-in action rules)
11868
11883
  * can say which addresses it means. Several claims may name the same route.
@@ -2107,7 +2107,7 @@ export declare type InteractionTarget = ReadonlyContract<InteractionTarget_2>;
2107
2107
 
2108
2108
  /**
2109
2109
  * One thing a driver can be pointed at: a browser page, a running app. `id` is the driver-owned
2110
- * address the agent passes back as `target` (`kay://tabs/.../items/<item>`, `app://<bundle id>`).
2110
+ * address the agent passes back as `target` (`bonsai://tabs/.../items/<item>`, `app://<bundle id>`).
2111
2111
  * `active` marks the default target (the page the user is looking at, the front app).
2112
2112
  */
2113
2113
  declare const InteractionTarget_2: z.ZodObject<{
@@ -3088,8 +3088,15 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
3088
3088
  * 0.55.1 adds canonical path resources on declared tool permissions, asynchronous display-only
3089
3089
  * tool previews with cancellation, app-scoped availability snapshots and app-target output
3090
3090
  * artifact usage declarations. A plugin using these declares ^0.55.1.
3091
+ *
3092
+ * 0.55.2 adds opt-in query and connection-selector preservation during URI claim normalization.
3093
+ *
3094
+ * 0.55.3 lets a signal carry the item's own text. `PluginSignal` gains an optional `body`: plain
3095
+ * text, at most `PLUGIN_SIGNAL_BODY_MAX` (20,000) characters. Gmail puts a mail's text there, so
3096
+ * Flows can keep the messages a task came from. Additive: a plugin that publishes a body declares
3097
+ * ^0.55.3.
3091
3098
  */
3092
- declare const KAY_PLUGIN_SDK_VERSION: "0.55.1";
3099
+ declare const KAY_PLUGIN_SDK_VERSION: "0.55.3";
3093
3100
 
3094
3101
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
3095
3102
 
@@ -11753,7 +11760,7 @@ export declare interface UriParameterOptions {
11753
11760
  /**
11754
11761
  * A claim on a module's own custom-scheme addresses. The pattern matches with or without an
11755
11762
  * account prefix: `gcal://@work/events/x` is matched as `gcal://events/x`, and the prefix is put
11756
- * back on the official spelling, so one claim covers both spellings.
11763
+ * back on the same-scheme official spelling, so one claim covers both spellings.
11757
11764
  */
11758
11765
  declare interface UriPatternClaim {
11759
11766
  readonly kind: "uriPattern";
@@ -11764,9 +11771,13 @@ declare interface UriPatternClaim {
11764
11771
  * carried over verbatim so a route flag such as `?permanent=true` survives
11765
11772
  * canonicalization (kay#7859); a template that re-targets to another scheme
11766
11773
  * (a projection to a web link) drops it, as does a pattern that consumed the
11767
- * query itself or a template that spells its own.
11774
+ * query itself or a template that spells its own. `preserveQuery` opts out of those defaults.
11768
11775
  */
11769
11776
  readonly normalizeTo?: UriClaimTemplate;
11777
+ /** Carry the matched query verbatim across normalization; incompatible with a target query. */
11778
+ readonly preserveQuery?: true;
11779
+ /** Carry the input's account selector even when normalization changes scheme. */
11780
+ readonly preserveConnection?: true;
11770
11781
  /**
11771
11782
  * Names the route this claim matches, so data keyed by route (a module's sign-in action rules)
11772
11783
  * can say which addresses it means. Several claims may name the same route.
package/dist/kay-agent.js CHANGED
@@ -37,6 +37,9 @@ function compileClaim(claim) {
37
37
  }
38
38
  };
39
39
  }
40
+ if (claim.kind === "uriPattern" && claim.preserveQuery === true && claim.normalizeTo?.split("#", 1)[0]?.includes("?") === true) {
41
+ throw new Error("URI claim preserveQuery cannot use normalizeTo with a query.");
42
+ }
40
43
  const pattern = compileRoutePattern(claim.pattern);
41
44
  const normalizeTo = claim.normalizeTo === void 0 ? void 0 : compileTemplate(claim.normalizeTo);
42
45
  const sourceUrl = claim.kind === "uriPattern" && claim.sourceUrl !== void 0 ? compileTemplate(claim.sourceUrl) : void 0;
@@ -55,7 +58,7 @@ function compileClaim(claim) {
55
58
  claimKind: claim.kind,
56
59
  ...Object.keys(captures).length > 0 ? { captures } : {},
57
60
  ...normalized !== void 0 ? {
58
- normalizedUri: withSelector(withInputSearch(claim, pattern, parsed, normalized), parsed.protocol, selector)
61
+ normalizedUri: withSelector(withInputSearch(claim, pattern, parsed, normalized), parsed.protocol, selector, claim.kind === "uriPattern" && claim.preserveConnection === true)
59
62
  } : {},
60
63
  ...claim.route !== void 0 ? { route: claim.route } : {},
61
64
  ...filledSourceUrl !== void 0 ? { sourceUrl: filledSourceUrl } : {},
@@ -88,11 +91,13 @@ function parseUriConnectionSelector(input) {
88
91
  }
89
92
  return { selector: match[2], rest: `${match[1]}${match[3] ?? ""}` };
90
93
  }
91
- function withSelector(normalized, inputProtocol, selector) {
94
+ function withSelector(normalized, inputProtocol, selector, preserveConnection) {
92
95
  if (selector === void 0)
93
96
  return normalized;
94
- if (templateProtocol(normalized) !== inputProtocol)
97
+ const targetProtocol = templateProtocol(normalized);
98
+ if (targetProtocol === void 0 || !preserveConnection && targetProtocol !== inputProtocol) {
95
99
  return normalized;
100
+ }
96
101
  const schemeEnd = normalized.indexOf("://") + 3;
97
102
  return `${normalized.slice(0, schemeEnd)}@${selector}/${normalized.slice(schemeEnd)}`;
98
103
  }
@@ -253,7 +258,14 @@ function safeDecode(value) {
253
258
  }
254
259
  }
255
260
  function withInputSearch(claim, pattern, input, normalized) {
256
- if (claim.kind !== "uriPattern" || claim.normalizeTo === void 0 || pattern.search !== void 0 || input.search.length === 0 || claim.normalizeTo.includes("?") || templateProtocol(claim.normalizeTo) !== input.protocol) {
261
+ if (claim.kind !== "uriPattern" || claim.normalizeTo === void 0 || input.search.length === 0) {
262
+ return normalized;
263
+ }
264
+ if (claim.preserveQuery === true) {
265
+ const fragment = normalized.indexOf("#");
266
+ return fragment === -1 ? `${normalized}?${input.search}` : `${normalized.slice(0, fragment)}?${input.search}${normalized.slice(fragment)}`;
267
+ }
268
+ if (pattern.search !== void 0 || claim.normalizeTo.includes("?") || templateProtocol(claim.normalizeTo) !== input.protocol) {
257
269
  return normalized;
258
270
  }
259
271
  return `${normalized}?${input.search}`;
@@ -2230,6 +2230,14 @@ declare interface KayAppSessions {
2230
2230
  * against `sessions.read`. Unsupported hosts may omit it.
2231
2231
  */
2232
2232
  listSessionPage?(request: KayAppSessionListRequest): Promise<KayAppSessionList>;
2233
+ /**
2234
+ * Called after a stored session's index row changes (its title, folder or project, or its
2235
+ * removal); a change without `sessionId` means any session may have changed. Checked against
2236
+ * `sessions.read`. Returns the unsubscribe. Unsupported hosts may omit it.
2237
+ */
2238
+ subscribeChanges?(listener: (change: {
2239
+ readonly sessionId?: string;
2240
+ }) => void): () => void;
2233
2241
  /**
2234
2242
  * Read a cheap transcript change marker without loading the entry log. Unknown or deleted
2235
2243
  * sessions yield `undefined`.
package/dist/kay-app.d.ts CHANGED
@@ -4390,8 +4390,15 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
4390
4390
  * 0.55.1 adds canonical path resources on declared tool permissions, asynchronous display-only
4391
4391
  * tool previews with cancellation, app-scoped availability snapshots and app-target output
4392
4392
  * artifact usage declarations. A plugin using these declares ^0.55.1.
4393
+ *
4394
+ * 0.55.2 adds opt-in query and connection-selector preservation during URI claim normalization.
4395
+ *
4396
+ * 0.55.3 lets a signal carry the item's own text. `PluginSignal` gains an optional `body`: plain
4397
+ * text, at most `PLUGIN_SIGNAL_BODY_MAX` (20,000) characters. Gmail puts a mail's text there, so
4398
+ * Flows can keep the messages a task came from. Additive: a plugin that publishes a body declares
4399
+ * ^0.55.3.
4393
4400
  */
4394
- declare const KAY_PLUGIN_SDK_VERSION: "0.55.1";
4401
+ declare const KAY_PLUGIN_SDK_VERSION: "0.55.3";
4395
4402
 
4396
4403
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
4397
4404
 
@@ -6142,6 +6149,8 @@ export declare interface KayAppKnowledgeSearchOptions {
6142
6149
  readonly limit?: number;
6143
6150
  /** Treat the last query term as a prefix, for search as the person types. */
6144
6151
  readonly prefixLastTerm?: boolean;
6152
+ /** Skip vector eligibility and query embedding; false or absent keeps hybrid search. */
6153
+ readonly lexicalOnly?: boolean;
6145
6154
  /** Drop vector-leg hits below this cosine similarity. */
6146
6155
  readonly minCosineSimilarity?: number;
6147
6156
  /** Relative weights of the lexical and vector legs in rank fusion. */
@@ -6977,8 +6986,19 @@ export declare interface KayAppPluginApi {
6977
6986
  implementWorkflow(localId: string, run: KayAppWorkflowImplementation): void;
6978
6987
  }
6979
6988
 
6989
+ /** What `kay.app` code may add to one publish beyond the event itself. */
6990
+ export declare interface KayAppPluginEventPublishOptions {
6991
+ /**
6992
+ * A session this plugin created through `sessions.create`: the host records it as the event's
6993
+ * session, so a notification rule's `session-unfocused` stays quiet while that session is the
6994
+ * focused pane. The host refuses a session this plugin did not create. Absent publishes an
6995
+ * app-lifetime event, as before.
6996
+ */
6997
+ readonly sessionId?: string;
6998
+ }
6999
+
6980
7000
  export declare interface KayAppPluginEvents {
6981
- publish(localId: string, input: PluginEventPublishInput): void;
7001
+ publish(localId: string, input: PluginEventPublishInput, options?: KayAppPluginEventPublishOptions): void;
6982
7002
  }
6983
7003
 
6984
7004
  /**
@@ -7923,6 +7943,14 @@ export declare interface KayAppSessions {
7923
7943
  * against `sessions.read`. Unsupported hosts may omit it.
7924
7944
  */
7925
7945
  listSessionPage?(request: KayAppSessionListRequest): Promise<KayAppSessionList>;
7946
+ /**
7947
+ * Called after a stored session's index row changes (its title, folder or project, or its
7948
+ * removal); a change without `sessionId` means any session may have changed. Checked against
7949
+ * `sessions.read`. Returns the unsubscribe. Unsupported hosts may omit it.
7950
+ */
7951
+ subscribeChanges?(listener: (change: {
7952
+ readonly sessionId?: string;
7953
+ }) => void): () => void;
7926
7954
  /**
7927
7955
  * Read a cheap transcript change marker without loading the entry log. Unknown or deleted
7928
7956
  * sessions yield `undefined`.
@@ -10051,13 +10079,20 @@ export declare interface KayAppWorkingDocuments {
10051
10079
  * recovery data.
10052
10080
  */
10053
10081
  export declare interface KayAppWorkspace {
10082
+ /**
10083
+ * Called after the window publishes a new snapshot (tabs, groups, panes or titles changed). Read
10084
+ * `snapshot()` for the new state. Returns the unsubscribe.
10085
+ */
10086
+ subscribe(listener: () => void): () => void;
10054
10087
  snapshot(): Promise<{
10055
10088
  readonly kind: "snapshot";
10056
10089
  readonly snapshot: KayAppWorkspaceSnapshot;
10057
10090
  } | {
10058
10091
  readonly kind: "unavailable";
10059
10092
  }>;
10060
- execute(command: KayAppWorkspaceCommand, ctx: KayAppWorkspaceExecuteContext): Promise<{
10093
+ execute(command: KayAppWorkspaceCommand,
10094
+ /** The session the command is issued for; absent for a plugin's own background work. */
10095
+ ctx?: KayAppWorkspaceExecuteContext): Promise<{
10061
10096
  readonly kind: "outcome";
10062
10097
  readonly outcome: KayAppWorkspaceCommandOutcome;
10063
10098
  readonly validPageIds?: readonly string[];
@@ -10101,6 +10136,23 @@ export declare type KayAppWorkspaceCommand = {
10101
10136
  } | {
10102
10137
  verb: "tabs.ungroup";
10103
10138
  tabIds: readonly string[];
10139
+ } | {
10140
+ /**
10141
+ * Set the membership and look of this plugin's own linked tab groups, computed from the
10142
+ * snapshot numbered `rev` (`stale` when the tabs changed since). Each listed group ends up
10143
+ * holding exactly `tabIds` plus its guests, created when missing; only ungrouped Tabs join.
10144
+ * One of this plugin's groups left unlisted keeps only its guests. Never recorded as the
10145
+ * person's own edit.
10146
+ */
10147
+ verb: "tabs.syncLinkedGroups";
10148
+ rev: number;
10149
+ groups: readonly {
10150
+ readonly key: string;
10151
+ readonly name: string;
10152
+ readonly color?: KayAppWorkspaceTabGroupColor | undefined;
10153
+ readonly icon?: string | undefined;
10154
+ readonly tabIds: readonly string[];
10155
+ }[];
10104
10156
  } | {
10105
10157
  verb: "panes.split";
10106
10158
  tabId: string;
@@ -10259,15 +10311,21 @@ export declare type KayAppWorkspacePlaceContent = {
10259
10311
  };
10260
10312
 
10261
10313
  /** The full workspace tree, app-wide (every tab, not one session's). */
10262
- export declare interface KayAppWorkspaceSnapshot {
10263
- readonly rev: number;
10264
- readonly activeTabId: string;
10265
- readonly tabs: readonly KayAppWorkspaceTabSnapshot[];
10266
- }
10314
+ /**
10315
+ * The workspace as the window last published it: every Tab, the tab groups (`link` present only on
10316
+ * a linked tab group), and the person's linked-group guests and dismissals in this window.
10317
+ */
10318
+ export declare type KayAppWorkspaceSnapshot = ReadonlyContract<WorkspaceSnapshot>;
10267
10319
 
10268
10320
  /** The tab-group colour palette, by name (Chrome's nine); mirrors the wire schema's enum. */
10269
10321
  export declare type KayAppWorkspaceTabGroupColor = "grey" | "blue" | "red" | "yellow" | "green" | "pink" | "purple" | "cyan" | "orange";
10270
10322
 
10323
+ /** The plugin a linked tab group belongs to and the key that plugin chose for it. */
10324
+ export declare type KayAppWorkspaceTabGroupLink = NonNullable<KayAppWorkspaceTabGroupSnapshot["link"]>;
10325
+
10326
+ /** One tab group in the strip. */
10327
+ export declare type KayAppWorkspaceTabGroupSnapshot = NonNullable<KayAppWorkspaceSnapshot["groups"]>[number];
10328
+
10271
10329
  export declare type KayAppWorkspaceTabSnapshot = ReadonlyContract<WorkspaceTabSnapshot>;
10272
10330
 
10273
10331
  /**
@@ -11530,6 +11588,12 @@ export declare const PLUGIN_SIGNAL_ATTACHMENT_TYPE_MAX = 127;
11530
11588
  /** At most this many entries in a signal's `attachments`; a source lists the first ones. */
11531
11589
  export declare const PLUGIN_SIGNAL_ATTACHMENTS_MAX = 10;
11532
11590
 
11591
+ /**
11592
+ * A signal's `body`, the item's own text, at most this many characters: a source cuts longer text
11593
+ * and marks the cut. Enough for a long email with its quoted history.
11594
+ */
11595
+ export declare const PLUGIN_SIGNAL_BODY_MAX = 20000;
11596
+
11533
11597
  /** The `kind` values of `PluginSignal` v1. `status` is system state that clears itself. */
11534
11598
  export declare const PLUGIN_SIGNAL_KINDS: readonly ["message", "request", "mention", "invite", "status"];
11535
11599
 
@@ -11711,6 +11775,7 @@ export declare const PluginSignal: z.ZodObject<{
11711
11775
  }>>;
11712
11776
  title: z.ZodOptional<z.ZodString>;
11713
11777
  snippet: z.ZodOptional<z.ZodString>;
11778
+ body: z.ZodOptional<z.ZodString>;
11714
11779
  actor: z.ZodOptional<z.ZodObject<{
11715
11780
  name: z.ZodOptional<z.ZodString>;
11716
11781
  uri: z.ZodString;
@@ -11755,6 +11820,7 @@ export declare const PluginSignalPublish: z.ZodObject<{
11755
11820
  }>>;
11756
11821
  title: z.ZodOptional<z.ZodString>;
11757
11822
  snippet: z.ZodOptional<z.ZodString>;
11823
+ body: z.ZodOptional<z.ZodString>;
11758
11824
  actor: z.ZodOptional<z.ZodObject<{
11759
11825
  name: z.ZodOptional<z.ZodString>;
11760
11826
  uri: z.ZodString;
@@ -18261,7 +18327,7 @@ declare interface UriParameter {
18261
18327
  /**
18262
18328
  * A claim on a module's own custom-scheme addresses. The pattern matches with or without an
18263
18329
  * account prefix: `gcal://@work/events/x` is matched as `gcal://events/x`, and the prefix is put
18264
- * back on the official spelling, so one claim covers both spellings.
18330
+ * back on the same-scheme official spelling, so one claim covers both spellings.
18265
18331
  */
18266
18332
  declare interface UriPatternClaim {
18267
18333
  readonly kind: "uriPattern";
@@ -18272,9 +18338,13 @@ declare interface UriPatternClaim {
18272
18338
  * carried over verbatim so a route flag such as `?permanent=true` survives
18273
18339
  * canonicalization (kay#7859); a template that re-targets to another scheme
18274
18340
  * (a projection to a web link) drops it, as does a pattern that consumed the
18275
- * query itself or a template that spells its own.
18341
+ * query itself or a template that spells its own. `preserveQuery` opts out of those defaults.
18276
18342
  */
18277
18343
  readonly normalizeTo?: UriClaimTemplate;
18344
+ /** Carry the matched query verbatim across normalization; incompatible with a target query. */
18345
+ readonly preserveQuery?: true;
18346
+ /** Carry the input's account selector even when normalization changes scheme. */
18347
+ readonly preserveConnection?: true;
18278
18348
  /**
18279
18349
  * Names the route this claim matches, so data keyed by route (a module's sign-in action rules)
18280
18350
  * can say which addresses it means. Several claims may name the same route.
@@ -19162,6 +19232,7 @@ declare const WorkspaceCommandOutcome: z.ZodEnum<{
19162
19232
  "no-window": "no-window";
19163
19233
  "not-found": "not-found";
19164
19234
  applied: "applied";
19235
+ stale: "stale";
19165
19236
  }>;
19166
19237
 
19167
19238
  declare type WorkspaceCommandOutcome = z.infer<typeof WorkspaceCommandOutcome>;
@@ -19214,11 +19285,64 @@ declare type WorkspacePaneNode = {
19214
19285
 
19215
19286
  declare const WorkspacePaneNode: z.ZodType<WorkspacePaneNode>;
19216
19287
 
19288
+ /**
19289
+ * The full workspace tree, app-wide (every tab, not one session's). `rev` is a monotonic counter the
19290
+ * renderer stamps on push (mirroring the `<kay_tab_context>` block's `rev`), so a reader can tell a
19291
+ * pulled snapshot apart from one already in hand.
19292
+ */
19293
+ declare const WorkspaceSnapshot: z.ZodObject<{
19294
+ rev: z.ZodNumber;
19295
+ activeTabId: z.ZodString;
19296
+ tabs: z.ZodArray<z.ZodObject<{
19297
+ id: z.ZodString;
19298
+ label: z.ZodString;
19299
+ active: z.ZodBoolean;
19300
+ variable: z.ZodOptional<z.ZodBoolean>;
19301
+ preview: z.ZodOptional<z.ZodBoolean>;
19302
+ groupId: z.ZodOptional<z.ZodString>;
19303
+ sessionIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
19304
+ focusedPaneId: z.ZodString;
19305
+ maximizedPaneId: z.ZodNullable<z.ZodString>;
19306
+ root: z.ZodType<WorkspacePaneNode, unknown, z.core.$ZodTypeInternals<WorkspacePaneNode, unknown>>;
19307
+ }, z.core.$strict>>;
19308
+ groups: z.ZodOptional<z.ZodArray<z.ZodObject<{
19309
+ id: z.ZodString;
19310
+ name: z.ZodString;
19311
+ color: z.ZodEnum<{
19312
+ grey: "grey";
19313
+ blue: "blue";
19314
+ red: "red";
19315
+ yellow: "yellow";
19316
+ green: "green";
19317
+ pink: "pink";
19318
+ purple: "purple";
19319
+ cyan: "cyan";
19320
+ orange: "orange";
19321
+ }>;
19322
+ collapsed: z.ZodOptional<z.ZodBoolean>;
19323
+ icon: z.ZodOptional<z.ZodString>;
19324
+ link: z.ZodOptional<z.ZodObject<{
19325
+ pluginId: z.ZodString;
19326
+ key: z.ZodString;
19327
+ }, z.core.$strict>>;
19328
+ }, z.core.$strict>>>;
19329
+ linkedGroupGuests: z.ZodOptional<z.ZodArray<z.ZodString>>;
19330
+ linkedGroupsDismissed: z.ZodOptional<z.ZodArray<z.ZodObject<{
19331
+ pluginId: z.ZodString;
19332
+ key: z.ZodString;
19333
+ }, z.core.$strict>>>;
19334
+ }, z.core.$strict>;
19335
+
19336
+ declare type WorkspaceSnapshot = z.infer<typeof WorkspaceSnapshot>;
19337
+
19217
19338
  declare const WorkspaceTabSnapshot: z.ZodObject<{
19218
19339
  id: z.ZodString;
19219
19340
  label: z.ZodString;
19220
19341
  active: z.ZodBoolean;
19221
19342
  variable: z.ZodOptional<z.ZodBoolean>;
19343
+ preview: z.ZodOptional<z.ZodBoolean>;
19344
+ groupId: z.ZodOptional<z.ZodString>;
19345
+ sessionIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
19222
19346
  focusedPaneId: z.ZodString;
19223
19347
  maximizedPaneId: z.ZodNullable<z.ZodString>;
19224
19348
  root: z.ZodType<WorkspacePaneNode, unknown, z.core.$ZodTypeInternals<WorkspacePaneNode, unknown>>;
package/dist/kay-app.js CHANGED
@@ -371,6 +371,7 @@ var PLUGIN_SIGNAL_KINDS = [
371
371
  ];
372
372
  var PLUGIN_SIGNAL_TITLE_MAX = 300;
373
373
  var PLUGIN_SIGNAL_SNIPPET_MAX = 500;
374
+ var PLUGIN_SIGNAL_BODY_MAX = 2e4;
374
375
  var PLUGIN_SIGNAL_LABELS_MAX = 20;
375
376
  var PLUGIN_SIGNAL_LABEL_MAX = 64;
376
377
  var PLUGIN_SIGNAL_ATTACHMENTS_MAX = 10;
@@ -1205,6 +1206,7 @@ export {
1205
1206
  PLUGIN_SIGNAL_ATTACHMENTS_MAX,
1206
1207
  PLUGIN_SIGNAL_ATTACHMENT_NAME_MAX,
1207
1208
  PLUGIN_SIGNAL_ATTACHMENT_TYPE_MAX,
1209
+ PLUGIN_SIGNAL_BODY_MAX,
1208
1210
  PLUGIN_SIGNAL_KINDS,
1209
1211
  PLUGIN_SIGNAL_LABELS_MAX,
1210
1212
  PLUGIN_SIGNAL_LABEL_MAX,
@@ -865,7 +865,7 @@ declare interface UriParameter {
865
865
  /**
866
866
  * A claim on a module's own custom-scheme addresses. The pattern matches with or without an
867
867
  * account prefix: `gcal://@work/events/x` is matched as `gcal://events/x`, and the prefix is put
868
- * back on the official spelling, so one claim covers both spellings.
868
+ * back on the same-scheme official spelling, so one claim covers both spellings.
869
869
  */
870
870
  declare interface UriPatternClaim {
871
871
  readonly kind: "uriPattern";
@@ -876,9 +876,13 @@ declare interface UriPatternClaim {
876
876
  * carried over verbatim so a route flag such as `?permanent=true` survives
877
877
  * canonicalization (kay#7859); a template that re-targets to another scheme
878
878
  * (a projection to a web link) drops it, as does a pattern that consumed the
879
- * query itself or a template that spells its own.
879
+ * query itself or a template that spells its own. `preserveQuery` opts out of those defaults.
880
880
  */
881
881
  readonly normalizeTo?: UriClaimTemplate;
882
+ /** Carry the matched query verbatim across normalization; incompatible with a target query. */
883
+ readonly preserveQuery?: true;
884
+ /** Carry the input's account selector even when normalization changes scheme. */
885
+ readonly preserveConnection?: true;
882
886
  /**
883
887
  * Names the route this claim matches, so data keyed by route (a module's sign-in action rules)
884
888
  * can say which addresses it means. Several claims may name the same route.
@@ -91,6 +91,9 @@ function compileClaim(claim) {
91
91
  }
92
92
  };
93
93
  }
94
+ if (claim.kind === "uriPattern" && claim.preserveQuery === true && claim.normalizeTo?.split("#", 1)[0]?.includes("?") === true) {
95
+ throw new Error("URI claim preserveQuery cannot use normalizeTo with a query.");
96
+ }
94
97
  const pattern = compileRoutePattern(claim.pattern);
95
98
  const normalizeTo = claim.normalizeTo === void 0 ? void 0 : compileTemplate(claim.normalizeTo);
96
99
  const sourceUrl = claim.kind === "uriPattern" && claim.sourceUrl !== void 0 ? compileTemplate(claim.sourceUrl) : void 0;
@@ -109,7 +112,7 @@ function compileClaim(claim) {
109
112
  claimKind: claim.kind,
110
113
  ...Object.keys(captures).length > 0 ? { captures } : {},
111
114
  ...normalized !== void 0 ? {
112
- normalizedUri: withSelector(withInputSearch(claim, pattern, parsed, normalized), parsed.protocol, selector)
115
+ normalizedUri: withSelector(withInputSearch(claim, pattern, parsed, normalized), parsed.protocol, selector, claim.kind === "uriPattern" && claim.preserveConnection === true)
113
116
  } : {},
114
117
  ...claim.route !== void 0 ? { route: claim.route } : {},
115
118
  ...filledSourceUrl !== void 0 ? { sourceUrl: filledSourceUrl } : {},
@@ -142,11 +145,13 @@ function parseUriConnectionSelector(input) {
142
145
  }
143
146
  return { selector: match[2], rest: `${match[1]}${match[3] ?? ""}` };
144
147
  }
145
- function withSelector(normalized, inputProtocol, selector) {
148
+ function withSelector(normalized, inputProtocol, selector, preserveConnection) {
146
149
  if (selector === void 0)
147
150
  return normalized;
148
- if (templateProtocol(normalized) !== inputProtocol)
151
+ const targetProtocol = templateProtocol(normalized);
152
+ if (targetProtocol === void 0 || !preserveConnection && targetProtocol !== inputProtocol) {
149
153
  return normalized;
154
+ }
150
155
  const schemeEnd = normalized.indexOf("://") + 3;
151
156
  return `${normalized.slice(0, schemeEnd)}@${selector}/${normalized.slice(schemeEnd)}`;
152
157
  }
@@ -307,7 +312,14 @@ function safeDecode(value) {
307
312
  }
308
313
  }
309
314
  function withInputSearch(claim, pattern, input, normalized) {
310
- if (claim.kind !== "uriPattern" || claim.normalizeTo === void 0 || pattern.search !== void 0 || input.search.length === 0 || claim.normalizeTo.includes("?") || templateProtocol(claim.normalizeTo) !== input.protocol) {
315
+ if (claim.kind !== "uriPattern" || claim.normalizeTo === void 0 || input.search.length === 0) {
316
+ return normalized;
317
+ }
318
+ if (claim.preserveQuery === true) {
319
+ const fragment = normalized.indexOf("#");
320
+ return fragment === -1 ? `${normalized}?${input.search}` : `${normalized.slice(0, fragment)}?${input.search}${normalized.slice(fragment)}`;
321
+ }
322
+ if (pattern.search !== void 0 || claim.normalizeTo.includes("?") || templateProtocol(claim.normalizeTo) !== input.protocol) {
311
323
  return normalized;
312
324
  }
313
325
  return `${normalized}?${input.search}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaklabs-ai/plugin-sdk",
3
- "version": "0.55.1",
3
+ "version": "0.55.3",
4
4
  "description": "The Kay plugin SDK — types and helpers for authoring a Kay directory plugin.",
5
5
  "author": "YakLabs",
6
6
  "type": "module",