@yaklabs-ai/plugin-sdk 0.13.4 → 0.13.6
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 +47 -8
- package/THIRD-PARTY-INVENTORY.json +16 -19
- package/THIRD-PARTY-NOTICES.md +9 -15
- package/dist/disclosure.d.ts +6184 -718
- package/dist/disclosure.js +9 -30
- package/dist/kay-agent-testing.d.ts +6217 -726
- package/dist/kay-agent-testing.js +40 -15
- package/dist/kay-agent.d.ts +6253 -762
- package/dist/kay-agent.js +277 -80
- package/dist/kay-app.d.ts +6953 -818
- package/dist/kay-app.js +70 -50
- package/dist/phase-shift.d.ts +4 -4
- package/dist/phase-shift.js +31 -25
- package/dist/time.d.ts +157 -0
- package/dist/time.js +37 -0
- package/dist/transcript.d.ts +115 -6
- package/dist/transcript.js +89 -1
- package/package.json +7 -2
- package/skills/directory-plugin-authoring/SKILL.md +37 -25
package/dist/transcript.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* The ids on a session's active branch: the leaf chain walked parent-ward from
|
|
3
5
|
* the tip, which a trailing `leaf` marker moves. Empty for an empty log.
|
|
@@ -8,11 +10,11 @@
|
|
|
8
10
|
* moment it revisits an id, so a cycle yields the cycle's own ids rather than
|
|
9
11
|
* looping forever.
|
|
10
12
|
*/
|
|
11
|
-
export declare function branchEntryIds(entries: readonly
|
|
13
|
+
export declare function branchEntryIds(entries: readonly TranscriptTreeEntryLike[]): ReadonlySet<string>;
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Every `type: "custom"` entry of one `customType`, oldest→newest, with every
|
|
15
|
-
* field ({@link
|
|
17
|
+
* field ({@link TranscriptSourceEntryLike}'s id/timestamp/tree links plus the
|
|
16
18
|
* entry's own `data`) preserved unchanged — `data` is returned exactly as it
|
|
17
19
|
* arrived, opaque and unvalidated, never coerced toward a schema this module
|
|
18
20
|
* cannot know.
|
|
@@ -24,12 +26,37 @@ export declare function branchEntryIds(entries: readonly TranscriptTreeEntry[]):
|
|
|
24
26
|
* itself needs to reason about abandoned entries (an audit, a recovery
|
|
25
27
|
* tool); an ordinary reader states `"active"` explicitly.
|
|
26
28
|
*/
|
|
27
|
-
export declare function customEntriesFromEntries<T extends
|
|
29
|
+
export declare function customEntriesFromEntries<T extends TranscriptSourceEntryLike = TranscriptSourceEntryLike>(entries: readonly T[], customType: string, options: {
|
|
28
30
|
readonly branch: "active" | "all";
|
|
29
31
|
}): readonly T[];
|
|
30
32
|
|
|
33
|
+
export declare function matchesWorkBlock(coverage: WorkBlockCoverage, toolResultEntryIds: readonly string[]): boolean;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Tool names the transcript draws as their own surface, or lifts into the reply, instead of
|
|
37
|
+
* counting them as work in the closed bar (venue `inline` or `turn-tail`, or the parsed
|
|
38
|
+
* `submit_final`). A call to one of these is not a "visible call". The renderer's built-in tool
|
|
39
|
+
* registrations are the source of truth; its parity test fails when this list drifts from them.
|
|
40
|
+
* A tool a plugin registers with a non-activity venue is not listed, so the daemon may count it
|
|
41
|
+
* (a headline can appear on a turn whose bar shows fewer calls than the cutoff only in that
|
|
42
|
+
* corner, and the renderer re-checks its own count before displaying anything).
|
|
43
|
+
*/
|
|
44
|
+
export declare const NON_ACTIVITY_TOOL_NAMES: readonly string[];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The daemon's own run-start record. A run's entries begin here, before the
|
|
48
|
+
* user message that opened it.
|
|
49
|
+
*/
|
|
50
|
+
export declare const RUN_STARTED_CUSTOM_TYPE = "session_run_started";
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The daemon's own terminal run record (completed / interrupted / cancelled /
|
|
54
|
+
* failed). A run's entries end here.
|
|
55
|
+
*/
|
|
56
|
+
export declare const RUN_TERMINAL_CUSTOM_TYPE = "session_run_state";
|
|
57
|
+
|
|
31
58
|
/** The fields the customType selector needs off a session entry. */
|
|
32
|
-
export declare interface
|
|
59
|
+
export declare interface TranscriptEntryLike {
|
|
33
60
|
readonly id: string;
|
|
34
61
|
readonly type: string;
|
|
35
62
|
readonly customType?: string;
|
|
@@ -41,7 +68,7 @@ export declare interface TranscriptEntry {
|
|
|
41
68
|
* A tree entry plus the ISO timestamp the timeline projections read alongside
|
|
42
69
|
* the walk result (`turnSegments`, `phaseShiftTimelineFromEntries`).
|
|
43
70
|
*/
|
|
44
|
-
export declare interface
|
|
71
|
+
export declare interface TranscriptSourceEntryLike extends TranscriptTreeEntryLike {
|
|
45
72
|
readonly timestamp: string;
|
|
46
73
|
}
|
|
47
74
|
|
|
@@ -52,9 +79,91 @@ export declare interface TranscriptSourceEntry extends TranscriptTreeEntry {
|
|
|
52
79
|
* v3 log carries an ISO string, so requiring either shape here would force one
|
|
53
80
|
* class of caller to convert entries it only wants walked.
|
|
54
81
|
*/
|
|
55
|
-
declare interface
|
|
82
|
+
declare interface TranscriptTreeEntryLike extends TranscriptEntryLike {
|
|
56
83
|
readonly parentId?: string | null;
|
|
57
84
|
readonly targetId?: string | null;
|
|
58
85
|
}
|
|
59
86
|
|
|
87
|
+
/**
|
|
88
|
+
* The fields the turn walk reads. Deliberately narrower and looser than
|
|
89
|
+
* {@link TranscriptEntryLike}: the walk answers in INDEXES, so it never needs an
|
|
90
|
+
* `id`, and both callers hold entries typed by their own layer (herd's
|
|
91
|
+
* `TranscriptEntry`, the daemon's branch rows).
|
|
92
|
+
*/
|
|
93
|
+
export declare interface TurnBoundaryEntry {
|
|
94
|
+
readonly type?: string;
|
|
95
|
+
readonly customType?: string;
|
|
96
|
+
/** Opaque here; only `message.role === "user"` is read, defensively. */
|
|
97
|
+
readonly message?: unknown;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** One turn's bounds as {@link turnSegments} reports them, both inclusive. */
|
|
101
|
+
export declare interface TurnSegment {
|
|
102
|
+
/** The user message entry that opened the turn. A steer never opens one. */
|
|
103
|
+
readonly openingIndex: number;
|
|
104
|
+
/**
|
|
105
|
+
* The entry the turn settled on. Equal to `openingIndex` when nothing
|
|
106
|
+
* followed the opening message, which is a turn that never settled rather
|
|
107
|
+
* than a turn with an empty body.
|
|
108
|
+
*/
|
|
109
|
+
readonly settleIndex: number;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* THE definition of a turn, shared by every reader that has to agree about one:
|
|
114
|
+
* the settled-turn extractors that key a record to a turn, and the replay
|
|
115
|
+
* publisher that offers a stored session's turns as events. Two readers
|
|
116
|
+
* disagreeing here means a replayed "turn" is not the turn production would
|
|
117
|
+
* have extracted.
|
|
118
|
+
*
|
|
119
|
+
* **A turn is a run.** The daemon brackets every run in the transcript
|
|
120
|
+
* ({@link RUN_STARTED_CUSTOM_TYPE} then, at the end,
|
|
121
|
+
* {@link RUN_TERMINAL_CUSTOM_TYPE}), and the user message that opened the run
|
|
122
|
+
* opens the turn. A human steering or a queued message admitted mid-flight is
|
|
123
|
+
* persisted as another `role: "user"` entry INSIDE that bracket, and belongs to
|
|
124
|
+
* the turn it interrupted: it is a correction to work already under way, not a
|
|
125
|
+
* new request. Anchoring on the last user entry instead hands an extractor the
|
|
126
|
+
* steer as the turn's goal and demotes the real request to prior context.
|
|
127
|
+
*
|
|
128
|
+
* A transcript with no run brackets at all (a log older than run bookkeeping, a
|
|
129
|
+
* hand-built fixture) falls back to one turn per user message, which is the
|
|
130
|
+
* most this function can honestly say: with no record of when a run ended,
|
|
131
|
+
* nothing distinguishes a steer from a fresh request.
|
|
132
|
+
*
|
|
133
|
+
* `throughIndex` bounds the walk for a caller holding only a prefix; segments
|
|
134
|
+
* are returned oldest first.
|
|
135
|
+
*/
|
|
136
|
+
export declare function turnSegments(entries: readonly TurnBoundaryEntry[], throughIndex?: number): readonly TurnSegment[];
|
|
137
|
+
|
|
138
|
+
export declare const WORK_HEADLINE_MAX_CHARS = 90;
|
|
139
|
+
|
|
140
|
+
/** The longest and shortest stored work headline, in characters. Shorter is filler; longer is a sentence. */
|
|
141
|
+
export declare const WORK_HEADLINE_MIN_CHARS = 12;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The fewest VISIBLE tool calls a settled turn needs before it earns a work headline (owner
|
|
145
|
+
* ruling, 2026-10-14: "it shouldn't do that if it ran 2 commands but could be helpful if it ran
|
|
146
|
+
* like 15"; the scope card's 10 was accepted). ONE value, read by the writer (the hindsight
|
|
147
|
+
* `work-headline` handler decides whether to spend a model call) and by the closed work bar's
|
|
148
|
+
* renderer (decides whether to show a stored headline), so the two can never disagree about which
|
|
149
|
+
* turns qualify.
|
|
150
|
+
*/
|
|
151
|
+
export declare const WORK_HEADLINE_MIN_VISIBLE_CALLS = 10;
|
|
152
|
+
|
|
153
|
+
export declare type WorkBlockCoverage = z.infer<typeof WorkBlockCoverageSchema>;
|
|
154
|
+
|
|
155
|
+
export declare function workBlockCoverage(toolResultEntryIds: readonly string[]): WorkBlockCoverage | undefined;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Exact ordered completed executions, addressed by persisted tool-result entry IDs.
|
|
159
|
+
* Provider call IDs can repeat across retries and turns; they are never ownership keys.
|
|
160
|
+
*/
|
|
161
|
+
export declare const WorkBlockCoverageSchema: z.ZodObject<{
|
|
162
|
+
firstToolResultEntryId: z.ZodString;
|
|
163
|
+
lastToolResultEntryId: z.ZodString;
|
|
164
|
+
toolResultEntryIds: z.ZodArray<z.ZodString>;
|
|
165
|
+
}, z.core.$strict>;
|
|
166
|
+
|
|
167
|
+
export declare function workBlockKey(coverage: WorkBlockCoverage): string;
|
|
168
|
+
|
|
60
169
|
export { }
|
package/dist/transcript.js
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
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);
|
|
8
|
+
function workBlockCoverage(toolResultEntryIds) {
|
|
9
|
+
const parsed = WorkBlockCoverageSchema.safeParse({
|
|
10
|
+
firstToolResultEntryId: toolResultEntryIds[0],
|
|
11
|
+
lastToolResultEntryId: toolResultEntryIds.at(-1),
|
|
12
|
+
toolResultEntryIds
|
|
13
|
+
});
|
|
14
|
+
return parsed.success ? parsed.data : void 0;
|
|
15
|
+
}
|
|
16
|
+
function workBlockKey(coverage) {
|
|
17
|
+
return JSON.stringify(coverage.toolResultEntryIds);
|
|
18
|
+
}
|
|
19
|
+
function matchesWorkBlock(coverage, toolResultEntryIds) {
|
|
20
|
+
return coverage.toolResultEntryIds.length === toolResultEntryIds.length && coverage.toolResultEntryIds.every((id, index) => id === toolResultEntryIds[index]);
|
|
21
|
+
}
|
|
22
|
+
var RUN_STARTED_CUSTOM_TYPE = "session_run_started";
|
|
23
|
+
var RUN_TERMINAL_CUSTOM_TYPE = "session_run_state";
|
|
2
24
|
function branchEntryIds(entries) {
|
|
3
25
|
const last = entries[entries.length - 1];
|
|
4
26
|
if (last === void 0)
|
|
@@ -13,11 +35,77 @@ function branchEntryIds(entries) {
|
|
|
13
35
|
return onBranch;
|
|
14
36
|
}
|
|
15
37
|
var EMPTY_BRANCH_IDS = /* @__PURE__ */ new Set();
|
|
38
|
+
function isUserMessageEntry(entry) {
|
|
39
|
+
if (entry.type !== "message")
|
|
40
|
+
return false;
|
|
41
|
+
const message = entry.message;
|
|
42
|
+
return typeof message === "object" && message !== null && message.role === "user";
|
|
43
|
+
}
|
|
44
|
+
function runBoundaryOf(entry) {
|
|
45
|
+
if (entry.type !== "custom")
|
|
46
|
+
return void 0;
|
|
47
|
+
if (entry.customType === RUN_STARTED_CUSTOM_TYPE)
|
|
48
|
+
return "started";
|
|
49
|
+
if (entry.customType === RUN_TERMINAL_CUSTOM_TYPE)
|
|
50
|
+
return "terminal";
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
function turnSegments(entries, throughIndex = entries.length - 1) {
|
|
54
|
+
const segments = [];
|
|
55
|
+
let openingIndex;
|
|
56
|
+
let runOpen = false;
|
|
57
|
+
const close = (settleIndex) => {
|
|
58
|
+
if (openingIndex !== void 0 && settleIndex >= openingIndex) {
|
|
59
|
+
segments.push({ openingIndex, settleIndex });
|
|
60
|
+
}
|
|
61
|
+
openingIndex = void 0;
|
|
62
|
+
};
|
|
63
|
+
for (let index = 0; index <= throughIndex; index += 1) {
|
|
64
|
+
const entry = entries[index];
|
|
65
|
+
if (entry === void 0)
|
|
66
|
+
break;
|
|
67
|
+
const boundary = runBoundaryOf(entry);
|
|
68
|
+
if (boundary !== void 0) {
|
|
69
|
+
close(boundary === "terminal" ? index : index - 1);
|
|
70
|
+
runOpen = boundary === "started";
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!isUserMessageEntry(entry))
|
|
74
|
+
continue;
|
|
75
|
+
if (runOpen && openingIndex !== void 0)
|
|
76
|
+
continue;
|
|
77
|
+
close(index - 1);
|
|
78
|
+
openingIndex = index;
|
|
79
|
+
}
|
|
80
|
+
close(throughIndex);
|
|
81
|
+
return segments;
|
|
82
|
+
}
|
|
16
83
|
function customEntriesFromEntries(entries, customType, options) {
|
|
17
84
|
const onBranch = options.branch === "all" ? null : branchEntryIds(entries);
|
|
18
85
|
return entries.filter((entry) => entry.type === "custom" && entry.customType === customType && (onBranch === null || onBranch.has(entry.id)));
|
|
19
86
|
}
|
|
87
|
+
var WORK_HEADLINE_MIN_VISIBLE_CALLS = 10;
|
|
88
|
+
var NON_ACTIVITY_TOOL_NAMES = [
|
|
89
|
+
"agent_spawn",
|
|
90
|
+
"send_message",
|
|
91
|
+
"service_connect",
|
|
92
|
+
"ui_render",
|
|
93
|
+
"submit_final"
|
|
94
|
+
];
|
|
95
|
+
var WORK_HEADLINE_MIN_CHARS = 12;
|
|
96
|
+
var WORK_HEADLINE_MAX_CHARS = 90;
|
|
20
97
|
export {
|
|
98
|
+
NON_ACTIVITY_TOOL_NAMES,
|
|
99
|
+
RUN_STARTED_CUSTOM_TYPE,
|
|
100
|
+
RUN_TERMINAL_CUSTOM_TYPE,
|
|
101
|
+
WORK_HEADLINE_MAX_CHARS,
|
|
102
|
+
WORK_HEADLINE_MIN_CHARS,
|
|
103
|
+
WORK_HEADLINE_MIN_VISIBLE_CALLS,
|
|
104
|
+
WorkBlockCoverageSchema,
|
|
21
105
|
branchEntryIds,
|
|
22
|
-
customEntriesFromEntries
|
|
106
|
+
customEntriesFromEntries,
|
|
107
|
+
matchesWorkBlock,
|
|
108
|
+
turnSegments,
|
|
109
|
+
workBlockCoverage,
|
|
110
|
+
workBlockKey
|
|
23
111
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaklabs-ai/plugin-sdk",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.6",
|
|
4
4
|
"description": "The Kay plugin SDK — types and helpers for authoring a Kay directory plugin.",
|
|
5
5
|
"author": "YakLabs",
|
|
6
6
|
"type": "module",
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"./transcript": {
|
|
45
45
|
"types": "./dist/transcript.d.ts",
|
|
46
46
|
"default": "./dist/transcript.js"
|
|
47
|
+
},
|
|
48
|
+
"./time": {
|
|
49
|
+
"types": "./dist/time.d.ts",
|
|
50
|
+
"default": "./dist/time.js"
|
|
47
51
|
}
|
|
48
52
|
},
|
|
49
53
|
"files": [
|
|
@@ -56,7 +60,8 @@
|
|
|
56
60
|
],
|
|
57
61
|
"dependencies": {
|
|
58
62
|
"zod": "^4.0.0",
|
|
59
|
-
"yaml": "^2.9.0"
|
|
63
|
+
"yaml": "^2.9.0",
|
|
64
|
+
"@js-temporal/polyfill": "0.5.1"
|
|
60
65
|
},
|
|
61
66
|
"publishConfig": {
|
|
62
67
|
"registry": "https://registry.npmjs.org",
|
|
@@ -6,7 +6,7 @@ description: "Author, scaffold, debug, or iterate on a Bonsai directory plugin (
|
|
|
6
6
|
# Directory plugin authoring
|
|
7
7
|
|
|
8
8
|
A directory plugin is a plugin the user authors in `~/.kay/plugins/<name>/`, outside the Bonsai
|
|
9
|
-
repo. The full authoring guide is [`docs/directory-plugins.md`](
|
|
9
|
+
repo. The full authoring guide is [`docs/directory-plugins.md`](../../../docs/directory-plugins.md)
|
|
10
10
|
and the SDK reference is `packages/plugin-sdk/README.md`; this skill is the routing layer that
|
|
11
11
|
keeps an agent on the paved path and out of the known pits.
|
|
12
12
|
|
|
@@ -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
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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`
|
|
78
|
-
|
|
88
|
+
strips silently, or a tool whose `toolName` is not the namespace-derived name (that tool is left
|
|
89
|
+
out); plus compatibility notes (no declared SDK range).
|
|
79
90
|
|
|
80
|
-
It reads the
|
|
81
|
-
disclosure audit,
|
|
91
|
+
It never reads the declared files or applies the roster's rules: a missing entrypoint or skill
|
|
92
|
+
file, an `activate()` that throws, the disclosure audit, and an id another installed plugin already
|
|
93
|
+
claims all show only after enabling, 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
|
-
(`@bonsai/plugin-sdk/kay-agent`, `/kay-agent/testing`, `/kay-app`, `/disclosure` — only declared
|
|
98
|
+
(`@bonsai/plugin-sdk/kay-agent`, `/kay-agent/testing`, `/kay-app`, `/disclosure`, `/time` — only declared
|
|
87
99
|
exports exist).
|
|
88
|
-
- The daemon's
|
|
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
|
-
-
|
|
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
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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.
|
|
@@ -281,7 +293,7 @@ file.
|
|
|
281
293
|
|
|
282
294
|
## Reference map
|
|
283
295
|
|
|
284
|
-
- [`docs/directory-plugins.md`](
|
|
296
|
+
- [`docs/directory-plugins.md`](../../../docs/directory-plugins.md) — the authoring guide:
|
|
285
297
|
manifest shape, the enable gate and what it does not gate, trust tiers, diagnostics.
|
|
286
298
|
- `packages/plugin-sdk/README.md` — the SDK surface.
|
|
287
299
|
- `packages/plugin-sdk/PUBLISHED.md` — installing the published SDK outside a Bonsai checkout.
|