@yaklabs-ai/plugin-sdk 0.13.3 → 0.13.5

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-app.js CHANGED
@@ -258,6 +258,8 @@ var UriProviderError = class extends UriError {
258
258
  retryable;
259
259
  /** Provider-directed delay before retrying, in milliseconds. */
260
260
  retryAfterMs;
261
+ /** See {@link UriProviderErrorOptions.effect}. */
262
+ effect;
261
263
  constructor(options) {
262
264
  super(options.message);
263
265
  this.name = "UriProviderError";
@@ -276,6 +278,8 @@ var UriProviderError = class extends UriError {
276
278
  }
277
279
  this.retryAfterMs = options.retryAfterMs;
278
280
  }
281
+ if (options.effect !== void 0)
282
+ this.effect = options.effect;
279
283
  }
280
284
  };
281
285
  var UriNotFoundError = class extends UriError {
@@ -62,7 +62,7 @@ export declare interface PhaseShiftTimelineChange {
62
62
  * version-1 record's source timestamps from the entries it referenced. Those
63
63
  * references are branch-validated by the fold, so the resolution is total.
64
64
  */
65
- export declare function phaseShiftTimelineFromEntries(entries: readonly TranscriptSourceEntry[]): readonly PhaseShiftTimelineRecord[];
65
+ export declare function phaseShiftTimelineFromEntries(entries: readonly TranscriptSourceEntryLike[]): readonly PhaseShiftTimelineRecord[];
66
66
 
67
67
  /**
68
68
  * One accepted detector check, as a surface reads it.
@@ -96,7 +96,7 @@ export declare interface PhaseShiftTimelineRecord {
96
96
  }
97
97
 
98
98
  /** The fields the customType selector needs off a session entry. */
99
- declare interface TranscriptEntry {
99
+ declare interface TranscriptEntryLike {
100
100
  readonly id: string;
101
101
  readonly type: string;
102
102
  readonly customType?: string;
@@ -108,7 +108,7 @@ declare interface TranscriptEntry {
108
108
  * A tree entry plus the ISO timestamp the timeline projections read alongside
109
109
  * the walk result (`turnSegments`, `phaseShiftTimelineFromEntries`).
110
110
  */
111
- declare interface TranscriptSourceEntry extends TranscriptTreeEntry {
111
+ declare interface TranscriptSourceEntryLike extends TranscriptTreeEntryLike {
112
112
  readonly timestamp: string;
113
113
  }
114
114
 
@@ -119,7 +119,7 @@ declare interface TranscriptSourceEntry extends TranscriptTreeEntry {
119
119
  * v3 log carries an ISO string, so requiring either shape here would force one
120
120
  * class of caller to convert entries it only wants walked.
121
121
  */
122
- declare interface TranscriptTreeEntry extends TranscriptEntry {
122
+ declare interface TranscriptTreeEntryLike extends TranscriptEntryLike {
123
123
  readonly parentId?: string | null;
124
124
  readonly targetId?: string | null;
125
125
  }
@@ -1,7 +1,13 @@
1
1
  // packages/plugin-protocol/dist/phase-shift.js
2
- import { z } from "zod";
2
+ import { z as z2 } from "zod";
3
3
 
4
4
  // packages/plugin-protocol/dist/transcript.js
5
+ import { z } from "zod";
6
+ var WorkBlockCoverageSchema = z.strictObject({
7
+ firstToolResultEntryId: z.string().min(1),
8
+ lastToolResultEntryId: z.string().min(1),
9
+ toolResultEntryIds: z.array(z.string().min(1)).min(1).max(4096)
10
+ }).refine((value) => value.firstToolResultEntryId === value.toolResultEntryIds[0] && value.lastToolResultEntryId === value.toolResultEntryIds.at(-1) && new Set(value.toolResultEntryIds).size === value.toolResultEntryIds.length);
5
11
  function branchEntryIds(entries) {
6
12
  const last = entries[entries.length - 1];
7
13
  if (last === void 0)
@@ -23,48 +29,48 @@ var PHASE_SHIFT_ENTRY_NAME = "observation";
23
29
  var PHASE_SHIFT_CUSTOM_TYPE = `ext:${PHASE_SHIFT_EXTENSION_ID}:${PHASE_SHIFT_ENTRY_NAME}`;
24
30
  var PHASE_SHIFT_TIMELINE_CUSTOM_TYPE = `ext:${PHASE_SHIFT_EXTENSION_ID}:timeline`;
25
31
  var PHASE_SHIFT_DETECTOR_VERSION = "activity-memory-v1";
26
- var entryId = z.string().min(1).max(256);
27
- var purpose = z.string().trim().min(1).max(320);
28
- var sourceTime = z.iso.datetime();
29
- var phaseShiftMemorySchema = z.object({
32
+ var entryId = z2.string().min(1).max(256);
33
+ var purpose = z2.string().trim().min(1).max(320);
34
+ var sourceTime = z2.iso.datetime();
35
+ var phaseShiftMemorySchema = z2.object({
30
36
  startEntryId: entryId,
31
37
  purpose,
32
- anchors: z.array(z.object({ entryId, excerpt: z.string().max(250) })).min(1).max(3)
38
+ anchors: z2.array(z2.object({ entryId, excerpt: z2.string().max(250) })).min(1).max(3)
33
39
  });
34
- var phaseShiftCostSchema = z.object({
35
- amountUsd: z.number().nonnegative(),
36
- amountBasis: z.enum(["measured", "list-estimate", "unknown"]),
37
- inputTokens: z.number().int().nonnegative(),
38
- outputTokens: z.number().int().nonnegative(),
40
+ var phaseShiftCostSchema = z2.object({
41
+ amountUsd: z2.number().nonnegative(),
42
+ amountBasis: z2.enum(["measured", "list-estimate", "unknown"]),
43
+ inputTokens: z2.number().int().nonnegative(),
44
+ outputTokens: z2.number().int().nonnegative(),
39
45
  /**
40
46
  * The highest provider-attempt ordinal this check accounted for; recovery
41
47
  * after a failure makes it more than one. An attempt that threw before
42
48
  * reporting usage is invisible to accounting, so this is a lower bound on
43
49
  * what the provider was actually asked to do.
44
50
  */
45
- attempts: z.number().int().positive()
51
+ attempts: z2.number().int().positive()
46
52
  });
47
53
  var observationFields = {
48
- detectorVersion: z.literal(PHASE_SHIFT_DETECTOR_VERSION),
49
- modelReference: z.string().min(1).max(256),
50
- observedAt: z.iso.datetime(),
54
+ detectorVersion: z2.literal(PHASE_SHIFT_DETECTOR_VERSION),
55
+ modelReference: z2.string().min(1).max(256),
56
+ observedAt: z2.iso.datetime(),
51
57
  throughEntryId: entryId,
52
- windowEntryIds: z.array(entryId).min(1).max(40),
58
+ windowEntryIds: z2.array(entryId).min(1).max(40),
53
59
  memory: phaseShiftMemorySchema
54
60
  };
55
61
  var changeFields = {
56
62
  startEntryId: entryId,
57
- relation: z.enum(["outcome", "phase"]),
58
- reason: z.string().trim().min(1).max(2e3),
63
+ relation: z2.enum(["outcome", "phase"]),
64
+ reason: z2.string().trim().min(1).max(2e3),
59
65
  purposeAfter: purpose
60
66
  };
61
- var phaseShiftObservationV1Schema = z.object({
62
- version: z.literal(1),
67
+ var phaseShiftObservationV1Schema = z2.object({
68
+ version: z2.literal(1),
63
69
  ...observationFields,
64
- changes: z.array(z.object(changeFields)).max(39)
70
+ changes: z2.array(z2.object(changeFields)).max(39)
65
71
  });
66
- var phaseShiftObservationV2Schema = z.object({
67
- version: z.literal(2),
72
+ var phaseShiftObservationV2Schema = z2.object({
73
+ version: z2.literal(2),
68
74
  ...observationFields,
69
75
  /** Timestamp of the `windowEntryIds[0]` entry: where this check's coverage starts. */
70
76
  windowStartAt: sourceTime,
@@ -72,13 +78,13 @@ var phaseShiftObservationV2Schema = z.object({
72
78
  throughAt: sourceTime,
73
79
  /** What this check cost to run; absent on a record written before it was accounted. */
74
80
  cost: phaseShiftCostSchema.optional(),
75
- changes: z.array(z.object({
81
+ changes: z2.array(z2.object({
76
82
  ...changeFields,
77
83
  /** Timestamp of the `startEntryId` entry: when the new work began. */
78
84
  startedAt: sourceTime
79
85
  })).max(39)
80
86
  });
81
- var phaseShiftObservationSchema = z.discriminatedUnion("version", [
87
+ var phaseShiftObservationSchema = z2.discriminatedUnion("version", [
82
88
  phaseShiftObservationV1Schema,
83
89
  phaseShiftObservationV2Schema
84
90
  ]);
@@ -8,11 +8,11 @@
8
8
  * moment it revisits an id, so a cycle yields the cycle's own ids rather than
9
9
  * looping forever.
10
10
  */
11
- export declare function branchEntryIds(entries: readonly TranscriptTreeEntry[]): ReadonlySet<string>;
11
+ export declare function branchEntryIds(entries: readonly TranscriptTreeEntryLike[]): ReadonlySet<string>;
12
12
 
13
13
  /**
14
14
  * Every `type: "custom"` entry of one `customType`, oldest→newest, with every
15
- * field ({@link TranscriptSourceEntry}'s id/timestamp/tree links plus the
15
+ * field ({@link TranscriptSourceEntryLike}'s id/timestamp/tree links plus the
16
16
  * entry's own `data`) preserved unchanged — `data` is returned exactly as it
17
17
  * arrived, opaque and unvalidated, never coerced toward a schema this module
18
18
  * cannot know.
@@ -24,12 +24,12 @@ export declare function branchEntryIds(entries: readonly TranscriptTreeEntry[]):
24
24
  * itself needs to reason about abandoned entries (an audit, a recovery
25
25
  * tool); an ordinary reader states `"active"` explicitly.
26
26
  */
27
- export declare function customEntriesFromEntries<T extends TranscriptSourceEntry = TranscriptSourceEntry>(entries: readonly T[], customType: string, options: {
27
+ export declare function customEntriesFromEntries<T extends TranscriptSourceEntryLike = TranscriptSourceEntryLike>(entries: readonly T[], customType: string, options: {
28
28
  readonly branch: "active" | "all";
29
29
  }): readonly T[];
30
30
 
31
31
  /** The fields the customType selector needs off a session entry. */
32
- export declare interface TranscriptEntry {
32
+ export declare interface TranscriptEntryLike {
33
33
  readonly id: string;
34
34
  readonly type: string;
35
35
  readonly customType?: string;
@@ -41,7 +41,7 @@ export declare interface TranscriptEntry {
41
41
  * A tree entry plus the ISO timestamp the timeline projections read alongside
42
42
  * the walk result (`turnSegments`, `phaseShiftTimelineFromEntries`).
43
43
  */
44
- export declare interface TranscriptSourceEntry extends TranscriptTreeEntry {
44
+ export declare interface TranscriptSourceEntryLike extends TranscriptTreeEntryLike {
45
45
  readonly timestamp: string;
46
46
  }
47
47
 
@@ -52,7 +52,7 @@ export declare interface TranscriptSourceEntry extends TranscriptTreeEntry {
52
52
  * v3 log carries an ISO string, so requiring either shape here would force one
53
53
  * class of caller to convert entries it only wants walked.
54
54
  */
55
- declare interface TranscriptTreeEntry extends TranscriptEntry {
55
+ declare interface TranscriptTreeEntryLike extends TranscriptEntryLike {
56
56
  readonly parentId?: string | null;
57
57
  readonly targetId?: string | null;
58
58
  }
@@ -1,4 +1,10 @@
1
1
  // packages/plugin-protocol/dist/transcript.js
2
+ import { z } from "zod";
3
+ var WorkBlockCoverageSchema = z.strictObject({
4
+ firstToolResultEntryId: z.string().min(1),
5
+ lastToolResultEntryId: z.string().min(1),
6
+ toolResultEntryIds: z.array(z.string().min(1)).min(1).max(4096)
7
+ }).refine((value) => value.firstToolResultEntryId === value.toolResultEntryIds[0] && value.lastToolResultEntryId === value.toolResultEntryIds.at(-1) && new Set(value.toolResultEntryIds).size === value.toolResultEntryIds.length);
2
8
  function branchEntryIds(entries) {
3
9
  const last = entries[entries.length - 1];
4
10
  if (last === void 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaklabs-ai/plugin-sdk",
3
- "version": "0.13.3",
3
+ "version": "0.13.5",
4
4
  "description": "The Kay plugin SDK — types and helpers for authoring a Kay directory plugin.",
5
5
  "author": "YakLabs",
6
6
  "type": "module",
@@ -55,37 +55,49 @@ and symlinking `node_modules/@bonsai` into the copy does not rescue it either: p
55
55
  layout does not expose workspace packages that way. The scaffold exists precisely so this
56
56
  cannot happen.
57
57
 
58
+ The entrypoint default-exports the function Bonsai calls with the plugin API:
59
+ `export default function activate(api: KayAgentPluginApi): void`, and the same with
60
+ `KayAppPluginApi` for `kay.app`. An older plugin's named `activate` export still loads. Set the
61
+ manifest's `entrypoint.export` only to run a different export, such as one module serving both
62
+ daemon targets. Bonsai reads the default export from plugin SDK 0.13.5 on. The scaffold's
63
+ `compatibility.sdk` range makes an older Bonsai refuse the plugin ("needs plugin SDK ^0.13.5");
64
+ a hand-written manifest with no range loads there and then fails with "has no 'activate' export".
65
+
58
66
  ## Check the manifest before you wait for a load
59
67
 
60
68
  ```
61
69
  pnpm plugin:check ~/.kay/plugins/widgets
62
70
  ```
63
71
 
64
- Run from the root of a Bonsai checkout, against the plugin directory. It runs the daemon's OWN
65
- manifest schema and contract resolver (imported from `@bonsai/plugin-protocol`, not re-implemented),
66
- so it answers "would Kay accept this manifest?" in about a second instead of after a load cycle.
67
- Run it after every manifest edit, and before concluding a plugin is broken for some deeper reason.
68
-
69
- It prints the resolved global contribution ids, exits non-zero when the plugin would be refused,
70
- and separates the two outcomes that look alike from outside:
71
-
72
- - **Refused outright** — schema-invalid manifest, a reserved `kay.*` / `herd.*` id, a declared
73
- path escaping the plugin directory, or contributions that do not resolve. Every contribution
74
- shares ONE flat `<namespace>.<localId>` space ACROSS kinds, so a tool and an endpoint both
75
- called `inspect` collide and the whole plugin is refused (#8936).
72
+ Run from the root of a Bonsai checkout, against the plugin directory. It prints the verdict the
73
+ daemon's directory scan reaches, from the same function (`judgeKayPluginDirectory`,
74
+ `@bonsai/plugin-protocol`), judged against that checkout's plugin SDK, whose version it prints. So
75
+ it answers "would Bonsai refuse this plugin?" in about a second instead of after a load cycle. Run
76
+ it after every manifest edit, and before concluding a plugin is broken for some deeper reason.
77
+
78
+ It prints the resolved global contribution ids, exits non-zero unless the plugin would load, and
79
+ separates:
80
+
81
+ - **Refused outright** — an SDK range or protocol this Bonsai does not support (checked before
82
+ the schema, so a manifest for another SDK names the version, not its unknown key), a
83
+ schema-invalid manifest, a reserved `kay.*` / `herd.*` id, a declared path escaping the plugin
84
+ directory, or contributions that do not resolve. Every contribution shares ONE flat
85
+ `<namespace>.<localId>` space ACROSS kinds, so a tool and an endpoint both called `inspect`
86
+ collide and the whole plugin is refused (#8936).
76
87
  - **Loads, minus something** — a retired `compatibility` / `permissions.byTarget` key the parser
77
- strips silently, a `toolName` that is not the namespace-derived name (that tool is excluded and
78
- nothing tells the user), a missing entrypoint or skill file.
88
+ strips silently; plus compatibility notes (no declared SDK range).
79
89
 
80
- It reads the manifest only. A clean verdict says nothing about `activate()` throwing, the
81
- disclosure audit, or an id another installed plugin already claims.
90
+ It never reads the declared files or applies the roster's rules: a missing entrypoint or skill
91
+ file, a `toolName` that is not the namespace-derived name, an `activate()` that throws, the
92
+ disclosure audit, and an id another installed plugin already claims all show only after enabling,
93
+ in `read plugin://<id>`.
82
94
 
83
95
  ## The SDK resolution contract
84
96
 
85
97
  - Plugin source imports `@bonsai/plugin-sdk/<subpath>` as a **bare specifier**
86
98
  (`@bonsai/plugin-sdk/kay-agent`, `/kay-agent/testing`, `/kay-app`, `/disclosure` — only declared
87
99
  exports exist).
88
- - The daemon's chord loader bundles each declared TypeScript entrypoint and its relative imports.
100
+ - The daemon's plugin loader bundles each declared TypeScript entrypoint and its relative imports.
89
101
  It keeps `@bonsai/plugin-sdk` imports external and resolves them to the running app's own built SDK.
90
102
  Multi-file plugins are supported, and no plugin-local build is needed for Bonsai to load them.
91
103
  - Corollary: **bare `node` outside the daemon cannot resolve `@bonsai/plugin-sdk`** from the plugin
@@ -94,7 +106,7 @@ disclosure audit, or an id another installed plugin already claims.
94
106
  standalone test; a published SDK dependency is the other standalone option.
95
107
  - Typecheck with `npm run typecheck` inside the plugin directory; the scaffold's generated
96
108
  tsconfig maps the SDK's types via `paths`.
97
- - Chord compiles TypeScript syntax that native type stripping cannot execute, including `enum`,
109
+ - The loader compiles TypeScript syntax that native type stripping cannot execute, including `enum`,
98
110
  `namespace` blocks, constructor parameter properties, and decorators. Typecheck before enabling
99
111
  the plugin; a bundle error is reported as a target-load diagnostic.
100
112
 
@@ -208,10 +220,10 @@ list` names the exact local root — because writing into the wrong one produces
208
220
  The commit is the activation boundary, so run these against the working tree first. Each catches
209
221
  a failure class the load would otherwise be the first thing to surface:
210
222
 
211
- 1. **Run `pnpm plugin:check <dir>`** from a Bonsai checkout. It validates `kay-plugin.json` against
212
- `KayPluginManifestV1` and resolves its contributions with the daemon's own resolver (both from
213
- `@bonsai/plugin-protocol`), not just "it is valid JSON": that schema and resolver are the gate the
214
- scan actually runs. A non-zero exit means the plugin would be refused.
223
+ 1. **Run `pnpm plugin:check <dir>`** from a Bonsai checkout. It prints the verdict of the function
224
+ the daemon's directory scan runs (`judgeKayPluginDirectory`): SDK compatibility, the manifest
225
+ schema, reserved ids, contract resolution and path containment, not just "it is valid JSON". A
226
+ non-zero exit means the plugin would be refused.
215
227
  2. **Run the disclosure audit yourself**: `auditPluginDisclosure` from `@bonsai/plugin-sdk/disclosure`.
216
228
  It is not redundant with typecheck. A plugin shipped a `search` verb with no `verbDocs` entry
217
229
  that typechecked clean and would have failed the whole `kay.agent` target.