@yaklabs-ai/plugin-sdk 0.13.2 → 0.13.4

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/dist/kay-agent.js CHANGED
@@ -1011,7 +1011,7 @@ async function readBodyWithByteCap(response, maxBytes) {
1011
1011
  }
1012
1012
 
1013
1013
  // packages/plugin-sdk/dist/kay-agent/index.js
1014
- import { z as z6 } from "zod";
1014
+ import { z as z7 } from "zod";
1015
1015
 
1016
1016
  // packages/plugin-sdk/dist/kay-agent/http-client.js
1017
1017
  var PluginApiError = class extends Error {
@@ -1164,6 +1164,163 @@ function bytesToBase64(bytes) {
1164
1164
  return output;
1165
1165
  }
1166
1166
 
1167
+ // packages/plugin-sdk/dist/kay-agent/osascript.js
1168
+ import { z as z6 } from "zod";
1169
+ var OSASCRIPT_TIMEOUT_MS = 3e4;
1170
+ var OSASCRIPT_ENVELOPE_JXA = `
1171
+ function jsonOk(data) {
1172
+ return JSON.stringify({ ok: true, data: data === undefined ? null : data });
1173
+ }
1174
+ function jsonFail(code, message) {
1175
+ return JSON.stringify({ ok: false, error: { code: String(code), message: String(message) } });
1176
+ }
1177
+ `;
1178
+ var envelopeSchema = z6.discriminatedUnion("ok", [
1179
+ z6.object({ ok: z6.literal(true), data: z6.unknown() }),
1180
+ z6.object({
1181
+ ok: z6.literal(false),
1182
+ error: z6.object({ code: z6.string(), message: z6.string() })
1183
+ })
1184
+ ]);
1185
+ function readOsascriptOutput(stdout) {
1186
+ let json;
1187
+ try {
1188
+ json = JSON.parse(stdout);
1189
+ } catch (error) {
1190
+ return {
1191
+ kind: "invalid",
1192
+ reason: `output that was not valid JSON: ${error instanceof Error ? error.message : String(error)}`
1193
+ };
1194
+ }
1195
+ const envelope = envelopeSchema.safeParse(json);
1196
+ if (!envelope.success) {
1197
+ return {
1198
+ kind: "invalid",
1199
+ reason: "output in a shape Bonsai does not know."
1200
+ };
1201
+ }
1202
+ return envelope.data.ok ? { kind: "data", data: envelope.data.data } : {
1203
+ kind: "refused",
1204
+ code: envelope.data.error.code,
1205
+ message: envelope.data.error.message
1206
+ };
1207
+ }
1208
+ function osascriptArgs(script, argv) {
1209
+ return ["-l", "JavaScript", "-e", script, "--", ...argv];
1210
+ }
1211
+ function parseOsascriptArgs(args) {
1212
+ const [language, name, flag, script, separator, ...argv] = args;
1213
+ if (language !== "-l" || name !== "JavaScript" || flag !== "-e" || script === void 0 || separator !== "--") {
1214
+ return void 0;
1215
+ }
1216
+ return { script, argv };
1217
+ }
1218
+ function withOutcome(call, message) {
1219
+ if (call.outcomeUnknown === void 0)
1220
+ return message;
1221
+ const sentence = /[.!?]$/.test(message) ? message : `${message}.`;
1222
+ return `${sentence} ${call.outcomeUnknown}`;
1223
+ }
1224
+ function createOsascriptRunner(options) {
1225
+ const failurePrefix = options.failurePrefix ?? `${options.label} automation failed`;
1226
+ const providerError = (call, providerCode, message) => new UriProviderError({
1227
+ moduleId: options.moduleId,
1228
+ operation: call.operation,
1229
+ uri: call.uri,
1230
+ message,
1231
+ providerCode,
1232
+ retryable: false
1233
+ });
1234
+ const requireExternalCli = (ctx, operation, uri) => {
1235
+ const externalCli = ctx.auth?.externalCli;
1236
+ if (externalCli === void 0) {
1237
+ throw new UriProviderError({
1238
+ moduleId: options.moduleId,
1239
+ operation,
1240
+ uri,
1241
+ message: `${options.label} access is not connected. Run service_connect for ${options.serviceId}.`,
1242
+ providerCode: "auth_required"
1243
+ });
1244
+ }
1245
+ return externalCli;
1246
+ };
1247
+ const dispatch = (simulator) => simulator === void 0 ? {
1248
+ requireExternalCli,
1249
+ externalCliOf: (ctx) => ctx.auth?.externalCli
1250
+ } : {
1251
+ requireExternalCli: (ctx, operation, uri) => {
1252
+ requireExternalCli(ctx, operation, uri);
1253
+ return simulator.runnerFor(ctx);
1254
+ },
1255
+ externalCliOf: (ctx) => ctx.auth?.externalCli === void 0 ? void 0 : simulator.runnerFor(ctx)
1256
+ };
1257
+ const scriptFailure = (call, result) => {
1258
+ const detail = (result.stderr.trim() !== "" ? result.stderr : result.stdout).trim();
1259
+ const base = `${failurePrefix}: ${detail !== "" ? detail : "osascript exited with a non-zero status."}`;
1260
+ const denied = options.automationDeniedAdvice;
1261
+ if (denied !== void 0 && (/-1743/.test(detail) || /not authorized/i.test(detail))) {
1262
+ return providerError(call, "automation-denied", withOutcome(call, `${base} ${denied}`));
1263
+ }
1264
+ const notRunning = options.appNotRunning;
1265
+ if (notRunning !== void 0 && (/-60[09]\b/.test(detail) || /isn.t running/i.test(detail))) {
1266
+ return providerError(call, notRunning.code, withOutcome(call, `${base} ${notRunning.advice}`));
1267
+ }
1268
+ return providerError(call, "script-failed", withOutcome(call, base));
1269
+ };
1270
+ const runScript = async (externalCli, script, argv, call) => {
1271
+ let result;
1272
+ try {
1273
+ result = await externalCli.run({
1274
+ args: osascriptArgs(script, argv),
1275
+ timeoutMs: call.timeoutMs ?? OSASCRIPT_TIMEOUT_MS,
1276
+ maxOutputChars: call.maxOutputChars,
1277
+ ...call.signal !== void 0 ? { signal: call.signal } : {}
1278
+ });
1279
+ } catch (error) {
1280
+ if (call.outcomeUnknown === void 0)
1281
+ throw error;
1282
+ throw providerError(call, "script-failed", withOutcome(call, `${failurePrefix}: ${error instanceof Error ? error.message : String(error)}`));
1283
+ }
1284
+ if (result.exitCode !== 0)
1285
+ throw scriptFailure(call, result);
1286
+ if (result.stdoutTruncated === true) {
1287
+ throw providerError(call, "output-truncated", withOutcome(call, `${options.label} script output was truncated; ${call.truncatedAdvice ?? "narrow the request"}.`));
1288
+ }
1289
+ const output = readOsascriptOutput(result.stdout);
1290
+ switch (output.kind) {
1291
+ case "data":
1292
+ return output.data;
1293
+ case "refused":
1294
+ throw providerError(call, output.code, output.message);
1295
+ case "invalid":
1296
+ throw providerError(call, "output-invalid", withOutcome(call, `${options.label} script produced ${output.reason}`));
1297
+ }
1298
+ };
1299
+ return {
1300
+ requireExternalCli,
1301
+ dispatch,
1302
+ runScript,
1303
+ providerError,
1304
+ scriptFailure
1305
+ };
1306
+ }
1307
+
1308
+ // packages/plugin-sdk/dist/kay-agent/untrusted-text.js
1309
+ function lineOf(value) {
1310
+ return (value ?? "").trim().replace(/\s+/g, " ");
1311
+ }
1312
+ function labelOf(value) {
1313
+ return lineOf((value ?? "").replace(/[\p{Cc}\p{Zl}\p{Zp}]/gu, " ").replace(/\p{Cf}/gu, ""));
1314
+ }
1315
+ function markdownText(text) {
1316
+ return text.replace(/[\\[\]!<|]/g, (character) => `\\${character}`);
1317
+ }
1318
+ function fenced(text) {
1319
+ const runs = text.match(/`+/g) ?? [];
1320
+ const fence = "`".repeat(Math.max(3, ...runs.map((run) => run.length + 1)));
1321
+ return [`${fence}text`, text, fence];
1322
+ }
1323
+
1167
1324
  // packages/plugin-sdk/dist/kay-agent/advertisement.js
1168
1325
  function defineKayAgentAdvertisement(advertisement) {
1169
1326
  return advertisement;
@@ -1399,6 +1556,8 @@ export {
1399
1556
  KayAgentCapabilityError,
1400
1557
  KayAgentWebSessionError,
1401
1558
  OBSERVATION_MIME,
1559
+ OSASCRIPT_ENVELOPE_JXA,
1560
+ OSASCRIPT_TIMEOUT_MS,
1402
1561
  PluginApiError,
1403
1562
  SearchProviderError,
1404
1563
  URI_TEXT_EDIT_SHAPE_SUMMARY,
@@ -1420,28 +1579,36 @@ export {
1420
1579
  contentResult,
1421
1580
  createFallbackOnlyWithJev,
1422
1581
  createJsonApiClient,
1582
+ createOsascriptRunner,
1423
1583
  declareProducedResources,
1424
1584
  defineAuthedTool,
1425
1585
  defineKayAgentAdvertisement,
1426
1586
  defineTool,
1427
1587
  describeUriTextEditTarget,
1588
+ fenced,
1428
1589
  formatIsoDate,
1429
1590
  formatNumber,
1430
1591
  getBytes,
1431
1592
  isDelegatedRefreshRejected,
1432
1593
  jsonResource,
1594
+ labelOf,
1595
+ lineOf,
1433
1596
  lintUriHelpContracts,
1434
1597
  lintUriTemplate,
1435
1598
  markdownResource,
1599
+ markdownText,
1436
1600
  num,
1437
1601
  numLike,
1438
1602
  observationFromResource,
1439
1603
  observationResource,
1604
+ osascriptArgs,
1440
1605
  parseLineSelector,
1606
+ parseOsascriptArgs,
1441
1607
  parseUriConnectionSelector,
1442
1608
  parseUriTextEditPayload,
1443
1609
  payload,
1444
1610
  readBodyWithByteCap,
1611
+ readOsascriptOutput,
1445
1612
  str,
1446
1613
  textResult,
1447
1614
  toJsonSchema,
@@ -1452,5 +1619,5 @@ export {
1452
1619
  uriTextEditPayloadSchema,
1453
1620
  withResolvedAuth,
1454
1621
  withResolvedSearchProvider,
1455
- z6 as z
1622
+ z7 as z
1456
1623
  };
package/dist/kay-app.d.ts CHANGED
@@ -224,6 +224,10 @@ declare interface BackgroundTaskCompletion {
224
224
  readonly resultDelivery?: BackgroundTaskResultDelivery;
225
225
  readonly output?: string;
226
226
  readonly error?: string;
227
+ /** The thrown Error's stack, when a failed task threw one. */
228
+ readonly errorStack?: string;
229
+ /** Why a failed AGENT task's run failed, classified from what it threw; absent for other kinds. */
230
+ readonly failureReason?: RunFailureReason;
227
231
  }
228
232
 
229
233
  declare type BackgroundTaskInspector = () => Promise<string> | string;
@@ -437,6 +441,12 @@ export declare function createUnavailableSkills(message: string): KayAppSkills;
437
441
  */
438
442
  export declare function createUnavailableTools(): KayAppTools;
439
443
 
444
+ /**
445
+ * A `KayAppUsage` that answers `unavailable` to every call: what a host with no telemetry lane
446
+ * binds, so `app.usage` stays present on every host.
447
+ */
448
+ export declare function createUnavailableUsage(message: string): KayAppUsage;
449
+
440
450
  declare interface CustomEntry extends EntryBase {
441
451
  type: "custom";
442
452
  customType: string;
@@ -765,7 +775,7 @@ export declare interface JevScoreQuestion {
765
775
  * - The wire. The host serves `classify` over whichever route the user's Jev provider order
766
776
  * picks; a plugin never sees a vendor client, a vendor type, or a route.
767
777
  * - Credential resolution, the `jev.use` decision, the Jev-branch budget, or usage accounting.
768
- * Those are host-side (`@kay/agent-runtime`'s `kay/jev/`).
778
+ * Those are host-side (`@bonsai/agent-runtime`'s `kay/jev/`).
769
779
  * ---
770
780
  *
771
781
  * The vocabulary is Bonsai's own (SDK 0.12.0). Before it, `use` received TypeSafe's `TypeSafeClient`
@@ -1155,6 +1165,24 @@ export declare interface KayAppAgentDefinition {
1155
1165
  * position: the two are separately compared, so moving a rule between them is a different claim.
1156
1166
  */
1157
1167
  readonly permissionsFallback?: readonly PluginAgentDefinitionPermissionRule[];
1168
+ /**
1169
+ * The URI schemes this definition's runs MAINTAIN, e.g. `["memory"]` for a job that curates,
1170
+ * prunes or rewrites the memory notebook. Bare scheme names, lowercase, no `://`.
1171
+ *
1172
+ * What it changes, and only this: a READ under a declared scheme, made by a session of this
1173
+ * definition or by any session in that session's family (its subagents, keyed by root), is not
1174
+ * counted as use of the resource in Bonsai's per-resource read activity (the `uriReadActivity`
1175
+ * metric, `analytics://memory`, and `app.usage`'s answers). A job that keeps a notebook tidy
1176
+ * reads old and irrelevant notes as a matter of course, and counting those reads would make
1177
+ * every note it touches look used. Reads by every other session, including other background
1178
+ * jobs, still count: a job that reads a note to do its own work found the note useful.
1179
+ *
1180
+ * It confers no authority and removes none. The host re-reads it from the live definition at
1181
+ * every activation, like {@link permissions}, and stamps it on the session's activation row.
1182
+ * Adding or removing one scheme here is the whole switch for moving a job between the excluded
1183
+ * and the counted side. Absent means the job maintains nothing. SDK 0.13.3.
1184
+ */
1185
+ readonly maintains?: readonly string[];
1158
1186
  }
1159
1187
 
1160
1188
  /** The inert session-config subset a plugin-authored agent definition may contribute. */
@@ -1927,8 +1955,8 @@ export declare interface KayAppConfig {
1927
1955
  /**
1928
1956
  * Where a folded config value's winner was defined: the config LEVEL that won and the absolute
1929
1957
  * path of the winning file. A JSON-safe twin of the runtime's `KayConfigValueOrigin`
1930
- * (`@kay/product-catalog`) — this SDK cannot import that package, so the shape is re-declared
1931
- * here and an assignability test in `@kay/agent-runtime` pins the two together. The level union
1958
+ * (`@bonsai/product-catalog`) — this SDK cannot import that package, so the shape is re-declared
1959
+ * here and an assignability test in `@bonsai/agent-runtime` pins the two together. The level union
1932
1960
  * is the closed, protocol-owned fold precedence stack (`baseline < learned < global < project`).
1933
1961
  * `"baseline"` names a host-composed, in-memory, build/channel-scoped default with no file
1934
1962
  * behind it (`KayConfigStoreOptions.baselineDefaults`) — a plugin can still be handed one as a
@@ -2677,8 +2705,8 @@ export declare interface KayAppPermissions {
2677
2705
  export declare interface KayAppPhaseShiftSessionEvent extends KayAppSessionEventBase {
2678
2706
  readonly type: "phase-shift";
2679
2707
  /**
2680
- * The accepted observation, in the SURFACE-FRIENDLY resolved shape (`@kay/plugin-protocol`'s
2681
- * `PhaseShiftTimelineRecord`, re-exported from `@kay/plugin-sdk/phase-shift`) rather than the
2708
+ * The accepted observation, in the SURFACE-FRIENDLY resolved shape (`@bonsai/plugin-protocol`'s
2709
+ * `PhaseShiftTimelineRecord`, re-exported from `@bonsai/plugin-sdk/phase-shift`) rather than the
2682
2710
  * raw validated one: source timestamps already resolved, total and pure, no entry read needed
2683
2711
  * to place it in time. Re-validate its onsets against the branch you actually read — a rewind
2684
2712
  * can race between publish and read — the same discipline `resolveClosedPhase` already applies.
@@ -2877,6 +2905,16 @@ export declare interface KayAppPluginApi {
2877
2905
  * Always present, like {@link skills}.
2878
2906
  */
2879
2907
  readonly tools: KayAppTools;
2908
+ /**
2909
+ * Read and change activity for the resources under this plugin's OWN URI schemes, counted under
2910
+ * Bonsai's read rule (reads by a job that declares it maintains the scheme never count). Each
2911
+ * call checks, against the live registry, that the asked prefix is under a scheme this plugin's
2912
+ * registered URI modules serve; any other prefix resolves to `{ status: "refused" }`. A host
2913
+ * with no telemetry lane, or a scan that cannot complete, resolves to `{ status: "unavailable" }`.
2914
+ * No call rejects while the plugin is enabled; after it is disabled, a kept handle's method
2915
+ * throws synchronously. Always present, like {@link skills}.
2916
+ */
2917
+ readonly usage: KayAppUsage;
2880
2918
  /** The app-wide endpoint bus (register handlers a consuming surface invokes). */
2881
2919
  readonly endpoints: KayAppEndpoints;
2882
2920
  /** Publish one event declared by this plugin's `kay.app` manifest target. */
@@ -3090,6 +3128,17 @@ export declare interface KayAppResolvedCredential {
3090
3128
  readonly fields?: Readonly<Record<string, string>>;
3091
3129
  }
3092
3130
 
3131
+ /**
3132
+ * What one run produced, as named counts: `{ suggestions: 2, pullRequests: 0 }`. Optional, and
3133
+ * read by Bonsai's own analytics, not shown to a person.
3134
+ *
3135
+ * Keys are your own names, in camelCase (a letter first, then letters and digits, at most 40
3136
+ * characters), at most 16 per run; values are whole numbers of zero or more. Report a zero rather
3137
+ * than leaving a key out: "made nothing" is the answer a reader is usually asking about. A result
3138
+ * that breaks these rules is dropped whole, with the run otherwise recorded as usual.
3139
+ */
3140
+ export declare type KayAppRunProduced = Readonly<Record<string, number>>;
3141
+
3093
3142
  export declare interface KayAppRunSummary {
3094
3143
  /** One plain-text line, at most 100 characters. */
3095
3144
  readonly headline: string;
@@ -3100,8 +3149,8 @@ export declare interface KayAppRunSummary {
3100
3149
 
3101
3150
  /**
3102
3151
  * The session provenance vocabulary, as the plugin protocol declares it. These used to be spelled
3103
- * out here because the canonical schemas lived in `@kay/agent-runtime-protocol`, which this
3104
- * package may not import; they now live in `@kay/plugin-protocol` (so artifact writers and the
3152
+ * out here because the canonical schemas lived in `@bonsai/agent-runtime-protocol`, which this
3153
+ * package may not import; they now live in `@bonsai/plugin-protocol` (so artifact writers and the
3105
3154
  * daemon share one shape), and a mirror here would be exactly the second declaration the
3106
3155
  * placement law forbids. Each name is kept as an alias so existing plugin code keeps compiling.
3107
3156
  */
@@ -3236,6 +3285,12 @@ export declare interface KayAppSessionEventsSubscribeOptions {
3236
3285
  * immediately.
3237
3286
  */
3238
3287
  readonly serialize?: boolean;
3288
+ /**
3289
+ * Your own name for this handler (the id you would grep a log for). The host names it, with
3290
+ * your plugin id, on the record it writes when the handler throws, so a failure says whose it
3291
+ * was. Optional; unnamed handlers are recorded without it.
3292
+ */
3293
+ readonly handlerId?: string;
3239
3294
  readonly handler: (event: KayAppSessionEvent, context: KayAppSessionEventContext) => void | Promise<void>;
3240
3295
  }
3241
3296
 
@@ -3734,7 +3789,7 @@ export declare type KayAppSignalsAnswerFilter = {
3734
3789
  */
3735
3790
  export declare interface KayAppSignalsDetectorInput {
3736
3791
  /**
3737
- * Stable identifier, e.g. `memory-curator-gate`: a lowercase slug (a-z and 0-9 in words joined
3792
+ * Stable identifier, e.g. `memory-packet`: a lowercase slug (a-z and 0-9 in words joined
3738
3793
  * by single hyphens), at most 64 characters, plugin-scoped. It becomes a `signals://` path
3739
3794
  * segment.
3740
3795
  */
@@ -4169,7 +4224,7 @@ export declare interface KayAppTimeSnapshot {
4169
4224
  * The time zone the user is in, as an IANA id in the spelling ICU resolves (`America/New_York`).
4170
4225
  * One zone can have two ids, and ICU picks one: `Asia/Kolkata` comes back as `Asia/Calcutta`. So
4171
4226
  * never compare zone ids with `===`. A first-party plugin compares with `sameZone` from
4172
- * `@kay/time`. A plugin that imports only this SDK and node builtins resolves each id with
4227
+ * `@bonsai/time`. A plugin that imports only this SDK and node builtins resolves each id with
4173
4228
  * `new Intl.DateTimeFormat("en-US", { timeZone: id }).resolvedOptions().timeZone` and compares the
4174
4229
  * results. Pass `zone` as the `timeZone` of `Intl.DateTimeFormat` or `toLocaleString` to format a
4175
4230
  * time for the user.
@@ -4365,7 +4420,7 @@ export declare interface KayAppTurnSettledSessionEvent extends KayAppSessionEven
4365
4420
  *
4366
4421
  * Optional on this SDK-facing type only for fixture leniency (a handcrafted test event may omit
4367
4422
  * it); the daemon's own emitter always populates it for a genuine settle — see
4368
- * `TurnSettledSessionEvent.runId` (`@kay/agent-runtime`'s internal twin of this event, which
4423
+ * `TurnSettledSessionEvent.runId` (`@bonsai/agent-runtime`'s internal twin of this event, which
4369
4424
  * DOES require it) for the producer side of that guarantee.
4370
4425
  */
4371
4426
  readonly runId?: string;
@@ -4398,6 +4453,97 @@ export declare interface KayAppUiActionSessionEvent extends KayAppSessionEventBa
4398
4453
  readonly toolCallId: string;
4399
4454
  }
4400
4455
 
4456
+ /** How one resource under the asked prefix has been read and changed. */
4457
+ export declare interface KayAppUriActivity {
4458
+ /** The canonical URI as the read or change recorded it. */
4459
+ readonly uri: string;
4460
+ /** One entry per window, in `KayAppUriActivityAnswer.windowDays` order. */
4461
+ readonly windows: readonly KayAppUriActivityWindow[];
4462
+ /** The newest counted read, ISO-8601 UTC; absent when none fell inside the scan. */
4463
+ readonly lastReadAt?: string;
4464
+ /** The newest change, ISO-8601 UTC; absent when none fell inside the scan. */
4465
+ readonly lastChangedAt?: string;
4466
+ }
4467
+
4468
+ export declare type KayAppUriActivityAnswer = {
4469
+ readonly status: "ok";
4470
+ /** The instant the windows are measured back from, ISO-8601 UTC. */
4471
+ readonly asOf: string;
4472
+ /** The windows every resource reports, in order. */
4473
+ readonly windowDays: readonly number[];
4474
+ /**
4475
+ * Every resource under the prefix the scan saw read or changed. One read only by a
4476
+ * maintaining job is listed with zero counts and no `lastReadAt`.
4477
+ */
4478
+ readonly resources: readonly KayAppUriActivity[];
4479
+ } | {
4480
+ /** The prefix is not under a scheme this plugin's registered URI modules serve. */
4481
+ readonly status: "refused";
4482
+ readonly message: string;
4483
+ } | {
4484
+ /** No telemetry lane on this host, or the scan could not complete. */
4485
+ readonly status: "unavailable";
4486
+ readonly message: string;
4487
+ };
4488
+
4489
+ /**
4490
+ * The plugin-facing `kay.app` usage contract: how the resources under a plugin's OWN URI schemes
4491
+ * have been read and changed, as Bonsai's telemetry records it. SDK 0.13.3; a plugin that calls it
4492
+ * declares `compatibility.sdk` `^0.13.3`.
4493
+ *
4494
+ * Scoped, not gated. A plugin may ask only about a prefix under a scheme one of its own registered
4495
+ * URI modules serves; that check runs at every call against the live registry, and a prefix under
4496
+ * any other scheme answers `{ status: "refused" }`. The member is ALWAYS present and no call
4497
+ * rejects while the plugin is enabled: no telemetry lane on this host, a query that could not
4498
+ * complete, or an answer too large to return all resolve to `{ status: "unavailable" }`. After the
4499
+ * plugin is disabled, a kept handle's method throws synchronously.
4500
+ *
4501
+ * ## What counts as a read
4502
+ *
4503
+ * Every executed read counts, from any session (a person's conversation and its subagents,
4504
+ * automations, loops, watches, Dream passes, any other job), EXCEPT a read made by a session whose
4505
+ * agent definition declares that it maintains the read's scheme (`KayAppAgentDefinition.maintains`)
4506
+ * or by any session in that session's family. Each counted read is split by who it serves:
4507
+ * `personReads` when its session tree was started by a person, `jobReads` otherwise. Changes
4508
+ * (writes and edits) count from every session, maintainers included.
4509
+ *
4510
+ * Reads recorded before the host build that stamps the declaration follow the earlier rule until
4511
+ * they leave the 90-day window: such a read counts only when a person started the session that made it (a person's subagents
4512
+ * did not count under that rule).
4513
+ *
4514
+ * ## Zero and unavailable are different answers
4515
+ *
4516
+ * An `ok` answer describes a complete scan: a resource absent from `resources` was neither read
4517
+ * nor changed inside the scan, so it has zero counted reads. When the scan could not complete the answer is `unavailable`,
4518
+ * never a list of zeros, because "nothing read this" and "nothing could tell" are different facts.
4519
+ */
4520
+ /** One window's activity for one resource. */
4521
+ export declare interface KayAppUriActivityWindow {
4522
+ /** The span, in days, measured back from the answer's `asOf`. */
4523
+ readonly windowDays: number;
4524
+ /** Counted reads: `personReads + jobReads`. */
4525
+ readonly reads: number;
4526
+ /** Counted reads whose session tree a person started. */
4527
+ readonly personReads: number;
4528
+ /** Counted reads by every other session tree (automations, Dream passes, other jobs). */
4529
+ readonly jobReads: number;
4530
+ /** Distinct session trees (keyed by root session) behind the counted reads. */
4531
+ readonly sessions: number;
4532
+ /** Writes and edits of any origin, maintainers included. */
4533
+ readonly changes: number;
4534
+ }
4535
+
4536
+ export declare interface KayAppUsage {
4537
+ /**
4538
+ * Read and change activity for every resource whose URI starts with `prefix`, for example
4539
+ * `memory://` or `memory://projects/`. The prefix must name a scheme (`<scheme>://`) that one of
4540
+ * this plugin's registered URI modules serves.
4541
+ */
4542
+ uriActivity(input: {
4543
+ readonly prefix: string;
4544
+ }): Promise<KayAppUriActivityAnswer>;
4545
+ }
4546
+
4401
4547
  export declare interface KayAppUserFileEntry {
4402
4548
  readonly name: string;
4403
4549
  /** From the directory entry itself: a symlink is reported as `symlink`, never followed. */
@@ -4680,6 +4826,7 @@ export declare interface KayAppWorkflowDefinition {
4680
4826
  }>;
4681
4827
  readonly run: (context: KayAppWorkflowContext) => Promise<{
4682
4828
  readonly summary: KayAppRunSummary;
4829
+ readonly produced?: KayAppRunProduced;
4683
4830
  }>;
4684
4831
  }
4685
4832
 
@@ -4750,7 +4897,7 @@ export declare interface KayAppWorkspace {
4750
4897
  /**
4751
4898
  * The full workspace arrangement verb catalog: tab lifecycle/reorder/grouping, pane split/close/
4752
4899
  * focus/maximize/restore/place, shell panel toggles, and opening a session. A JSON-safe twin of
4753
- * `KayWorkspaceCommand` in `@kay/agent-runtime-protocol`.
4900
+ * `KayWorkspaceCommand` in `@bonsai/agent-runtime-protocol`.
4754
4901
  */
4755
4902
  export declare type KayAppWorkspaceCommand = {
4756
4903
  verb: "tabs.open";
@@ -4828,7 +4975,14 @@ export declare type KayAppWorkspaceCommand = {
4828
4975
  verb: "shell.togglePanel";
4829
4976
  } | {
4830
4977
  verb: "shell.toggleInspector";
4831
- } | {
4978
+ }
4979
+ /**
4980
+ * Open a session without taking the user's focus. A session no pane shows gets a tab of its own
4981
+ * in the background (`opened`); one some pane already shows is left as it is (`applied`). The
4982
+ * active tab and every focus stay as they were. The wire's `reveal` flag, which asks for a
4983
+ * sidebar-click-style open, is not offered here: it is for Bonsai Desktop's own menus.
4984
+ */
4985
+ | {
4832
4986
  verb: "sessions.open";
4833
4987
  sessionId: string;
4834
4988
  title?: string | undefined;
@@ -4836,7 +4990,7 @@ export declare type KayAppWorkspaceCommand = {
4836
4990
 
4837
4991
  /**
4838
4992
  * The one outcome grammar every {@link KayAppWorkspaceCommand} resolves to. A JSON-safe twin of
4839
- * `WorkspaceCommandOutcome` in `@kay/agent-runtime-protocol`.
4993
+ * `WorkspaceCommandOutcome` in `@bonsai/agent-runtime-protocol`.
4840
4994
  */
4841
4995
  export declare type KayAppWorkspaceCommandOutcome = "applied" | "opened" | "focused-existing" | "no-room" | "no-window" | "not-found" | "invalid";
4842
4996
 
@@ -4847,8 +5001,8 @@ export declare interface KayAppWorkspaceExecuteContext {
4847
5001
 
4848
5002
  /**
4849
5003
  * What one pane item shows. A JSON-safe, hand-copied twin of `WorkspacePaneContent` in
4850
- * `@kay/agent-runtime-protocol` — this package cannot import that protocol package (see this
4851
- * module's own doc comment), so the shape is re-declared here. A parity check in `@kay/agent-runtime`
5004
+ * `@bonsai/agent-runtime-protocol` — this package cannot import that protocol package (see this
5005
+ * module's own doc comment), so the shape is re-declared here. A parity check in `@bonsai/agent-runtime`
4852
5006
  * asserts `app.workspace` (the runtime's `KayWorkspaceCapability`, built from the protocol types)
4853
5007
  * stays structurally assignable to {@link KayAppWorkspace} below.
4854
5008
  *
@@ -5592,14 +5746,14 @@ declare interface ScopeRequirement {
5592
5746
  * this list. Two consequences worth knowing:
5593
5747
  *
5594
5748
  * - `sessions.create` and `sessions.run` are the two separately-granted plugin doors
5595
- * (`@kay/plugin-protocol`'s `permissions.ts`: `create` mints a persisted user-visible session,
5749
+ * (`@bonsai/plugin-protocol`'s `permissions.ts`: `create` mints a persisted user-visible session,
5596
5750
  * `run` executes a hidden headless one). Each names ITSELF at the site that mints the session;
5597
5751
  * neither is inferred from the surface string, which cannot tell them apart.
5598
5752
  * - `sessions.create` stamps a plugin creator only for a create that declares a plugin-surfaced
5599
5753
  * `system` origin. The SDK `create` door passes no origin today, so its sessions read as the
5600
5754
  * ordinary chats they are; the value is still the honest one for that door when it does.
5601
5755
  * - `agents.run` (G1, 2026-09) is the generic session-event run door
5602
- * (`app.agents.run(ref, input, cause, {fork?})`, `@kay/plugin-sdk`'s `kay-app/agents.ts`): a
5756
+ * (`app.agents.run(ref, input, cause, {fork?})`, `@bonsai/plugin-sdk`'s `kay-app/agents.ts`): a
5603
5757
  * plugin's `app.sessionEvents.subscribe` handler spins up a bounded agent run in response to a
5604
5758
  * published session event. `callerSessionId` carries the cause's `sourceSessionId` — the session
5605
5759
  * whose event caused this run — and {@link SessionProvenanceRecord.causedByEvent} carries the
@@ -7339,7 +7493,7 @@ declare interface UriValidationMessage {
7339
7493
  * applies label changes"), never wire mechanics or URI shapes. Verbs the
7340
7494
  * module does not support are omitted; at runtime a supported verb with no
7341
7495
  * entry simply renders undocumented (fail-open metadata) — but for a plugin's
7342
- * `kay.agent` URI modules, `auditPluginDisclosure` (`@kay/plugin-sdk/
7496
+ * `kay.agent` URI modules, `auditPluginDisclosure` (`@bonsai/plugin-sdk/
7343
7497
  * disclosure`) enforces coverage of every supported verb at authoring/load
7344
7498
  * time.
7345
7499
  *
package/dist/kay-app.js CHANGED
@@ -538,6 +538,13 @@ function transcriptRoleLabel(role) {
538
538
  return role === "user" ? "User" : "Assistant";
539
539
  }
540
540
 
541
+ // packages/plugin-sdk/dist/kay-app/usage.js
542
+ function createUnavailableUsage(message) {
543
+ return {
544
+ uriActivity: async () => ({ status: "unavailable", message })
545
+ };
546
+ }
547
+
541
548
  // packages/plugin-sdk/dist/kay-app/workflows.js
542
549
  var KayAppWorkflowFailure = class extends Error {
543
550
  summary;
@@ -580,6 +587,7 @@ export {
580
587
  createFallbackOnlyWithJev,
581
588
  createUnavailableSkills,
582
589
  createUnavailableTools,
590
+ createUnavailableUsage,
583
591
  invalidObservation,
584
592
  isRetryable,
585
593
  parseLineSelector,
package/dist/signals.d.ts CHANGED
@@ -86,7 +86,7 @@ declare interface JevScoreQuestion {
86
86
  * - The wire. The host serves `classify` over whichever route the user's Jev provider order
87
87
  * picks; a plugin never sees a vendor client, a vendor type, or a route.
88
88
  * - Credential resolution, the `jev.use` decision, the Jev-branch budget, or usage accounting.
89
- * Those are host-side (`@kay/agent-runtime`'s `kay/jev/`).
89
+ * Those are host-side (`@bonsai/agent-runtime`'s `kay/jev/`).
90
90
  * ---
91
91
  *
92
92
  * The vocabulary is Bonsai's own (SDK 0.12.0). Before it, `use` received TypeSafe's `TypeSafeClient`
@@ -301,7 +301,7 @@ export declare type KayAppSignalsAnswerFilter = {
301
301
  */
302
302
  export declare interface KayAppSignalsDetectorInput {
303
303
  /**
304
- * Stable identifier, e.g. `memory-curator-gate`: a lowercase slug (a-z and 0-9 in words joined
304
+ * Stable identifier, e.g. `memory-packet`: a lowercase slug (a-z and 0-9 in words joined
305
305
  * by single hyphens), at most 64 characters, plugin-scoped. It becomes a `signals://` path
306
306
  * segment.
307
307
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaklabs-ai/plugin-sdk",
3
- "version": "0.13.2",
3
+ "version": "0.13.4",
4
4
  "description": "The Kay plugin SDK — types and helpers for authoring a Kay directory plugin.",
5
5
  "author": "YakLabs",
6
6
  "type": "module",