@yaklabs-ai/plugin-sdk 0.34.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.34.0"
16
+ "@bonsai/plugin-sdk": "npm:@yaklabs-ai/plugin-sdk@^0.36.0"
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.34.0" },
65
+ "compatibility": { "sdk": "^0.36.0" },
66
66
  "targets": {
67
67
  "kay": {
68
68
  "agent": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "@yaklabs-ai/plugin-sdk",
4
- "version": "0.34.0",
4
+ "version": "0.36.0",
5
5
  "target": "npm",
6
6
  "firstPartyLicense": "MIT",
7
7
  "bundled": [
@@ -1598,11 +1598,18 @@ declare type FailureJsonValue = null | boolean | number | string | readonly Fail
1598
1598
  readonly [key: string]: FailureJsonValue;
1599
1599
  };
1600
1600
 
1601
- declare interface FailureOccurrence<C = FailureJsonValue> {
1601
+ /**
1602
+ * One failure, as every lane records it. `Code` and `Domain` default to
1603
+ * `string`; a lane that declares its codes (an `as const` list beside its
1604
+ * cause schema) passes them, so minting a record with an undeclared code
1605
+ * fails typecheck. Reading is unchanged: `failureSchema` keeps both as
1606
+ * strings, so a saved record with an unknown code still reads.
1607
+ */
1608
+ declare interface FailureOccurrence<C = FailureJsonValue, Code extends string = string, Domain extends string = string> {
1602
1609
  readonly ref: FailureRef;
1603
1610
  readonly sourceRef: FailureRef;
1604
- readonly domain: string;
1605
- readonly code: string;
1611
+ readonly domain: Domain;
1612
+ readonly code: Code;
1606
1613
  readonly context: {
1607
1614
  readonly component: string;
1608
1615
  readonly operation: string;
@@ -6833,6 +6840,17 @@ declare interface Tool<TParameters extends TSchema = TSchema> {
6833
6840
  constrainedSampling?: false | ConstrainedSamplingConfig;
6834
6841
  }
6835
6842
 
6843
+ /**
6844
+ * The closed vocabulary of tool call failure codes. The engine and the tools
6845
+ * mint only these; a returned failure with a code outside it is not a
6846
+ * classification (the engine records the rejection on an unexplained failure),
6847
+ * and a stored record with one reads as none.
6848
+ */
6849
+ declare const TOOL_CALL_FAILURE_CODES: readonly ["tool.invalid_input", "tool.blocked", "tool.cancelled", "tool.timeout", "tool.not_found", "tool.unavailable", "tool.execution_failed", "tool.auth_unavailable", "tool.replies_owed", "provider.http", "provider.api", "provider.transport", "uri.not_found", "uri.unsupported_operation", "uri.not_connected", "uri.auth_unavailable", "permission.denied", "edit.empty_edits", "edit.text_not_found", "edit.text_not_unique", "edit.empty_old_text", "edit.overlapping_edits", "edit.no_change", "uri.not_connected.pick_account", "uri.not_connected.account_not_allowed", "uri.not_connected.sign_in_expired", "tool.blocked.by_user", "tool.blocked.by_rule", "tool.blocked.by_bonsai", "tool.blocked.by_mode", "tool.blocked.protected_path", "tool.blocked.approval_not_shown", "tool.blocked.unverified", "tool.blocked.edits_not_applied"];
6850
+
6851
+ /** The `domain` of a tool call failure occurrence; it selects this lane's cause contract. */
6852
+ declare const TOOL_CALL_FAILURE_DOMAIN: "tool-call";
6853
+
6836
6854
  declare interface ToolCall {
6837
6855
  type: "toolCall";
6838
6856
  id: string;
@@ -6891,13 +6909,15 @@ declare const toolCallFailureCauseSchema: z.ZodObject<{
6891
6909
  }, z.core.$strict>>;
6892
6910
  }, z.core.$strict>;
6893
6911
 
6912
+ declare type ToolCallFailureCode = (typeof TOOL_CALL_FAILURE_CODES)[number];
6913
+
6894
6914
  /**
6895
6915
  * The failure occurrence a failed tool result carries under
6896
6916
  * `details.toolCallFailure`: the classification's code at the occurrence
6897
6917
  * level and the rest as its cause, with its own ref and the call it came out
6898
6918
  * of as its operation.
6899
6919
  */
6900
- declare type ToolCallFailureOccurrence = FailureOccurrence<ToolCallFailureCause>;
6920
+ declare type ToolCallFailureOccurrence = FailureOccurrence<ToolCallFailureCause, ToolCallFailureCode, typeof TOOL_CALL_FAILURE_DOMAIN>;
6901
6921
 
6902
6922
  /**
6903
6923
  * The minimal shape this leaf reads from a session entry: a `timestamp` and the
@@ -2380,8 +2380,18 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
2380
2380
  * 0.34.0 lets a rail entry declare a sidebar (#15901). `panel.body` on a `railEntries` entry names
2381
2381
  * what fills it, either a `contextOnly` app in the plugin or a body the host ships. Additive: a rail
2382
2382
  * entry with no `panel` still opens its page alone. A plugin that declares a panel declares ^0.34.0.
2383
+ *
2384
+ * 0.35.0 lets a rail entry declare a count badge as data (#15900). `badge` on a `railEntries` entry
2385
+ * names one of the plugin's `rows` data sources, an optional `where` and a plural `label`.
2386
+ * `app.skills` gains `listPendingSuggestions` and `subscribeSuggestions` under `skills.read`. A plugin
2387
+ * that declares a badge declares ^0.35.0.
2388
+ *
2389
+ * 0.36.0 lets a plugin declare its pages and which page an address opens (#15897, #15898). `pages`
2390
+ * under `kay.ui` `contributes` names each page's title, icon file, companion status and opened
2391
+ * event. A reference presentation that opens a page gains `scheme` and `addresses`, and may leave
2392
+ * out `uriModule` when another layer serves the address. A plugin using these declares ^0.36.0.
2383
2393
  */
2384
- declare const KAY_PLUGIN_SDK_VERSION: "0.34.0";
2394
+ declare const KAY_PLUGIN_SDK_VERSION: "0.36.0";
2385
2395
 
2386
2396
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2387
2397
 
@@ -9868,12 +9878,13 @@ declare interface UriPatternInit {
9868
9878
  */
9869
9879
  readonly hostname?: string;
9870
9880
  /**
9871
- * Slash-delimited path pattern. A final capture may use `:name*` to consume
9872
- * the remaining path segments, preserving `/` in the captured value. Unless the pattern itself
9873
- * ends in `/`, an input path ending in one `/` matches as if the slash were absent, so
9874
- * `threads/t1/` is `threads/t1` (captures and official spelling included) and a lone `/` after a
9875
- * host is the empty path; a doubled `//` is never dropped, nor the root path of an address with
9876
- * no host (`bfs:///`).
9881
+ * Slash-delimited path pattern, starting with `/`. `""` matches only an address with no path. A
9882
+ * final capture may use `:name*` to consume the remaining path segments, preserving `/` in the
9883
+ * captured value. A capture may end in a literal extension: `:name.md` matches `billing.md` and
9884
+ * captures `billing`. Unless the pattern itself ends in `/`, an input path ending in one `/`
9885
+ * matches as if the slash were absent, so `threads/t1/` is `threads/t1` (captures and official
9886
+ * spelling included) and a lone `/` after a host is the empty path; a doubled `//` is never
9887
+ * dropped, nor the root path of an address with no host (`bfs:///`).
9877
9888
  */
9878
9889
  readonly pathname?: string;
9879
9890
  /** `&`-delimited query pattern; `""` matches only an address with no query. */
@@ -2434,8 +2434,18 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
2434
2434
  * 0.34.0 lets a rail entry declare a sidebar (#15901). `panel.body` on a `railEntries` entry names
2435
2435
  * what fills it, either a `contextOnly` app in the plugin or a body the host ships. Additive: a rail
2436
2436
  * entry with no `panel` still opens its page alone. A plugin that declares a panel declares ^0.34.0.
2437
+ *
2438
+ * 0.35.0 lets a rail entry declare a count badge as data (#15900). `badge` on a `railEntries` entry
2439
+ * names one of the plugin's `rows` data sources, an optional `where` and a plural `label`.
2440
+ * `app.skills` gains `listPendingSuggestions` and `subscribeSuggestions` under `skills.read`. A plugin
2441
+ * that declares a badge declares ^0.35.0.
2442
+ *
2443
+ * 0.36.0 lets a plugin declare its pages and which page an address opens (#15897, #15898). `pages`
2444
+ * under `kay.ui` `contributes` names each page's title, icon file, companion status and opened
2445
+ * event. A reference presentation that opens a page gains `scheme` and `addresses`, and may leave
2446
+ * out `uriModule` when another layer serves the address. A plugin using these declares ^0.36.0.
2437
2447
  */
2438
- declare const KAY_PLUGIN_SDK_VERSION: "0.34.0";
2448
+ declare const KAY_PLUGIN_SDK_VERSION: "0.36.0";
2439
2449
 
2440
2450
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2441
2451
 
@@ -4756,14 +4766,14 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
4756
4766
  "kay.ui": "kay.ui";
4757
4767
  }>;
4758
4768
  kind: z.ZodEnum<{
4769
+ endpoint: "endpoint";
4759
4770
  tool: "tool";
4760
4771
  resource: "resource";
4761
- endpoint: "endpoint";
4762
4772
  storage: "storage";
4763
- command: "command";
4764
4773
  uriModule: "uriModule";
4765
4774
  authService: "authService";
4766
4775
  app: "app";
4776
+ command: "command";
4767
4777
  configSection: "configSection";
4768
4778
  skill: "skill";
4769
4779
  converter: "converter";
@@ -5284,30 +5294,30 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
5284
5294
  description: z.ZodString;
5285
5295
  operations: z.ZodExactOptional<z.ZodArray<z.ZodEnum<{
5286
5296
  search: "search";
5287
- delete: "delete";
5288
5297
  list: "list";
5289
5298
  read: "read";
5290
5299
  write: "write";
5291
5300
  help: "help";
5292
5301
  edit: "edit";
5302
+ delete: "delete";
5293
5303
  }>>>;
5294
5304
  operationDocs: z.ZodExactOptional<z.ZodRecord<z.ZodEnum<{
5295
5305
  search: "search";
5296
- delete: "delete";
5297
5306
  list: "list";
5298
5307
  read: "read";
5299
5308
  write: "write";
5300
5309
  help: "help";
5301
5310
  edit: "edit";
5311
+ delete: "delete";
5302
5312
  }> & z.core.$partial, z.ZodString>>;
5303
5313
  operationDefaults: z.ZodExactOptional<z.ZodRecord<z.ZodEnum<{
5304
5314
  search: "search";
5305
- delete: "delete";
5306
5315
  list: "list";
5307
5316
  read: "read";
5308
5317
  write: "write";
5309
5318
  help: "help";
5310
5319
  edit: "edit";
5320
+ delete: "delete";
5311
5321
  }> & z.core.$partial, z.ZodEnum<{
5312
5322
  never: "never";
5313
5323
  allow: "allow";
@@ -5366,12 +5376,12 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
5366
5376
  action: z.ZodString;
5367
5377
  operations: z.ZodExactOptional<z.ZodArray<z.ZodEnum<{
5368
5378
  search: "search";
5369
- delete: "delete";
5370
5379
  list: "list";
5371
5380
  read: "read";
5372
5381
  write: "write";
5373
5382
  help: "help";
5374
5383
  edit: "edit";
5384
+ delete: "delete";
5375
5385
  }>>>;
5376
5386
  routes: z.ZodExactOptional<z.ZodArray<z.ZodString>>;
5377
5387
  query: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -5379,12 +5389,12 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
5379
5389
  public: z.ZodLiteral<true>;
5380
5390
  operations: z.ZodExactOptional<z.ZodArray<z.ZodEnum<{
5381
5391
  search: "search";
5382
- delete: "delete";
5383
5392
  list: "list";
5384
5393
  read: "read";
5385
5394
  write: "write";
5386
5395
  help: "help";
5387
5396
  edit: "edit";
5397
+ delete: "delete";
5388
5398
  }>>>;
5389
5399
  routes: z.ZodExactOptional<z.ZodArray<z.ZodString>>;
5390
5400
  query: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -5566,9 +5576,9 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
5566
5576
  localId: z.ZodString;
5567
5577
  globalId: z.ZodString;
5568
5578
  scope: z.ZodEnum<{
5569
- session: "session";
5570
5579
  runtime: "runtime";
5571
5580
  account: "account";
5581
+ session: "session";
5572
5582
  }>;
5573
5583
  snapshot: z.ZodObject<{
5574
5584
  version: z.ZodNumber;
@@ -5642,7 +5652,20 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
5642
5652
  referencePresentations: z.ZodOptional<z.ZodArray<z.ZodObject<{
5643
5653
  localId: z.ZodString;
5644
5654
  globalId: z.ZodString;
5645
- uriModule: z.ZodString;
5655
+ uriModule: z.ZodOptional<z.ZodString>;
5656
+ schemes: z.ZodOptional<z.ZodArray<z.ZodString>>;
5657
+ addresses: z.ZodOptional<z.ZodArray<z.ZodObject<{
5658
+ pattern: z.ZodObject<{
5659
+ protocol: z.ZodString;
5660
+ hostname: z.ZodString;
5661
+ pathname: z.ZodOptional<z.ZodString>;
5662
+ search: z.ZodOptional<z.ZodString>;
5663
+ }, z.core.$strict>;
5664
+ objectId: z.ZodOptional<z.ZodString>;
5665
+ open: z.ZodOptional<z.ZodObject<{
5666
+ kind: z.ZodLiteral<"none">;
5667
+ }, z.core.$strict>>;
5668
+ }, z.core.$strict>>>;
5646
5669
  resourceKind: z.ZodString;
5647
5670
  open: z.ZodDiscriminatedUnion<[z.ZodObject<{
5648
5671
  kind: z.ZodLiteral<"working-document">;
@@ -5702,17 +5725,18 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
5702
5725
  globalId: z.ZodString;
5703
5726
  }, z.core.$strict>>>;
5704
5727
  host: z.ZodOptional<z.ZodArray<z.ZodEnum<{
5728
+ "navigation.open-session": "navigation.open-session";
5705
5729
  "sessions.create": "sessions.create";
5706
- "memory.read": "memory.read";
5707
- "skills.read": "skills.read";
5708
- "automations.run": "automations.run";
5709
5730
  "artifacts.read": "artifacts.read";
5710
- "navigation.open-session": "navigation.open-session";
5711
5731
  "artifacts.preview": "artifacts.preview";
5732
+ "markdown.document-view": "markdown.document-view";
5712
5733
  "cost.read": "cost.read";
5713
5734
  "billing.read": "billing.read";
5735
+ "skills.read": "skills.read";
5736
+ "memory.read": "memory.read";
5714
5737
  "self-improvement.read": "self-improvement.read";
5715
5738
  "automations.read": "automations.read";
5739
+ "automations.run": "automations.run";
5716
5740
  "shared-artifacts.read": "shared-artifacts.read";
5717
5741
  "workspace.place-pane": "workspace.place-pane";
5718
5742
  "workspace.pane-header": "workspace.pane-header";
@@ -10939,12 +10963,13 @@ declare interface UriPatternInit {
10939
10963
  */
10940
10964
  readonly hostname?: string;
10941
10965
  /**
10942
- * Slash-delimited path pattern. A final capture may use `:name*` to consume
10943
- * the remaining path segments, preserving `/` in the captured value. Unless the pattern itself
10944
- * ends in `/`, an input path ending in one `/` matches as if the slash were absent, so
10945
- * `threads/t1/` is `threads/t1` (captures and official spelling included) and a lone `/` after a
10946
- * host is the empty path; a doubled `//` is never dropped, nor the root path of an address with
10947
- * no host (`bfs:///`).
10966
+ * Slash-delimited path pattern, starting with `/`. `""` matches only an address with no path. A
10967
+ * final capture may use `:name*` to consume the remaining path segments, preserving `/` in the
10968
+ * captured value. A capture may end in a literal extension: `:name.md` matches `billing.md` and
10969
+ * captures `billing`. Unless the pattern itself ends in `/`, an input path ending in one `/`
10970
+ * matches as if the slash were absent, so `threads/t1/` is `threads/t1` (captures and official
10971
+ * spelling included) and a lone `/` after a host is the empty path; a doubled `//` is never
10972
+ * dropped, nor the root path of an address with no host (`bfs:///`).
10948
10973
  */
10949
10974
  readonly pathname?: string;
10950
10975
  /** `&`-delimited query pattern; `""` matches only an address with no query. */
@@ -2709,8 +2709,18 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
2709
2709
  * 0.34.0 lets a rail entry declare a sidebar (#15901). `panel.body` on a `railEntries` entry names
2710
2710
  * what fills it, either a `contextOnly` app in the plugin or a body the host ships. Additive: a rail
2711
2711
  * entry with no `panel` still opens its page alone. A plugin that declares a panel declares ^0.34.0.
2712
+ *
2713
+ * 0.35.0 lets a rail entry declare a count badge as data (#15900). `badge` on a `railEntries` entry
2714
+ * names one of the plugin's `rows` data sources, an optional `where` and a plural `label`.
2715
+ * `app.skills` gains `listPendingSuggestions` and `subscribeSuggestions` under `skills.read`. A plugin
2716
+ * that declares a badge declares ^0.35.0.
2717
+ *
2718
+ * 0.36.0 lets a plugin declare its pages and which page an address opens (#15897, #15898). `pages`
2719
+ * under `kay.ui` `contributes` names each page's title, icon file, companion status and opened
2720
+ * event. A reference presentation that opens a page gains `scheme` and `addresses`, and may leave
2721
+ * out `uriModule` when another layer serves the address. A plugin using these declares ^0.36.0.
2712
2722
  */
2713
- declare const KAY_PLUGIN_SDK_VERSION: "0.34.0";
2723
+ declare const KAY_PLUGIN_SDK_VERSION: "0.36.0";
2714
2724
 
2715
2725
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2716
2726
 
@@ -11190,12 +11200,13 @@ declare interface UriPatternInit {
11190
11200
  */
11191
11201
  readonly hostname?: string;
11192
11202
  /**
11193
- * Slash-delimited path pattern. A final capture may use `:name*` to consume
11194
- * the remaining path segments, preserving `/` in the captured value. Unless the pattern itself
11195
- * ends in `/`, an input path ending in one `/` matches as if the slash were absent, so
11196
- * `threads/t1/` is `threads/t1` (captures and official spelling included) and a lone `/` after a
11197
- * host is the empty path; a doubled `//` is never dropped, nor the root path of an address with
11198
- * no host (`bfs:///`).
11203
+ * Slash-delimited path pattern, starting with `/`. `""` matches only an address with no path. A
11204
+ * final capture may use `:name*` to consume the remaining path segments, preserving `/` in the
11205
+ * captured value. A capture may end in a literal extension: `:name.md` matches `billing.md` and
11206
+ * captures `billing`. Unless the pattern itself ends in `/`, an input path ending in one `/`
11207
+ * matches as if the slash were absent, so `threads/t1/` is `threads/t1` (captures and official
11208
+ * spelling included) and a lone `/` after a host is the empty path; a doubled `//` is never
11209
+ * dropped, nor the root path of an address with no host (`bfs:///`).
11199
11210
  */
11200
11211
  readonly pathname?: string;
11201
11212
  /** `&`-delimited query pattern; `""` matches only an address with no query. */
package/dist/kay-agent.js CHANGED
@@ -2,7 +2,7 @@
2
2
  var SCHEME_RE = /^([a-z][a-z0-9+.-]*):\/\//i;
3
3
  var CAPTURE_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
4
4
  var REST_CAPTURE_RE = /^:([A-Za-z_][A-Za-z0-9_]*)\*$/;
5
- var CONSTRAINED_CAPTURE_RE = /^([A-Za-z_][A-Za-z0-9_]*)(?:\((.*)\))?$/;
5
+ var CONSTRAINED_CAPTURE_RE = /^([A-Za-z_][A-Za-z0-9_]*)(?:\((.*)\))?(\.[A-Za-z0-9]+)?$/;
6
6
  var MAX_CAPTURE_CONSTRAINT_LENGTH = 64;
7
7
  function compileUriClaim(claim) {
8
8
  if (claim.kind === "scheme") {
@@ -144,8 +144,10 @@ function compileParts(value, separator, options = {}) {
144
144
  const constraint = match[2];
145
145
  if (constraint !== void 0)
146
146
  assertSupportedConstraint(constraint);
147
+ const suffix = match[3];
147
148
  return {
148
149
  capture,
150
+ ...suffix !== void 0 ? { suffix } : {},
149
151
  ...constraint !== void 0 ? { constraint: new RegExp(`^(?:${constraint})$`) } : {}
150
152
  };
151
153
  }
@@ -165,8 +167,11 @@ function matchRoute(pattern, input) {
165
167
  if (pattern.hostname !== void 0 && !matchParts(pattern.hostname, input.hostname, isLoneCapture(pattern.hostname) ? void 0 : ".", captures, true)) {
166
168
  return void 0;
167
169
  }
168
- if (pattern.pathname !== void 0 && !matchParts(pattern.pathname, pattern.pathnameEndsInSlash === true ? input.pathname : withoutTrailingSlash(input), "/", captures)) {
169
- return void 0;
170
+ if (pattern.pathname !== void 0) {
171
+ const pathname = pattern.pathnameEndsInSlash === true ? input.pathname : withoutTrailingSlash(input);
172
+ if (pattern.pathname.length === 0 ? pathname !== "" : !matchParts(pattern.pathname, pathname, "/", captures)) {
173
+ return void 0;
174
+ }
170
175
  }
171
176
  if (pattern.search !== void 0 && (pattern.search.length === 0 ? input.search !== "" : !matchParts(pattern.search, input.search, "&", captures))) {
172
177
  return void 0;
@@ -200,7 +205,11 @@ function matchParts(pattern, value, separator, captures, foldLiterals = false) {
200
205
  return false;
201
206
  const actual = parts[partIndex];
202
207
  if (expected.capture !== void 0) {
203
- const decoded = safeDecode(actual);
208
+ const { suffix } = expected;
209
+ if (suffix !== void 0 && (!actual.endsWith(suffix) || actual.length === suffix.length)) {
210
+ return false;
211
+ }
212
+ const decoded = safeDecode(suffix === void 0 ? actual : actual.slice(0, -suffix.length));
204
213
  if (expected.constraint !== void 0 && !expected.constraint.test(decoded)) {
205
214
  return false;
206
215
  }
@@ -453,9 +453,9 @@ declare const CorpusResourceInvocationQuery: z.ZodObject<{
453
453
  }, z.core.$strict>>;
454
454
  limit: z.ZodOptional<z.ZodNumber>;
455
455
  rootCreatedByKind: z.ZodOptional<z.ZodEnum<{
456
+ agent: "agent";
456
457
  plugin: "plugin";
457
458
  human: "human";
458
- agent: "agent";
459
459
  trigger: "trigger";
460
460
  daemon: "daemon";
461
461
  }>>;
@@ -841,11 +841,18 @@ declare type FailureJsonValue = null | boolean | number | string | readonly Fail
841
841
  readonly [key: string]: FailureJsonValue;
842
842
  };
843
843
 
844
- declare interface FailureOccurrence<C = FailureJsonValue> {
844
+ /**
845
+ * One failure, as every lane records it. `Code` and `Domain` default to
846
+ * `string`; a lane that declares its codes (an `as const` list beside its
847
+ * cause schema) passes them, so minting a record with an undeclared code
848
+ * fails typecheck. Reading is unchanged: `failureSchema` keeps both as
849
+ * strings, so a saved record with an unknown code still reads.
850
+ */
851
+ declare interface FailureOccurrence<C = FailureJsonValue, Code extends string = string, Domain extends string = string> {
845
852
  readonly ref: FailureRef;
846
853
  readonly sourceRef: FailureRef;
847
- readonly domain: string;
848
- readonly code: string;
854
+ readonly domain: Domain;
855
+ readonly code: Code;
849
856
  readonly context: {
850
857
  readonly component: string;
851
858
  readonly operation: string;
package/dist/kay-app.d.ts CHANGED
@@ -795,9 +795,9 @@ export declare const CorpusResourceInvocationQuery: z.ZodObject<{
795
795
  }, z.core.$strict>>;
796
796
  limit: z.ZodOptional<z.ZodNumber>;
797
797
  rootCreatedByKind: z.ZodOptional<z.ZodEnum<{
798
+ agent: "agent";
798
799
  plugin: "plugin";
799
800
  human: "human";
800
- agent: "agent";
801
801
  trigger: "trigger";
802
802
  daemon: "daemon";
803
803
  }>>;
@@ -1501,11 +1501,18 @@ declare type FailureJsonValue = null | boolean | number | string | readonly Fail
1501
1501
  readonly [key: string]: FailureJsonValue;
1502
1502
  };
1503
1503
 
1504
- declare interface FailureOccurrence<C = FailureJsonValue> {
1504
+ /**
1505
+ * One failure, as every lane records it. `Code` and `Domain` default to
1506
+ * `string`; a lane that declares its codes (an `as const` list beside its
1507
+ * cause schema) passes them, so minting a record with an undeclared code
1508
+ * fails typecheck. Reading is unchanged: `failureSchema` keeps both as
1509
+ * strings, so a saved record with an unknown code still reads.
1510
+ */
1511
+ declare interface FailureOccurrence<C = FailureJsonValue, Code extends string = string, Domain extends string = string> {
1505
1512
  readonly ref: FailureRef;
1506
1513
  readonly sourceRef: FailureRef;
1507
- readonly domain: string;
1508
- readonly code: string;
1514
+ readonly domain: Domain;
1515
+ readonly code: Code;
1509
1516
  readonly context: {
1510
1517
  readonly component: string;
1511
1518
  readonly operation: string;
@@ -2664,8 +2671,18 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
2664
2671
  * 0.34.0 lets a rail entry declare a sidebar (#15901). `panel.body` on a `railEntries` entry names
2665
2672
  * what fills it, either a `contextOnly` app in the plugin or a body the host ships. Additive: a rail
2666
2673
  * entry with no `panel` still opens its page alone. A plugin that declares a panel declares ^0.34.0.
2674
+ *
2675
+ * 0.35.0 lets a rail entry declare a count badge as data (#15900). `badge` on a `railEntries` entry
2676
+ * names one of the plugin's `rows` data sources, an optional `where` and a plural `label`.
2677
+ * `app.skills` gains `listPendingSuggestions` and `subscribeSuggestions` under `skills.read`. A plugin
2678
+ * that declares a badge declares ^0.35.0.
2679
+ *
2680
+ * 0.36.0 lets a plugin declare its pages and which page an address opens (#15897, #15898). `pages`
2681
+ * under `kay.ui` `contributes` names each page's title, icon file, companion status and opened
2682
+ * event. A reference presentation that opens a page gains `scheme` and `addresses`, and may leave
2683
+ * out `uriModule` when another layer serves the address. A plugin using these declares ^0.36.0.
2667
2684
  */
2668
- declare const KAY_PLUGIN_SDK_VERSION: "0.34.0";
2685
+ declare const KAY_PLUGIN_SDK_VERSION: "0.36.0";
2669
2686
 
2670
2687
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2671
2688
 
@@ -4986,6 +5003,18 @@ export declare interface KayAppPaths {
4986
5003
  readonly logsRoot?: string;
4987
5004
  }
4988
5005
 
5006
+ /** One suggested change waiting for a person's review. Ids repeat across skills, so all three identify it. */
5007
+ export declare interface KayAppPendingSkillSuggestion {
5008
+ readonly sourceId: string;
5009
+ readonly skillName: string;
5010
+ readonly suggestionId: string;
5011
+ }
5012
+
5013
+ export declare type KayAppPendingSkillSuggestionsResult = {
5014
+ readonly status: "ok";
5015
+ readonly suggestions: readonly KayAppPendingSkillSuggestion[];
5016
+ } | KayAppSkillsUnavailable;
5017
+
4989
5018
  export declare interface KayAppPermissions {
4990
5019
  /**
4991
5020
  * Register a live source of permission rows under a plugin-local id (register during
@@ -7198,6 +7227,13 @@ export declare interface KayAppSkills {
7198
7227
  list(): Promise<KayAppSkillsListResult>;
7199
7228
  /** One skill's identity, current version, and suggestions. */
7200
7229
  read(skill: KayAppSkillRef): Promise<KayAppSkillReadResult>;
7230
+ /** Every suggested change, across all skills, still waiting for a person's review. */
7231
+ listPendingSuggestions(): Promise<KayAppPendingSkillSuggestionsResult>;
7232
+ /**
7233
+ * Calls `listener` when {@link KayAppSkills.listPendingSuggestions} may answer differently, at most
7234
+ * once per burst of writes. Returns an unsubscribe.
7235
+ */
7236
+ subscribeSuggestions(listener: () => void): () => void;
7201
7237
  /** One skill's revisions, newest first, each with its version and how it came about. */
7202
7238
  history(skill: KayAppSkillRef, options?: KayAppSkillHistoryOptions): Promise<KayAppSkillHistoryResult>;
7203
7239
  /** How the skill's local copy differs from its source. */
@@ -15949,12 +15985,13 @@ declare interface UriPatternInit {
15949
15985
  */
15950
15986
  readonly hostname?: string;
15951
15987
  /**
15952
- * Slash-delimited path pattern. A final capture may use `:name*` to consume
15953
- * the remaining path segments, preserving `/` in the captured value. Unless the pattern itself
15954
- * ends in `/`, an input path ending in one `/` matches as if the slash were absent, so
15955
- * `threads/t1/` is `threads/t1` (captures and official spelling included) and a lone `/` after a
15956
- * host is the empty path; a doubled `//` is never dropped, nor the root path of an address with
15957
- * no host (`bfs:///`).
15988
+ * Slash-delimited path pattern, starting with `/`. `""` matches only an address with no path. A
15989
+ * final capture may use `:name*` to consume the remaining path segments, preserving `/` in the
15990
+ * captured value. A capture may end in a literal extension: `:name.md` matches `billing.md` and
15991
+ * captures `billing`. Unless the pattern itself ends in `/`, an input path ending in one `/`
15992
+ * matches as if the slash were absent, so `threads/t1/` is `threads/t1` (captures and official
15993
+ * spelling included) and a lone `/` after a host is the empty path; a doubled `//` is never
15994
+ * dropped, nor the root path of an address with no host (`bfs:///`).
15958
15995
  */
15959
15996
  readonly pathname?: string;
15960
15997
  /** `&`-delimited query pattern; `""` matches only an address with no query. */
package/dist/kay-app.js CHANGED
@@ -735,6 +735,8 @@ function createUnavailableSkills(message) {
735
735
  },
736
736
  list: async () => unavailable,
737
737
  read: async () => unavailable,
738
+ listPendingSuggestions: async () => unavailable,
739
+ subscribeSuggestions: () => () => void 0,
738
740
  history: async () => unavailable,
739
741
  diff: async () => unavailable,
740
742
  revision: async () => unavailable,
@@ -823,12 +823,13 @@ declare interface UriPatternInit {
823
823
  */
824
824
  readonly hostname?: string;
825
825
  /**
826
- * Slash-delimited path pattern. A final capture may use `:name*` to consume
827
- * the remaining path segments, preserving `/` in the captured value. Unless the pattern itself
828
- * ends in `/`, an input path ending in one `/` matches as if the slash were absent, so
829
- * `threads/t1/` is `threads/t1` (captures and official spelling included) and a lone `/` after a
830
- * host is the empty path; a doubled `//` is never dropped, nor the root path of an address with
831
- * no host (`bfs:///`).
826
+ * Slash-delimited path pattern, starting with `/`. `""` matches only an address with no path. A
827
+ * final capture may use `:name*` to consume the remaining path segments, preserving `/` in the
828
+ * captured value. A capture may end in a literal extension: `:name.md` matches `billing.md` and
829
+ * captures `billing`. Unless the pattern itself ends in `/`, an input path ending in one `/`
830
+ * matches as if the slash were absent, so `threads/t1/` is `threads/t1` (captures and official
831
+ * spelling included) and a lone `/` after a host is the empty path; a doubled `//` is never
832
+ * dropped, nor the root path of an address with no host (`bfs:///`).
832
833
  */
833
834
  readonly pathname?: string;
834
835
  /** `&`-delimited query pattern; `""` matches only an address with no query. */
@@ -73,7 +73,7 @@ function copyClaim(claim) {
73
73
  var SCHEME_RE = /^([a-z][a-z0-9+.-]*):\/\//i;
74
74
  var CAPTURE_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
75
75
  var REST_CAPTURE_RE = /^:([A-Za-z_][A-Za-z0-9_]*)\*$/;
76
- var CONSTRAINED_CAPTURE_RE = /^([A-Za-z_][A-Za-z0-9_]*)(?:\((.*)\))?$/;
76
+ var CONSTRAINED_CAPTURE_RE = /^([A-Za-z_][A-Za-z0-9_]*)(?:\((.*)\))?(\.[A-Za-z0-9]+)?$/;
77
77
  var MAX_CAPTURE_CONSTRAINT_LENGTH = 64;
78
78
  function compileUriClaim(claim) {
79
79
  if (claim.kind === "scheme") {
@@ -215,8 +215,10 @@ function compileParts(value, separator, options = {}) {
215
215
  const constraint = match[2];
216
216
  if (constraint !== void 0)
217
217
  assertSupportedConstraint(constraint);
218
+ const suffix = match[3];
218
219
  return {
219
220
  capture,
221
+ ...suffix !== void 0 ? { suffix } : {},
220
222
  ...constraint !== void 0 ? { constraint: new RegExp(`^(?:${constraint})$`) } : {}
221
223
  };
222
224
  }
@@ -236,8 +238,11 @@ function matchRoute(pattern, input) {
236
238
  if (pattern.hostname !== void 0 && !matchParts(pattern.hostname, input.hostname, isLoneCapture(pattern.hostname) ? void 0 : ".", captures, true)) {
237
239
  return void 0;
238
240
  }
239
- if (pattern.pathname !== void 0 && !matchParts(pattern.pathname, pattern.pathnameEndsInSlash === true ? input.pathname : withoutTrailingSlash(input), "/", captures)) {
240
- return void 0;
241
+ if (pattern.pathname !== void 0) {
242
+ const pathname = pattern.pathnameEndsInSlash === true ? input.pathname : withoutTrailingSlash(input);
243
+ if (pattern.pathname.length === 0 ? pathname !== "" : !matchParts(pattern.pathname, pathname, "/", captures)) {
244
+ return void 0;
245
+ }
241
246
  }
242
247
  if (pattern.search !== void 0 && (pattern.search.length === 0 ? input.search !== "" : !matchParts(pattern.search, input.search, "&", captures))) {
243
248
  return void 0;
@@ -271,7 +276,11 @@ function matchParts(pattern, value, separator, captures, foldLiterals = false) {
271
276
  return false;
272
277
  const actual = parts[partIndex];
273
278
  if (expected.capture !== void 0) {
274
- const decoded = safeDecode(actual);
279
+ const { suffix } = expected;
280
+ if (suffix !== void 0 && (!actual.endsWith(suffix) || actual.length === suffix.length)) {
281
+ return false;
282
+ }
283
+ const decoded = safeDecode(suffix === void 0 ? actual : actual.slice(0, -suffix.length));
275
284
  if (expected.constraint !== void 0 && !expected.constraint.test(decoded)) {
276
285
  return false;
277
286
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaklabs-ai/plugin-sdk",
3
- "version": "0.34.0",
3
+ "version": "0.36.0",
4
4
  "description": "The Kay plugin SDK — types and helpers for authoring a Kay directory plugin.",
5
5
  "author": "YakLabs",
6
6
  "type": "module",