@yaklabs-ai/plugin-sdk 0.13.5 → 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 +4 -2
- package/THIRD-PARTY-INVENTORY.json +8 -1
- package/dist/disclosure.d.ts +5 -7
- package/dist/kay-agent-testing.d.ts +30 -7
- package/dist/kay-agent-testing.js +37 -0
- package/dist/kay-agent.d.ts +19 -8
- package/dist/kay-agent.js +197 -2
- package/dist/kay-app.d.ts +60 -55
- package/dist/kay-app.js +66 -50
- package/dist/time.d.ts +157 -0
- package/dist/time.js +37 -0
- package/dist/transcript.d.ts +109 -0
- package/dist/transcript.js +83 -1
- package/package.json +7 -2
- package/skills/directory-plugin-authoring/SKILL.md +7 -7
package/dist/time.d.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { Temporal as Temporal_2 } from '@js-temporal/polyfill';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The id ICU resolves for an IANA time-zone id, or `undefined` when `id` is not one.
|
|
5
|
+
*
|
|
6
|
+
* Accepts `UTC`, `GMT`, and any id that contains `/` and that ICU resolves. Rejects
|
|
7
|
+
* abbreviations and slash-less legacy ids (`EST`, `CET`, `EST5EDT`), offsets (`+05:30`,
|
|
8
|
+
* `UTC+2`), the empty string, strings over 64 characters and non-strings. ICU maps link
|
|
9
|
+
* names to its own canonical id (`Asia/Kolkata` to `Asia/Calcutta`), and which spelling is
|
|
10
|
+
* canonical depends on the engine, so compare zones with {@link sameZone}, never with `===`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function canonicalZone(id: unknown): string | undefined;
|
|
13
|
+
|
|
14
|
+
/** True when {@link canonicalZone} accepts `id`. */
|
|
15
|
+
export declare function isValidZone(id: unknown): boolean;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `app.time` / `api.time`: host facts about time. Added to `kay.app` in SDK 0.12.1 and to
|
|
19
|
+
* `kay.agent` with the calendar zone-of-record work: a plugin that calls it declares
|
|
20
|
+
* `compatibility.sdk` `^0.12.1`, so an older Bonsai refuses a directory or repository plugin at
|
|
21
|
+
* load instead of the call failing.
|
|
22
|
+
*/
|
|
23
|
+
export declare interface KayAppTime {
|
|
24
|
+
/**
|
|
25
|
+
* Reads the daemon's clock and the user's time zone now.
|
|
26
|
+
*
|
|
27
|
+
* Every call asks the host again and returns a new object; nothing is cached. Call it where you
|
|
28
|
+
* need the value (inside a tool, a handler or a workflow step), not inside `activate()`: a value
|
|
29
|
+
* read there is as old as the activation, and the host can run a bundled plugin's `activate()`
|
|
30
|
+
* against a recording api that has no `time`, so a read there throws. The exception is a
|
|
31
|
+
* directory plugin that declares a workflow with a cron `timezone`: `workflows.define` is legal
|
|
32
|
+
* only during `activate()`, so it reads the zone there, and the schedule then keeps that zone.
|
|
33
|
+
* Keep the result for as long as you use it, and not across a long wait (a sleep, a timer, a
|
|
34
|
+
* queued run): the zone can change while you wait. On a daemon that runs on the user's machine,
|
|
35
|
+
* a change of the process's `TZ` shows in the next call. A `TZ` that names a zone wins over the
|
|
36
|
+
* operating system's zone, so a change of the operating-system zone does not show while the
|
|
37
|
+
* process has one. When `TZ` is unset or does not name a zone, a change of the operating-system
|
|
38
|
+
* zone is meant to show, but none has been seen on a real machine yet.
|
|
39
|
+
*
|
|
40
|
+
* A zone the host cannot determine is an answer (`zoneSource: "utc-fallback"`), not an error.
|
|
41
|
+
*
|
|
42
|
+
* Use this to find the zone, not `Intl.DateTimeFormat().resolvedOptions().timeZone`, which can
|
|
43
|
+
* keep a zone the runtime read earlier, or `process.env.TZ`, which is often unset or not a zone.
|
|
44
|
+
* Neither says that the zone is unknown.
|
|
45
|
+
*
|
|
46
|
+
* It does not use `this`, so it is safe to destructure.
|
|
47
|
+
*/
|
|
48
|
+
readonly snapshot: () => KayAppTimeSnapshot;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The plugin-facing time contract: the daemon's clock and the time zone the user is in,
|
|
53
|
+
* read together in one call.
|
|
54
|
+
*
|
|
55
|
+
* Ungated and always present, like `app.paths`. It confers no authority: a plugin runs in the
|
|
56
|
+
* daemon's process and can already read the clock. What the door adds is the daemon's answer
|
|
57
|
+
* for the USER's zone, and a label for the case where that answer is unknown.
|
|
58
|
+
*
|
|
59
|
+
* This is a leaf module. It lives beside both target SDKs (rather than inside `kay-app`) so the
|
|
60
|
+
* `kay.agent` SDK can expose the same door without importing the `kay.app` surface, which pulls
|
|
61
|
+
* that surface into every agent-facing bundle.
|
|
62
|
+
*/
|
|
63
|
+
/** One reading of the daemon's clock and the user's time zone. Each call returns a new object. */
|
|
64
|
+
export declare interface KayAppTimeSnapshot {
|
|
65
|
+
/** The daemon's clock when the snapshot was read: milliseconds since the Unix epoch. */
|
|
66
|
+
readonly nowMs: number;
|
|
67
|
+
/**
|
|
68
|
+
* The time zone the user is in, as an IANA id in the spelling ICU resolves (`America/New_York`).
|
|
69
|
+
* One zone can have two ids, and ICU picks one: `Asia/Kolkata` comes back as `Asia/Calcutta`. So
|
|
70
|
+
* never compare zone ids with `===`: compare with `sameZone` from `@bonsai/plugin-sdk/time`. Pass
|
|
71
|
+
* `zone` as the `timeZone` of `Intl.DateTimeFormat` or `toLocaleString` to format a time for the
|
|
72
|
+
* user.
|
|
73
|
+
*
|
|
74
|
+
* When `zoneSource` is `utc-fallback` the zone is unknown, and this value is `UTC` only because a
|
|
75
|
+
* value is required.
|
|
76
|
+
*/
|
|
77
|
+
readonly zone: string;
|
|
78
|
+
/**
|
|
79
|
+
* How the daemon found `zone`.
|
|
80
|
+
* - `host`: the daemon runs on the user's machine, and `zone` is the zone the daemon process
|
|
81
|
+
* runs in, read for this call: the process's `TZ` when that names a zone, otherwise the
|
|
82
|
+
* operating system's zone.
|
|
83
|
+
* - `utc-fallback`: the daemon does not know the user's zone. It runs on another machine (a remote
|
|
84
|
+
* daemon), or the machine's zone could not be read or is not an IANA id. `zone` is `UTC`, which
|
|
85
|
+
* here is a placeholder and not a fact about the user: do not show it to the user as their zone.
|
|
86
|
+
*/
|
|
87
|
+
readonly zoneSource: "host" | "utc-fallback";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** True when both ids are valid and ICU resolves them to the same zone. */
|
|
91
|
+
export declare function sameZone(a: unknown, b: unknown): boolean;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The Temporal API of this process: the engine's own `globalThis.Temporal` when it has one
|
|
95
|
+
* (Electron's runtime does, Node 24 does not), and `@js-temporal/polyfill`'s otherwise.
|
|
96
|
+
* `temporalImplementation` says which.
|
|
97
|
+
*
|
|
98
|
+
* It is a value and, through the namespace below, a type namespace: `Temporal.Instant` and
|
|
99
|
+
* `Temporal.ZonedDateTime` name both the classes and their instance types.
|
|
100
|
+
*/
|
|
101
|
+
export declare const Temporal: TemporalApi;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The types of `@js-temporal/polyfill`'s `Temporal` namespace, so code can write
|
|
105
|
+
* `Temporal.Instant` as a type. `Now` and `DateTimeFormat` are left out on purpose: time
|
|
106
|
+
* that logic depends on comes from a `Clock`, and the polyfill's `Intl` export is not part of
|
|
107
|
+
* this API. `test/temporal.test.ts` fails when a new polyfill version adds a type that this
|
|
108
|
+
* list lacks.
|
|
109
|
+
*/
|
|
110
|
+
export declare namespace Temporal {
|
|
111
|
+
export type ComparisonResult = Temporal_2.ComparisonResult;
|
|
112
|
+
export type RoundingMode = Temporal_2.RoundingMode;
|
|
113
|
+
export type AssignmentOptions = Temporal_2.AssignmentOptions;
|
|
114
|
+
export type DurationOptions = Temporal_2.DurationOptions;
|
|
115
|
+
export type ToInstantOptions = Temporal_2.ToInstantOptions;
|
|
116
|
+
export type ZonedDateTimeAssignmentOptions = Temporal_2.ZonedDateTimeAssignmentOptions;
|
|
117
|
+
export type ArithmeticOptions = Temporal_2.ArithmeticOptions;
|
|
118
|
+
export type DateUnit = Temporal_2.DateUnit;
|
|
119
|
+
export type TimeUnit = Temporal_2.TimeUnit;
|
|
120
|
+
export type DateTimeUnit = Temporal_2.DateTimeUnit;
|
|
121
|
+
export type PluralUnit<T extends Temporal_2.DateTimeUnit> = Temporal_2.PluralUnit<T>;
|
|
122
|
+
export type LargestUnit<T extends Temporal_2.DateTimeUnit> = Temporal_2.LargestUnit<T>;
|
|
123
|
+
export type SmallestUnit<T extends Temporal_2.DateTimeUnit> = Temporal_2.SmallestUnit<T>;
|
|
124
|
+
export type TotalUnit<T extends Temporal_2.DateTimeUnit> = Temporal_2.TotalUnit<T>;
|
|
125
|
+
export type ToStringPrecisionOptions = Temporal_2.ToStringPrecisionOptions;
|
|
126
|
+
export type ShowCalendarOption = Temporal_2.ShowCalendarOption;
|
|
127
|
+
export type CalendarTypeToStringOptions = Temporal_2.CalendarTypeToStringOptions;
|
|
128
|
+
export type ZonedDateTimeToStringOptions = Temporal_2.ZonedDateTimeToStringOptions;
|
|
129
|
+
export type InstantToStringOptions = Temporal_2.InstantToStringOptions;
|
|
130
|
+
export type DifferenceOptions<T extends Temporal_2.DateTimeUnit> = Temporal_2.DifferenceOptions<T>;
|
|
131
|
+
export type RoundTo<T extends Temporal_2.DateTimeUnit> = Temporal_2.RoundTo<T>;
|
|
132
|
+
export type DurationRoundTo = Temporal_2.DurationRoundTo;
|
|
133
|
+
export type DurationTotalOf = Temporal_2.DurationTotalOf;
|
|
134
|
+
export type DurationArithmeticOptions = Temporal_2.DurationArithmeticOptions;
|
|
135
|
+
export type TransitionDirection = Temporal_2.TransitionDirection;
|
|
136
|
+
export type DurationLike = Temporal_2.DurationLike;
|
|
137
|
+
export type Duration = Temporal_2.Duration;
|
|
138
|
+
export type Instant = Temporal_2.Instant;
|
|
139
|
+
export type CalendarLike = Temporal_2.CalendarLike;
|
|
140
|
+
export type PlainDateLike = Temporal_2.PlainDateLike;
|
|
141
|
+
export type PlainDate = Temporal_2.PlainDate;
|
|
142
|
+
export type PlainDateTimeLike = Temporal_2.PlainDateTimeLike;
|
|
143
|
+
export type PlainDateTime = Temporal_2.PlainDateTime;
|
|
144
|
+
export type PlainMonthDayLike = Temporal_2.PlainMonthDayLike;
|
|
145
|
+
export type PlainMonthDay = Temporal_2.PlainMonthDay;
|
|
146
|
+
export type PlainTimeLike = Temporal_2.PlainTimeLike;
|
|
147
|
+
export type PlainTime = Temporal_2.PlainTime;
|
|
148
|
+
export type TimeZoneLike = Temporal_2.TimeZoneLike;
|
|
149
|
+
export type PlainYearMonthLike = Temporal_2.PlainYearMonthLike;
|
|
150
|
+
export type PlainYearMonth = Temporal_2.PlainYearMonth;
|
|
151
|
+
export type ZonedDateTimeLike = Temporal_2.ZonedDateTimeLike;
|
|
152
|
+
export type ZonedDateTime = Temporal_2.ZonedDateTime;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
declare type TemporalApi = typeof Temporal_2;
|
|
156
|
+
|
|
157
|
+
export { }
|
package/dist/time.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// packages/time/dist/temporal.js
|
|
2
|
+
import { Temporal as Polyfill } from "@js-temporal/polyfill";
|
|
3
|
+
var engineTemporal = Reflect.get(globalThis, "Temporal");
|
|
4
|
+
var Temporal = engineTemporal ?? Polyfill;
|
|
5
|
+
|
|
6
|
+
// packages/time/dist/zones.js
|
|
7
|
+
var MAX_ZONE_ID_LENGTH = 64;
|
|
8
|
+
function hasIanaShape(id) {
|
|
9
|
+
if (id.length > MAX_ZONE_ID_LENGTH)
|
|
10
|
+
return false;
|
|
11
|
+
if (id.includes("/"))
|
|
12
|
+
return true;
|
|
13
|
+
const upper = id.toUpperCase();
|
|
14
|
+
return upper === "UTC" || upper === "GMT";
|
|
15
|
+
}
|
|
16
|
+
function canonicalZone(id) {
|
|
17
|
+
if (typeof id !== "string" || !hasIanaShape(id))
|
|
18
|
+
return void 0;
|
|
19
|
+
try {
|
|
20
|
+
return new Intl.DateTimeFormat("en-US", { timeZone: id }).resolvedOptions().timeZone;
|
|
21
|
+
} catch {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function isValidZone(id) {
|
|
26
|
+
return canonicalZone(id) !== void 0;
|
|
27
|
+
}
|
|
28
|
+
function sameZone(a, b) {
|
|
29
|
+
const left = canonicalZone(a);
|
|
30
|
+
return left !== void 0 && left === canonicalZone(b);
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
Temporal,
|
|
34
|
+
canonicalZone,
|
|
35
|
+
isValidZone,
|
|
36
|
+
sameZone
|
|
37
|
+
};
|
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.
|
|
@@ -28,6 +30,31 @@ export declare function customEntriesFromEntries<T extends TranscriptSourceEntry
|
|
|
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
59
|
export declare interface TranscriptEntryLike {
|
|
33
60
|
readonly id: string;
|
|
@@ -57,4 +84,86 @@ declare interface TranscriptTreeEntryLike extends TranscriptEntryLike {
|
|
|
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
|
@@ -5,6 +5,22 @@ var WorkBlockCoverageSchema = z.strictObject({
|
|
|
5
5
|
lastToolResultEntryId: z.string().min(1),
|
|
6
6
|
toolResultEntryIds: z.array(z.string().min(1)).min(1).max(4096)
|
|
7
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";
|
|
8
24
|
function branchEntryIds(entries) {
|
|
9
25
|
const last = entries[entries.length - 1];
|
|
10
26
|
if (last === void 0)
|
|
@@ -19,11 +35,77 @@ function branchEntryIds(entries) {
|
|
|
19
35
|
return onBranch;
|
|
20
36
|
}
|
|
21
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
|
+
}
|
|
22
83
|
function customEntriesFromEntries(entries, customType, options) {
|
|
23
84
|
const onBranch = options.branch === "all" ? null : branchEntryIds(entries);
|
|
24
85
|
return entries.filter((entry) => entry.type === "custom" && entry.customType === customType && (onBranch === null || onBranch.has(entry.id)));
|
|
25
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;
|
|
26
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,
|
|
27
105
|
branchEntryIds,
|
|
28
|
-
customEntriesFromEntries
|
|
106
|
+
customEntriesFromEntries,
|
|
107
|
+
matchesWorkBlock,
|
|
108
|
+
turnSegments,
|
|
109
|
+
workBlockCoverage,
|
|
110
|
+
workBlockKey
|
|
29
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
|
|
|
@@ -85,17 +85,17 @@ separates:
|
|
|
85
85
|
`<namespace>.<localId>` space ACROSS kinds, so a tool and an endpoint both called `inspect`
|
|
86
86
|
collide and the whole plugin is refused (#8936).
|
|
87
87
|
- **Loads, minus something** — a retired `compatibility` / `permissions.byTarget` key the parser
|
|
88
|
-
strips silently
|
|
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).
|
|
89
90
|
|
|
90
91
|
It never reads the declared files or applies the roster's rules: a missing entrypoint or skill
|
|
91
|
-
file,
|
|
92
|
-
|
|
93
|
-
in `read plugin://<id>`.
|
|
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>`.
|
|
94
94
|
|
|
95
95
|
## The SDK resolution contract
|
|
96
96
|
|
|
97
97
|
- Plugin source imports `@bonsai/plugin-sdk/<subpath>` as a **bare specifier**
|
|
98
|
-
(`@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
|
|
99
99
|
exports exist).
|
|
100
100
|
- The daemon's plugin loader bundles each declared TypeScript entrypoint and its relative imports.
|
|
101
101
|
It keeps `@bonsai/plugin-sdk` imports external and resolves them to the running app's own built SDK.
|
|
@@ -293,7 +293,7 @@ file.
|
|
|
293
293
|
|
|
294
294
|
## Reference map
|
|
295
295
|
|
|
296
|
-
- [`docs/directory-plugins.md`](
|
|
296
|
+
- [`docs/directory-plugins.md`](../../../docs/directory-plugins.md) — the authoring guide:
|
|
297
297
|
manifest shape, the enable gate and what it does not gate, trust tiers, diagnostics.
|
|
298
298
|
- `packages/plugin-sdk/README.md` — the SDK surface.
|
|
299
299
|
- `packages/plugin-sdk/PUBLISHED.md` — installing the published SDK outside a Bonsai checkout.
|