@yaklabs-ai/plugin-protocol 0.8.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/index.js ADDED
@@ -0,0 +1,4805 @@
1
+ // packages/plugin-protocol/dist/app-data.js
2
+ var KAY_APP_DATA_SNAPSHOT_MAX_BYTES = 16 * 1024 * 1024;
3
+ var KAY_APP_DATA_CHANGE_MAX_BYTES = 256 * 1024;
4
+ var KAY_APP_DATA_READ_REQUEST_MAX_BYTES = 64 * 1024;
5
+ var KAY_APP_DATA_READ_RESULT_MAX_BYTES = 16 * 1024 * 1024;
6
+
7
+ // packages/plugin-protocol/dist/transcript.js
8
+ function branchEntryIds(entries) {
9
+ const last = entries[entries.length - 1];
10
+ if (last === void 0)
11
+ return EMPTY_BRANCH_IDS;
12
+ const byId = new Map(entries.map((entry) => [entry.id, entry]));
13
+ const onBranch = /* @__PURE__ */ new Set();
14
+ let cursor = last.type === "leaf" ? last.targetId ?? null : last.id;
15
+ while (cursor !== null && !onBranch.has(cursor)) {
16
+ onBranch.add(cursor);
17
+ cursor = byId.get(cursor)?.parentId ?? null;
18
+ }
19
+ return onBranch;
20
+ }
21
+ var EMPTY_BRANCH_IDS = /* @__PURE__ */ new Set();
22
+
23
+ // packages/plugin-protocol/dist/turn-packet.js
24
+ var RUN_STARTED = "session_run_started";
25
+ function textOf(entry) {
26
+ const content = entry.message?.content;
27
+ if (typeof content === "string")
28
+ return content;
29
+ if (!Array.isArray(content))
30
+ return "";
31
+ return content.map((part) => typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string" ? part.text : "").join("");
32
+ }
33
+ function originOf(entry) {
34
+ const m = entry.message;
35
+ if (m === void 0 || typeof m.clientIntentId !== "string")
36
+ return "unknown";
37
+ const details = m.details;
38
+ const deliveryId = typeof details === "object" && details !== null ? details.deliveryId : void 0;
39
+ return typeof deliveryId === "string" ? "human-verified" : "unknown";
40
+ }
41
+ function turnPacket(entries, settleEntryId, options = {}) {
42
+ const branch = branchEntryIds(entries);
43
+ const live = entries.filter((e) => branch.has(e.id));
44
+ const anchorIndex = live.findIndex((e) => e.id === settleEntryId);
45
+ const searchFrom = anchorIndex === -1 ? live.length - 1 : anchorIndex;
46
+ let runStart = -1;
47
+ for (let i = searchFrom; i >= 0; i -= 1) {
48
+ if (live[i]?.type === "custom" && live[i]?.customType === RUN_STARTED) {
49
+ runStart = i;
50
+ break;
51
+ }
52
+ }
53
+ let spanStart = runStart;
54
+ if (runStart === -1) {
55
+ spanStart = 0;
56
+ for (let i = searchFrom; i >= 0; i -= 1) {
57
+ if (live[i]?.type === "message" && live[i]?.message?.role === "user") {
58
+ spanStart = i;
59
+ break;
60
+ }
61
+ }
62
+ }
63
+ const span = live.slice(spanStart, searchFrom + 1);
64
+ const messages = span.filter((e) => e.type === "message" && e.message?.role !== void 0);
65
+ const people = messages.filter((e) => e.message?.role === "user");
66
+ const assistants = messages.filter((e) => e.message?.role === "assistant");
67
+ const opener = people[0];
68
+ if (opener === void 0)
69
+ return void 0;
70
+ const toPacket = (entry) => ({
71
+ entryId: entry.id,
72
+ timestamp: entry.timestamp,
73
+ text: textOf(entry),
74
+ origin: originOf(entry)
75
+ });
76
+ return {
77
+ personMessage: toPacket(opener),
78
+ personSteers: people.slice(1).map(toPacket),
79
+ assistantReplies: assistants.filter((e) => textOf(e).trim().length > 0).map(toPacket),
80
+ settleEntryId,
81
+ sessionTier: options.sessionTier ?? "unknown"
82
+ };
83
+ }
84
+ function precedingPersonMessages(entries, packet, count) {
85
+ if (count <= 0)
86
+ return [];
87
+ const branch = branchEntryIds(entries);
88
+ const live = entries.filter((e) => branch.has(e.id));
89
+ const openerIndex = live.findIndex((e) => e.id === packet.personMessage.entryId);
90
+ if (openerIndex === -1)
91
+ return [];
92
+ const found = [];
93
+ for (let i = openerIndex - 1; i >= 0 && found.length < count; i -= 1) {
94
+ const e = live[i];
95
+ if (e === void 0)
96
+ continue;
97
+ if (e.type !== "message" || e.message?.role !== "user")
98
+ continue;
99
+ if (originOf(e) !== "human-verified")
100
+ continue;
101
+ found.push({
102
+ entryId: e.id,
103
+ timestamp: e.timestamp,
104
+ text: textOf(e),
105
+ origin: "human-verified"
106
+ });
107
+ }
108
+ return found.reverse();
109
+ }
110
+
111
+ // packages/plugin-protocol/dist/app-data-read.js
112
+ import { z as z2 } from "zod";
113
+
114
+ // packages/plugin-protocol/dist/common.js
115
+ import { z } from "zod";
116
+ var KAY_PLUGIN_PROTOCOL_VERSION = "kay.plugin.v1";
117
+ var KAY_PLUGIN_PROTOCOL_PACKAGE_NAME = "@kay/plugin-protocol";
118
+ var KAY_PLUGIN_SDK_VERSION = "0.8.4";
119
+ var KAY_PLUGIN_TARGET_NAMES = [
120
+ "kay.agent",
121
+ "kay.app",
122
+ "kay.ui"
123
+ ];
124
+ var KayPluginProtocolVersion = z.literal(KAY_PLUGIN_PROTOCOL_VERSION);
125
+ var PluginTargetName = z.enum(KAY_PLUGIN_TARGET_NAMES);
126
+ var NonEmptyString = z.string().trim().min(1);
127
+ var JsonValue = z.lazy(() => z.union([
128
+ z.string(),
129
+ z.number(),
130
+ z.boolean(),
131
+ z.null(),
132
+ z.array(JsonValue),
133
+ z.record(z.string(), JsonValue)
134
+ ]));
135
+ var JsonObject = z.record(z.string(), JsonValue);
136
+ var JsonSchema = z.record(z.string(), JsonValue);
137
+ var PLUGIN_ID_PATTERN = /^[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)*$/;
138
+ var LOCAL_CONTRIBUTION_ID_PATTERN = /^[a-z][a-z0-9-]*$/;
139
+ var PLUGIN_TOOL_NAME_PATTERN = /^[a-z][a-z0-9_-]*$/;
140
+ var PACKAGE_ASSET_PATH_PATTERN = /^\.\/(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+$/;
141
+ var KAY_EVENT_PUBLISHER_ID = "kay";
142
+ var PluginId = z.string().trim().regex(PLUGIN_ID_PATTERN).refine((id) => id !== KAY_EVENT_PUBLISHER_ID, {
143
+ message: `'${KAY_EVENT_PUBLISHER_ID}' is reserved for Bonsai's own event publisher`
144
+ });
145
+ var PluginNamespace = PluginId;
146
+ var LocalContributionId = z.string().trim().regex(LOCAL_CONTRIBUTION_ID_PATTERN);
147
+ var GlobalContributionId = z.string().trim().regex(PLUGIN_ID_PATTERN);
148
+ var PluginToolName = z.string().trim().regex(PLUGIN_TOOL_NAME_PATTERN);
149
+ var AuthServiceReference = GlobalContributionId;
150
+ var AuthSchemeReference = GlobalContributionId;
151
+ var AdapterSelectionKey = z.string().trim().regex(PLUGIN_ID_PATTERN);
152
+ var AdapterPlacement = z.enum(["local-only", "anywhere"]);
153
+ var UriScheme = z.string().trim().regex(/^[a-z][a-z0-9+.-]*$/);
154
+ var MimeTypeString = z.string().trim().regex(/^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/);
155
+ var FileExtensionToken = z.string().trim().regex(/^[a-z0-9]+$/);
156
+ var CliBinaryName = NonEmptyString.refine((value) => !value.includes("/"), {
157
+ message: "cli binding binary/alias must be a basename, not a path (no '/')"
158
+ });
159
+ var CliAuthEnvVarName = z.string().trim().regex(/^[A-Z][A-Z0-9_]*$/);
160
+ var PackageAssetPath = z.string().trim().regex(PACKAGE_ASSET_PATH_PATTERN).refine((value) => !value.split("/").includes(".."), {
161
+ message: "package asset path must not contain a '..' path segment"
162
+ });
163
+ var PluginSourceClass = z.enum([
164
+ "bundled",
165
+ "org-shared",
166
+ "user-local",
167
+ "verified-marketplace",
168
+ "freeform"
169
+ ]);
170
+ var ArtifactGenerationId = z.string().trim().min(1).brand();
171
+ var HttpsUrl = z.url({ protocol: /^https$/ });
172
+ var RegistrableDomain = z.string().trim().regex(/^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/);
173
+ var HOST_LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
174
+ var MAX_HOST_LENGTH = 253;
175
+ var MAX_HOST_LABEL_LENGTH = 63;
176
+ var GitHostName = RegistrableDomain.max(MAX_HOST_LENGTH).refine((host) => host.split(".").every((label) => label.length <= MAX_HOST_LABEL_LENGTH && HOST_LABEL_PATTERN.test(label)), {
177
+ message: "git host labels must be 1-63 characters and start and end with a letter or digit"
178
+ });
179
+ var GitBasicAuthUser = z.string().trim().min(1).max(128).regex(/^[A-Za-z0-9._~-]+$/);
180
+ var HttpStatusCode = z.number().int().min(100).max(599);
181
+ var SignedInStatusCode = HttpStatusCode.refine((status) => !(status < 200 || status >= 300 && status < 400), {
182
+ message: "signedInStatuses cannot include 1xx or 3xx: the probe follows redirects and never observes them as a final status"
183
+ });
184
+ function hostWithinDomain(host, domain) {
185
+ return host === domain || host.endsWith(`.${domain}`);
186
+ }
187
+ function urlHostWithinDomain(url, domain) {
188
+ try {
189
+ return hostWithinDomain(new URL(url).hostname, domain);
190
+ } catch {
191
+ return false;
192
+ }
193
+ }
194
+ function refineWebSessionScopeConfined(webSession, ctx) {
195
+ const confined = (url) => webSession.cookieDomains.some((domain) => urlHostWithinDomain(url, domain));
196
+ if (!confined(webSession.loginUrl)) {
197
+ ctx.addIssue({
198
+ code: "custom",
199
+ path: ["loginUrl"],
200
+ message: "loginUrl host must fall within a declared cookie domain"
201
+ });
202
+ }
203
+ if (!confined(webSession.probe.url)) {
204
+ ctx.addIssue({
205
+ code: "custom",
206
+ path: ["probe", "url"],
207
+ message: "probe.url host must fall within a declared cookie domain"
208
+ });
209
+ }
210
+ }
211
+ var JsonPointer = z.string().trim().regex(/^(?:\/(?:[^/~]|~[01])*)*$/);
212
+ var TargetEntrypoint = z.strictObject({
213
+ module: NonEmptyString,
214
+ export: NonEmptyString.optional()
215
+ });
216
+
217
+ // packages/plugin-protocol/dist/app-data-read.js
218
+ var AppDataVersionedSchema = z2.strictObject({
219
+ version: z2.number().int().positive(),
220
+ schema: JsonSchema
221
+ });
222
+ var AppDataSourceRead = z2.strictObject({
223
+ request: AppDataVersionedSchema,
224
+ result: AppDataVersionedSchema
225
+ });
226
+
227
+ // packages/plugin-protocol/dist/artifact-provenance.js
228
+ import { z as z4 } from "zod";
229
+
230
+ // packages/plugin-protocol/dist/session-provenance.js
231
+ import { z as z3 } from "zod";
232
+ var NonEmptyString2 = z3.string().min(1);
233
+ var SessionId = NonEmptyString2;
234
+ var SessionRelationship = z3.discriminatedUnion("kind", [
235
+ /** Spawn lineage: this session is a subagent child of `sessionId`. */
236
+ z3.strictObject({ kind: z3.literal("child-of"), sessionId: SessionId }),
237
+ /**
238
+ * This session was forked from a point in `sessionId`'s transcript — `/fork`, and every
239
+ * hindsight followup. `atEntryId` pins the fork point when one was given.
240
+ */
241
+ z3.strictObject({
242
+ kind: z3.literal("forked-from"),
243
+ sessionId: SessionId,
244
+ atEntryId: NonEmptyString2.optional()
245
+ })
246
+ ]);
247
+ var SdkSessionDoor = z3.enum([
248
+ "sessions.create",
249
+ "sessions.run",
250
+ "hindsight.runFollowup",
251
+ "agents.run"
252
+ ]);
253
+ var SessionCreator = z3.discriminatedUnion("kind", [
254
+ /** A person, at the keyboard. `surface` names a scripted experience when one minted it. */
255
+ z3.strictObject({
256
+ kind: z3.literal("human"),
257
+ surface: NonEmptyString2.optional()
258
+ }),
259
+ /** Another session's agent, through a tool call. */
260
+ z3.strictObject({
261
+ kind: z3.literal("agent"),
262
+ sessionId: SessionId,
263
+ tool: NonEmptyString2,
264
+ /**
265
+ * The PARENT SESSION RUN that was driving when this session was minted
266
+ * (R2 provenance). Optional at parse so old rows stay readable; required
267
+ * of new agent producers. Absence on a new row is reported as a
268
+ * `legacy-run-id-missing` gap, never synthesized.
269
+ */
270
+ runId: NonEmptyString2.optional(),
271
+ /**
272
+ * Correlation only; never part of identity. A missing or reused
273
+ * `toolCallId` must not block the exact parent-run link and never
274
+ * participates in mutation-result identity.
275
+ */
276
+ toolCallId: NonEmptyString2.optional()
277
+ }),
278
+ /** A plugin, through one of its SDK doors. */
279
+ z3.strictObject({
280
+ kind: z3.literal("plugin"),
281
+ pluginId: NonEmptyString2,
282
+ door: SdkSessionDoor,
283
+ callerSessionId: SessionId.optional()
284
+ }),
285
+ /**
286
+ * An automation binding that fired.
287
+ *
288
+ * `triggerKind` names what the binding matched on, and `uri-change` is in the list because a
289
+ * durable watcher is a real condition kind (`@kay/automations`'s `AutomationMatch`) with no
290
+ * spelling among the other four: it watches the world OUTSIDE the session, so it is neither a
291
+ * schedule nor a session-fact occurrence. Added 2026-08-19, when the automation producers
292
+ * landed and a watcher's report session had nothing honest to stamp.
293
+ */
294
+ z3.strictObject({
295
+ kind: z3.literal("trigger"),
296
+ automationId: NonEmptyString2,
297
+ /**
298
+ * The AUTOMATION RUN that fired the binding (R2 provenance). Optional at
299
+ * parse so old rows stay readable; required of new automation producers.
300
+ * Absence on a new row is reported as a `legacy-run-id-missing` gap,
301
+ * never synthesized from the binding or a timestamp.
302
+ */
303
+ runId: NonEmptyString2.optional(),
304
+ bindingName: NonEmptyString2,
305
+ triggerKind: z3.enum(["cron", "time", "occurrence", "uri-change", "manual"])
306
+ }),
307
+ /** The daemon itself, for one of its own capabilities. */
308
+ z3.strictObject({ kind: z3.literal("daemon"), capability: NonEmptyString2 })
309
+ ]);
310
+ var AGENT_RUNTIME_SESSION_CREATOR_KINDS = [
311
+ "human",
312
+ "agent",
313
+ "plugin",
314
+ "trigger",
315
+ "daemon"
316
+ ];
317
+ var SessionDefiner = z3.discriminatedUnion("layer", [
318
+ z3.strictObject({
319
+ layer: z3.literal("plugin"),
320
+ pluginId: NonEmptyString2,
321
+ capability: NonEmptyString2,
322
+ /** See the type docblock: unpopulated by design until #5230's children land. */
323
+ version: NonEmptyString2.optional()
324
+ }),
325
+ z3.strictObject({ layer: z3.literal("host"), capability: NonEmptyString2 })
326
+ ]);
327
+ var SessionIntent = z3.enum(["interactive", "headless"]);
328
+ var SessionCausedByEventShape = z3.strictObject({
329
+ /** The plugin's own name for the subscribe handler that made this run, e.g. `"work-record"`. */
330
+ handler: NonEmptyString2,
331
+ /** Absent reads as `"session-event"` — see this type's own doc comment. */
332
+ kind: z3.enum(["session-event", "backfill"]).optional(),
333
+ /** Required when `kind` is `"session-event"` or absent; must be omitted when `kind` is `"backfill"`. */
334
+ event: z3.strictObject({
335
+ /** {@link KayAppSessionEventType} in `@kay/plugin-sdk`, mirrored: a closed, extensible union. */
336
+ type: z3.enum(["turn-settled", "phase-shift"]),
337
+ entryId: NonEmptyString2,
338
+ /** The source run captured at the event boundary; absence remains unresolved. */
339
+ runId: NonEmptyString2.optional()
340
+ }).optional(),
341
+ /**
342
+ * The plugin-supplied exact-match dedupe key, when it gave one. Absent when the handler chose
343
+ * not to dedupe this run (every call is admitted). Carried here (not just checked and
344
+ * discarded) so a later reader can explain WHY the host refused a same-keyed second run. A
345
+ * `"backfill"` cause always supplies one (`backfill:${sessionId}`) so a sweep can never
346
+ * double-run the same historical session, but that is a caller convention, not a schema rule.
347
+ */
348
+ dedupeKey: NonEmptyString2.optional()
349
+ });
350
+ var SessionCausedByEvent = SessionCausedByEventShape.refine((value) => (value.kind ?? "session-event") === "backfill" || value.event !== void 0, {
351
+ message: 'event is required unless kind is "backfill"',
352
+ path: ["event"]
353
+ }).refine((value) => value.kind !== "backfill" || value.event === void 0, {
354
+ message: 'event must be omitted when kind is "backfill"',
355
+ path: ["event"]
356
+ });
357
+ var SessionProvenanceRecord = z3.strictObject({
358
+ relationships: z3.array(SessionRelationship),
359
+ createdBy: SessionCreator,
360
+ definedBy: SessionDefiner,
361
+ intent: SessionIntent,
362
+ /** See {@link SessionCausedByEvent}. Present only alongside `createdBy.door === "agents.run"`. */
363
+ causedByEvent: SessionCausedByEvent.optional()
364
+ });
365
+
366
+ // packages/plugin-protocol/dist/artifact-provenance.js
367
+ var ARTIFACT_PROVENANCE_VERSION = "kay.artifact-provenance.v1";
368
+ function hasControlCharacter(value) {
369
+ for (let index = 0; index < value.length; index += 1) {
370
+ const code = value.charCodeAt(index);
371
+ if (code <= 31 || code === 127)
372
+ return true;
373
+ }
374
+ return false;
375
+ }
376
+ function isProvenanceJson(input) {
377
+ const active = /* @__PURE__ */ new Set();
378
+ const stack = [{ value: input }];
379
+ while (stack.length > 0) {
380
+ const { value, leave } = stack.pop();
381
+ if (value === null || typeof value === "boolean")
382
+ continue;
383
+ if (typeof value === "string") {
384
+ if (hasControlCharacter(value))
385
+ return false;
386
+ continue;
387
+ }
388
+ if (typeof value === "number") {
389
+ if (!Number.isFinite(value) || Object.is(value, -0))
390
+ return false;
391
+ continue;
392
+ }
393
+ if (typeof value !== "object")
394
+ return false;
395
+ if (leave) {
396
+ active.delete(value);
397
+ continue;
398
+ }
399
+ if (active.has(value))
400
+ return false;
401
+ const array = Array.isArray(value);
402
+ const prototype = Object.getPrototypeOf(value);
403
+ if (!array && prototype !== Object.prototype && prototype !== null)
404
+ return false;
405
+ const descriptors = Object.getOwnPropertyDescriptors(value);
406
+ const keys = Reflect.ownKeys(descriptors).filter((key) => !array || key !== "length");
407
+ if (array && (keys.length !== value.length || keys.some((key, index) => key !== String(index))))
408
+ return false;
409
+ active.add(value);
410
+ stack.push({ value, leave: true });
411
+ for (const key of keys) {
412
+ if (typeof key !== "string" || hasControlCharacter(key))
413
+ return false;
414
+ const descriptor = descriptors[key];
415
+ if (!descriptor.enumerable || !("value" in descriptor))
416
+ return false;
417
+ stack.push({ value: descriptor.value });
418
+ }
419
+ }
420
+ return true;
421
+ }
422
+ var Text = z4.string().min(1).refine((value) => !hasControlCharacter(value), {
423
+ message: "Provenance text must not contain control characters"
424
+ });
425
+ var ArtifactProvenanceRevision = z4.strictObject({
426
+ storeId: Text,
427
+ artifactId: Text,
428
+ uri: Text.optional(),
429
+ /** The enclosing commit is resolved by the reader, avoiding a self-referential commit hash. */
430
+ revision: z4.union([
431
+ Text,
432
+ z4.strictObject({ kind: z4.literal("enclosing-commit") }),
433
+ z4.null()
434
+ ]),
435
+ operationId: Text.nullable(),
436
+ /** Optional when the store identifies revisions without content addressing (including tombstones). */
437
+ digest: z4.strictObject({ algorithm: Text, value: Text }).optional()
438
+ });
439
+ var ArtifactProvenanceActor = z4.discriminatedUnion("kind", [
440
+ z4.strictObject({
441
+ kind: z4.literal("session"),
442
+ sessionId: Text,
443
+ runId: Text.nullable()
444
+ }),
445
+ z4.strictObject({
446
+ kind: z4.literal("human"),
447
+ userId: Text.optional(),
448
+ surface: Text
449
+ }),
450
+ z4.strictObject({
451
+ kind: z4.literal("plugin"),
452
+ pluginId: Text,
453
+ capability: Text
454
+ }),
455
+ z4.strictObject({ kind: z4.literal("host"), capability: Text }),
456
+ z4.strictObject({ kind: z4.literal("import"), source: Text }),
457
+ z4.strictObject({ kind: z4.literal("unknown") })
458
+ ]);
459
+ var ArtifactProvenanceCause = z4.discriminatedUnion("kind", [
460
+ z4.strictObject({
461
+ kind: z4.literal("session"),
462
+ sessionId: Text,
463
+ runId: Text.nullable()
464
+ }),
465
+ z4.strictObject({
466
+ kind: z4.literal("session-event"),
467
+ sessionId: Text,
468
+ cause: SessionCausedByEvent
469
+ }),
470
+ z4.strictObject({
471
+ kind: z4.literal("automation"),
472
+ automationId: Text,
473
+ runId: Text.nullable()
474
+ }),
475
+ z4.strictObject({ kind: z4.literal("operation"), operationId: Text })
476
+ ]);
477
+ var ArtifactProvenanceOperation = z4.strictObject({
478
+ id: Text.nullable(),
479
+ at: z4.iso.datetime().nullable(),
480
+ name: Text,
481
+ actor: ArtifactProvenanceActor,
482
+ /** Null means no tool invocation; a known invocation with an unknown id records a gap. */
483
+ invocation: z4.strictObject({
484
+ tool: Text,
485
+ toolCallId: Text.nullable(),
486
+ origin: z4.enum(["model", "host"])
487
+ }).nullable(),
488
+ /** Known causes only; unavailable cause capture requires a gap, never a guessed creation cause. */
489
+ causes: z4.array(ArtifactProvenanceCause)
490
+ });
491
+ var ArtifactProvenanceReference = z4.discriminatedUnion("kind", [
492
+ z4.strictObject({
493
+ kind: z4.literal("session"),
494
+ sessionId: Text,
495
+ runId: Text.optional()
496
+ }),
497
+ z4.strictObject({
498
+ kind: z4.literal("artifact"),
499
+ revision: ArtifactProvenanceRevision
500
+ }),
501
+ z4.strictObject({
502
+ kind: z4.literal("resource"),
503
+ uri: Text,
504
+ revision: Text.nullable()
505
+ })
506
+ ]);
507
+ var ArtifactProvenanceGap = z4.strictObject({
508
+ code: z4.enum([
509
+ "unknown",
510
+ "capture-failed",
511
+ "legacy-derived",
512
+ "ambiguous-predecessor",
513
+ "unverified-index",
514
+ "missing-ancestor",
515
+ "cycle",
516
+ "truncated",
517
+ "metadata-write-failed",
518
+ "invalid-record"
519
+ ]),
520
+ /** JSON Pointer into this envelope; the empty pointer covers the whole capture. */
521
+ at: z4.string().regex(/^(?:\/(?:[^~/]|~[01])*)*$/).refine((value) => !hasControlCharacter(value)),
522
+ detail: Text.optional()
523
+ }).refine((gap) => gap.at !== "" || gap.code === "capture-failed", {
524
+ message: "Only a whole-capture failure may cover the entire envelope"
525
+ });
526
+ var ArtifactProvenanceSession = z4.strictObject({
527
+ sessionId: Text,
528
+ basis: z4.enum(["recorded", "legacy-derived", "unverified-index", "missing"]),
529
+ record: SessionProvenanceRecord.nullable()
530
+ });
531
+ var ArtifactProvenanceSource = z4.strictObject({
532
+ relationship: z4.enum([
533
+ "revision-of",
534
+ "copied-from",
535
+ "harvested-from",
536
+ "restored-from",
537
+ "accepted-from",
538
+ "imported-from"
539
+ ]),
540
+ revision: ArtifactProvenanceRevision
541
+ });
542
+ var ArtifactProvenanceMutation = z4.strictObject({
543
+ kind: z4.enum([
544
+ "create",
545
+ "update",
546
+ "delete",
547
+ "copy",
548
+ "harvest",
549
+ "restore",
550
+ "accept",
551
+ "import"
552
+ ]),
553
+ target: ArtifactProvenanceRevision,
554
+ /** Retained from the source on copies/acceptance; operation.actor is the persistence actor. */
555
+ producer: ArtifactProvenanceOperation,
556
+ sources: z4.array(ArtifactProvenanceSource)
557
+ });
558
+ var Envelope = z4.strictObject({
559
+ version: z4.literal(ARTIFACT_PROVENANCE_VERSION),
560
+ operation: ArtifactProvenanceOperation,
561
+ artifacts: z4.array(ArtifactProvenanceMutation).min(1),
562
+ sessions: z4.array(ArtifactProvenanceSession),
563
+ subjects: z4.array(ArtifactProvenanceReference),
564
+ evidence: z4.array(ArtifactProvenanceReference),
565
+ gaps: z4.array(ArtifactProvenanceGap)
566
+ });
567
+ var UNKNOWN = [
568
+ "unknown",
569
+ "capture-failed",
570
+ "metadata-write-failed",
571
+ "invalid-record"
572
+ ];
573
+ var MISSING = ["missing-ancestor", "truncated"];
574
+ function checkOperation(operation, path, gap, session) {
575
+ if (operation.id === null)
576
+ gap(`${path}/id`, UNKNOWN);
577
+ if (operation.at === null)
578
+ gap(`${path}/at`, UNKNOWN);
579
+ if (operation.actor.kind === "unknown")
580
+ gap(`${path}/actor`, UNKNOWN);
581
+ if (operation.actor.kind === "session") {
582
+ session(operation.actor.sessionId, `${path}/actor/sessionId`);
583
+ if (operation.actor.runId === null)
584
+ gap(`${path}/actor/runId`, UNKNOWN);
585
+ }
586
+ if (operation.invocation?.toolCallId === null)
587
+ gap(`${path}/invocation/toolCallId`, UNKNOWN);
588
+ operation.causes.forEach((cause, index) => {
589
+ const causePath = `${path}/causes/${index}`;
590
+ if (cause.kind === "session" || cause.kind === "session-event")
591
+ session(cause.sessionId, `${causePath}/sessionId`);
592
+ if (cause.kind === "session-event" && (cause.cause.kind ?? "session-event") !== "backfill" && cause.cause.event?.runId === void 0) {
593
+ gap(`${causePath}/cause/event/runId`, UNKNOWN);
594
+ }
595
+ if ("runId" in cause && cause.runId === null)
596
+ gap(`${causePath}/runId`, UNKNOWN);
597
+ });
598
+ }
599
+ function checkRevision(revision, path, gap, issue, currentTarget = false) {
600
+ if (revision.revision === null)
601
+ gap(`${path}/revision`, UNKNOWN);
602
+ else if (typeof revision.revision === "object") {
603
+ if (!currentTarget)
604
+ issue(path.slice(1).split("/"), "Only the current target may refer to its enclosing commit");
605
+ if (revision.digest === void 0)
606
+ issue(path.slice(1).split("/"), "An enclosing-commit target must bind the exact content digest before commit");
607
+ }
608
+ if (revision.operationId === null)
609
+ gap(`${path}/operationId`, UNKNOWN);
610
+ }
611
+ function checkGraph(envelope, gap, issue) {
612
+ const nodes = new Map(envelope.sessions.map((node, index) => [node.sessionId, index]));
613
+ const edges = /* @__PURE__ */ new Map();
614
+ envelope.sessions.forEach((node, index) => {
615
+ const path = `/sessions/${index}`;
616
+ if (nodes.get(node.sessionId) !== index)
617
+ issue(["sessions", index, "sessionId"], "Session snapshots must be deduplicated by sessionId");
618
+ if (node.basis === "missing") {
619
+ if (node.record !== null)
620
+ issue(["sessions", index, "record"], "A missing snapshot cannot carry a creation record");
621
+ gap(path, MISSING);
622
+ } else if (node.record === null) {
623
+ issue(["sessions", index, "record"], "A non-missing snapshot requires its creation record");
624
+ }
625
+ if (node.basis === "legacy-derived")
626
+ gap(path, ["legacy-derived"]);
627
+ if (node.basis === "unverified-index")
628
+ gap(path, ["unverified-index"]);
629
+ const record = node.record;
630
+ if (record === null)
631
+ return;
632
+ const outgoing = record.relationships.map((relationship, edgeIndex) => ({
633
+ id: relationship.sessionId,
634
+ path: `${path}/record/relationships/${edgeIndex}/sessionId`
635
+ }));
636
+ const creator = record.createdBy;
637
+ if (creator.kind === "agent")
638
+ outgoing.push({
639
+ id: creator.sessionId,
640
+ path: `${path}/record/createdBy/sessionId`
641
+ });
642
+ if (creator.kind === "plugin" && creator.callerSessionId !== void 0)
643
+ outgoing.push({
644
+ id: creator.callerSessionId,
645
+ path: `${path}/record/createdBy/callerSessionId`
646
+ });
647
+ if ((creator.kind === "agent" || creator.kind === "trigger") && creator.runId === void 0 && node.basis === "recorded")
648
+ gap(`${path}/record/createdBy/runId`, UNKNOWN);
649
+ const eventCause = record.causedByEvent;
650
+ if (eventCause !== void 0 && (eventCause.kind ?? "session-event") !== "backfill" && eventCause.event?.runId === void 0 && node.basis === "recorded") {
651
+ gap(`${path}/record/causedByEvent/event/runId`, UNKNOWN);
652
+ }
653
+ edges.set(node.sessionId, outgoing);
654
+ outgoing.forEach((edge) => {
655
+ if (!nodes.has(edge.id))
656
+ gap(edge.path, MISSING);
657
+ });
658
+ });
659
+ const state = /* @__PURE__ */ new Map();
660
+ for (const node of envelope.sessions) {
661
+ if (state.has(node.sessionId))
662
+ continue;
663
+ const stack = [{ id: node.sessionId, next: 0 }];
664
+ state.set(node.sessionId, "active");
665
+ while (stack.length > 0) {
666
+ const current = stack[stack.length - 1];
667
+ const edge = edges.get(current.id)?.[current.next++];
668
+ if (edge === void 0) {
669
+ state.set(current.id, "done");
670
+ stack.pop();
671
+ } else if (state.get(edge.id) === "active") {
672
+ gap(edge.path, ["cycle"]);
673
+ } else if (nodes.has(edge.id) && !state.has(edge.id)) {
674
+ state.set(edge.id, "active");
675
+ stack.push({ id: edge.id, next: 0 });
676
+ }
677
+ }
678
+ }
679
+ }
680
+ function validateEnvelope(envelope, context) {
681
+ const issue = (path, message) => context.addIssue({ code: "custom", path, message });
682
+ const gap = (path, codes) => {
683
+ if (!envelope.gaps.some((entry) => entry.code === "capture-failed" && entry.at === "" || codes.includes(entry.code) && (path === entry.at || path.startsWith(`${entry.at}/`)))) {
684
+ issue(path.slice(1).split("/"), `Unresolved provenance requires an explicit ${codes.join(" or ")} gap`);
685
+ }
686
+ };
687
+ const nodes = new Set(envelope.sessions.map((node) => node.sessionId));
688
+ const session = (id, path) => {
689
+ if (!nodes.has(id))
690
+ gap(path, MISSING);
691
+ };
692
+ if (envelope.operation.id === null)
693
+ issue(["operation", "id"], "The current mutation requires a fresh operation identity");
694
+ checkOperation(envelope.operation, "/operation", gap, session);
695
+ const targets = /* @__PURE__ */ new Set();
696
+ envelope.artifacts.forEach((artifact, index) => {
697
+ const path = `/artifacts/${index}`;
698
+ const key = JSON.stringify([
699
+ artifact.target.storeId,
700
+ artifact.target.artifactId
701
+ ]);
702
+ if (targets.has(key))
703
+ issue(["artifacts", index, "target"], "One operation records each target artifact once");
704
+ targets.add(key);
705
+ if (artifact.target.operationId !== envelope.operation.id)
706
+ issue(["artifacts", index, "target", "operationId"], "The target must name the current persistence operation");
707
+ checkRevision(artifact.target, `${path}/target`, gap, issue, true);
708
+ checkOperation(artifact.producer, `${path}/producer`, gap, session);
709
+ artifact.sources.forEach((source, sourceIndex) => checkRevision(source.revision, `${path}/sources/${sourceIndex}/revision`, gap, issue));
710
+ const requiredSource = {
711
+ create: void 0,
712
+ update: "revision-of",
713
+ delete: "revision-of",
714
+ copy: "copied-from",
715
+ harvest: "harvested-from",
716
+ restore: "restored-from",
717
+ accept: "accepted-from",
718
+ import: "imported-from"
719
+ }[artifact.kind];
720
+ if (requiredSource !== void 0 && !artifact.sources.some((source) => source.relationship === requiredSource))
721
+ gap(`${path}/sources`, UNKNOWN);
722
+ });
723
+ for (const role of ["subjects", "evidence"]) {
724
+ envelope[role].forEach((reference, index) => {
725
+ const path = `/${role}/${index}`;
726
+ if (reference.kind === "session")
727
+ session(reference.sessionId, `${path}/sessionId`);
728
+ if (reference.kind === "artifact")
729
+ checkRevision(reference.revision, `${path}/revision`, gap, issue);
730
+ if (reference.kind === "resource" && reference.revision === null)
731
+ gap(`${path}/revision`, UNKNOWN);
732
+ });
733
+ }
734
+ checkGraph(envelope, gap, issue);
735
+ }
736
+ var ArtifactProvenanceEnvelopeV1 = z4.unknown().refine(isProvenanceJson, {
737
+ message: "Provenance must be plain, acyclic, lossless JSON without control characters"
738
+ }).pipe(Envelope.superRefine(validateEnvelope));
739
+ function parseArtifactProvenanceEnvelope(input) {
740
+ return ArtifactProvenanceEnvelopeV1.parse(input);
741
+ }
742
+ var ArtifactProvenanceCaptureContextSchema = z4.strictObject({
743
+ operation: ArtifactProvenanceOperation,
744
+ sessions: z4.array(ArtifactProvenanceSession),
745
+ subjects: z4.array(ArtifactProvenanceReference),
746
+ evidence: z4.array(ArtifactProvenanceReference),
747
+ gaps: z4.array(ArtifactProvenanceGap)
748
+ });
749
+ function parseArtifactProvenanceCaptureContext(input) {
750
+ return ArtifactProvenanceCaptureContextSchema.parse(input);
751
+ }
752
+
753
+ // packages/plugin-protocol/dist/artifact-provenance-capture.js
754
+ var DEFAULT_CLOCK = {
755
+ now: () => (/* @__PURE__ */ new Date()).toISOString(),
756
+ mintId: () => globalThis.crypto.randomUUID()
757
+ };
758
+ function mintOperation(input, clock = DEFAULT_CLOCK) {
759
+ const gaps = [];
760
+ if (input.actor.kind === "unknown") {
761
+ gaps.push({ code: "unknown", at: "/operation/actor" });
762
+ }
763
+ if (input.actor.kind === "session" && input.actor.runId === null) {
764
+ gaps.push({ code: "unknown", at: "/operation/actor/runId" });
765
+ }
766
+ const invocation = input.invocation ?? null;
767
+ if (invocation !== null && invocation.toolCallId === null) {
768
+ gaps.push({ code: "unknown", at: "/operation/invocation/toolCallId" });
769
+ }
770
+ const causes = input.causes ?? [];
771
+ causes.forEach((cause, index) => {
772
+ if ("runId" in cause && cause.runId === null) {
773
+ gaps.push({ code: "unknown", at: `/operation/causes/${index}/runId` });
774
+ }
775
+ if (cause.kind === "session-event" && (cause.cause.kind ?? "session-event") !== "backfill" && cause.cause.event?.runId === void 0) {
776
+ gaps.push({
777
+ code: "unknown",
778
+ at: `/operation/causes/${index}/cause/event/runId`
779
+ });
780
+ }
781
+ });
782
+ return {
783
+ operation: {
784
+ id: input.id ?? clock.mintId(),
785
+ at: input.at ?? clock.now(),
786
+ name: input.name,
787
+ actor: input.actor,
788
+ invocation,
789
+ // Copied into a fresh mutable array: `ArtifactProvenanceOperation.causes` is a zod-inferred
790
+ // mutable array, and `causes` here is `readonly` only because it defaulted from an
791
+ // optional readonly input.
792
+ causes: [...causes]
793
+ },
794
+ gaps
795
+ };
796
+ }
797
+ function captureFailedContext(input, clock = DEFAULT_CLOCK) {
798
+ return {
799
+ operation: {
800
+ id: clock.mintId(),
801
+ at: clock.now(),
802
+ name: input.name,
803
+ actor: { kind: "unknown" },
804
+ invocation: null,
805
+ causes: []
806
+ },
807
+ sessions: [],
808
+ subjects: [],
809
+ evidence: [],
810
+ // The detail is an error message, and error text can carry C0/DEL control characters (a
811
+ // multi-line zod or V8 message). Escaped, never dropped: a control character would break the
812
+ // kernel's lossless-JSON check for the whole capture, turning a failed capture into a failed
813
+ // MUTATION — the one outcome this record exists to prevent.
814
+ gaps: [
815
+ {
816
+ code: "capture-failed",
817
+ at: "",
818
+ detail: scrubControlCharacters(input.detail)
819
+ }
820
+ ]
821
+ };
822
+ }
823
+ function scrubControlCharacters(value) {
824
+ let out = "";
825
+ for (let index = 0; index < value.length; index += 1) {
826
+ const code = value.charCodeAt(index);
827
+ out += code <= 31 || code === 127 ? `\\u${code.toString(16).padStart(4, "0")}` : value[index];
828
+ }
829
+ return out;
830
+ }
831
+ var DEFAULT_MAX_NODES = 128;
832
+ function edgesOf(record) {
833
+ const edges = record.relationships.map((r) => r.sessionId);
834
+ const creator = record.createdBy;
835
+ if (creator.kind === "agent")
836
+ edges.push(creator.sessionId);
837
+ if (creator.kind === "plugin" && creator.callerSessionId !== void 0) {
838
+ edges.push(creator.callerSessionId);
839
+ }
840
+ return edges;
841
+ }
842
+ async function resolveSessionAncestry(options) {
843
+ const maxNodes = options.maxNodes ?? DEFAULT_MAX_NODES;
844
+ const resolved = /* @__PURE__ */ new Map();
845
+ const order = [];
846
+ const queued = /* @__PURE__ */ new Set();
847
+ const queue = [];
848
+ const enqueue = (id) => {
849
+ if (queued.has(id) || resolved.has(id))
850
+ return;
851
+ queued.add(id);
852
+ queue.push(id);
853
+ };
854
+ for (const id of options.seedSessionIds)
855
+ enqueue(id);
856
+ let truncated = false;
857
+ while (queue.length > 0) {
858
+ const id = queue.shift();
859
+ if (order.length >= maxNodes) {
860
+ truncated = true;
861
+ continue;
862
+ }
863
+ const result = await options.lookup(id);
864
+ resolved.set(id, result);
865
+ order.push(id);
866
+ if (result.record !== null) {
867
+ for (const edge of edgesOf(result.record))
868
+ enqueue(edge);
869
+ }
870
+ }
871
+ truncated = truncated || queue.length > 0;
872
+ const indexOf = new Map(order.map((id, index) => [id, index]));
873
+ const sessions = order.map((id) => {
874
+ const result = resolved.get(id);
875
+ return { sessionId: id, basis: result.basis, record: result.record };
876
+ });
877
+ const gaps = [];
878
+ if (truncated) {
879
+ gaps.push({
880
+ code: "truncated",
881
+ at: "/sessions",
882
+ detail: `Ancestry walk stopped at ${maxNodes} sessions`
883
+ });
884
+ }
885
+ sessions.forEach((node, index) => {
886
+ const path = `/sessions/${index}`;
887
+ if (node.basis === "missing" && !truncated) {
888
+ gaps.push({ code: "missing-ancestor", at: path });
889
+ }
890
+ if (node.basis === "legacy-derived") {
891
+ gaps.push({ code: "legacy-derived", at: path });
892
+ }
893
+ if (node.basis === "unverified-index") {
894
+ gaps.push({ code: "unverified-index", at: path });
895
+ }
896
+ const record = node.record;
897
+ if (record === null)
898
+ return;
899
+ record.relationships.forEach((relationship, edgeIndex) => {
900
+ if (!indexOf.has(relationship.sessionId) && !truncated) {
901
+ gaps.push({
902
+ code: "missing-ancestor",
903
+ at: `${path}/record/relationships/${edgeIndex}/sessionId`
904
+ });
905
+ }
906
+ });
907
+ const creator = record.createdBy;
908
+ if (creator.kind === "agent" && !indexOf.has(creator.sessionId) && !truncated) {
909
+ gaps.push({
910
+ code: "missing-ancestor",
911
+ at: `${path}/record/createdBy/sessionId`
912
+ });
913
+ }
914
+ if (creator.kind === "plugin" && creator.callerSessionId !== void 0 && !indexOf.has(creator.callerSessionId) && !truncated) {
915
+ gaps.push({
916
+ code: "missing-ancestor",
917
+ at: `${path}/record/createdBy/callerSessionId`
918
+ });
919
+ }
920
+ if ((creator.kind === "agent" || creator.kind === "trigger") && creator.runId === void 0 && node.basis === "recorded") {
921
+ gaps.push({
922
+ code: "unknown",
923
+ at: `${path}/record/createdBy/runId`
924
+ });
925
+ }
926
+ const eventCause = record.causedByEvent;
927
+ if (eventCause !== void 0 && (eventCause.kind ?? "session-event") !== "backfill" && eventCause.event?.runId === void 0 && node.basis === "recorded") {
928
+ gaps.push({
929
+ code: "unknown",
930
+ at: `${path}/record/causedByEvent/event/runId`
931
+ });
932
+ }
933
+ });
934
+ const state = /* @__PURE__ */ new Map();
935
+ const edgesByIndex = sessions.map((node) => {
936
+ if (node.record === null)
937
+ return [];
938
+ const resolvedEdges = [];
939
+ edgesOf(node.record).forEach((id, slot) => {
940
+ const targetIndex = indexOf.get(id);
941
+ if (targetIndex !== void 0)
942
+ resolvedEdges.push({ targetIndex, slot });
943
+ });
944
+ return resolvedEdges;
945
+ });
946
+ for (let start = 0; start < sessions.length; start += 1) {
947
+ const startId = sessions[start].sessionId;
948
+ if (state.has(startId))
949
+ continue;
950
+ const stack = [
951
+ { index: start, next: 0 }
952
+ ];
953
+ state.set(startId, "active");
954
+ while (stack.length > 0) {
955
+ const frame = stack[stack.length - 1];
956
+ const edges = edgesByIndex[frame.index];
957
+ if (frame.next >= edges.length) {
958
+ state.set(sessions[frame.index].sessionId, "done");
959
+ stack.pop();
960
+ continue;
961
+ }
962
+ const edge = edges[frame.next];
963
+ frame.next += 1;
964
+ const targetId = sessions[edge.targetIndex].sessionId;
965
+ const targetState = state.get(targetId);
966
+ if (targetState === "active") {
967
+ gaps.push({
968
+ code: "cycle",
969
+ at: edgeSlotPath(sessions[frame.index], frame.index, edge.slot)
970
+ });
971
+ } else if (targetState === void 0) {
972
+ state.set(targetId, "active");
973
+ stack.push({ index: edge.targetIndex, next: 0 });
974
+ }
975
+ }
976
+ }
977
+ return { sessions, gaps };
978
+ }
979
+ function edgeSlotPath(node, nodeIndex, slot) {
980
+ const path = `/sessions/${nodeIndex}`;
981
+ const record = node.record;
982
+ if (slot < record.relationships.length) {
983
+ return `${path}/record/relationships/${slot}/sessionId`;
984
+ }
985
+ const creator = record.createdBy;
986
+ return creator.kind === "agent" ? `${path}/record/createdBy/sessionId` : `${path}/record/createdBy/callerSessionId`;
987
+ }
988
+ async function captureArtifactProvenance(input, clock = DEFAULT_CLOCK) {
989
+ const { operation, gaps: operationGaps } = mintOperation(input.operation, clock);
990
+ const { sessions, gaps: ancestryGaps } = await resolveSessionAncestry({
991
+ seedSessionIds: [
992
+ .../* @__PURE__ */ new Set([
993
+ ...input.seedSessionIds,
994
+ ...operation.causes.flatMap((cause) => cause.kind === "session" || cause.kind === "session-event" ? [cause.sessionId] : []),
995
+ ...(input.subjects ?? []).flatMap((reference) => reference.kind === "session" ? [reference.sessionId] : []),
996
+ ...(input.evidence ?? []).flatMap((reference) => reference.kind === "session" ? [reference.sessionId] : [])
997
+ ])
998
+ ],
999
+ lookup: input.lookup,
1000
+ ...input.maxNodes === void 0 ? {} : { maxNodes: input.maxNodes }
1001
+ });
1002
+ return {
1003
+ operation,
1004
+ sessions,
1005
+ subjects: input.subjects ?? [],
1006
+ evidence: input.evidence ?? [],
1007
+ gaps: [...operationGaps, ...ancestryGaps]
1008
+ };
1009
+ }
1010
+ function finalizeArtifactProvenanceEnvelope(capture, artifacts) {
1011
+ const artifactGaps = [];
1012
+ const blanket = capture.gaps.some((gap) => gap.code === "capture-failed" && gap.at === "");
1013
+ if (!blanket) {
1014
+ artifacts.forEach((artifact, index) => {
1015
+ if (artifact.target.revision === null) {
1016
+ artifactGaps.push({
1017
+ code: "unknown",
1018
+ at: `/artifacts/${index}/target/revision`
1019
+ });
1020
+ }
1021
+ if (artifact.producer === capture.operation) {
1022
+ for (const gap of capture.gaps) {
1023
+ if (gap.at === "/operation" || gap.at.startsWith("/operation/")) {
1024
+ artifactGaps.push({
1025
+ ...gap,
1026
+ at: `/artifacts/${index}/producer${gap.at.slice("/operation".length)}`
1027
+ });
1028
+ }
1029
+ }
1030
+ }
1031
+ });
1032
+ }
1033
+ return parseArtifactProvenanceEnvelope({
1034
+ version: ARTIFACT_PROVENANCE_VERSION,
1035
+ operation: capture.operation,
1036
+ artifacts,
1037
+ sessions: capture.sessions,
1038
+ subjects: capture.subjects,
1039
+ evidence: capture.evidence,
1040
+ gaps: [...capture.gaps, ...artifactGaps]
1041
+ });
1042
+ }
1043
+
1044
+ // packages/plugin-protocol/dist/authority.js
1045
+ import { z as z6 } from "zod";
1046
+
1047
+ // packages/plugin-protocol/dist/permissions.js
1048
+ import { z as z5 } from "zod";
1049
+ var AuthGrantOperation = z5.enum([
1050
+ "status",
1051
+ "connect",
1052
+ "disconnect",
1053
+ "resolve"
1054
+ ]);
1055
+ var AuthAccountSelector = z5.discriminatedUnion("kind", [
1056
+ z5.strictObject({ kind: z5.literal("active") }),
1057
+ z5.strictObject({ kind: z5.literal("account-id"), accountId: NonEmptyString }),
1058
+ z5.strictObject({ kind: z5.literal("record-id"), recordId: NonEmptyString })
1059
+ ]);
1060
+ var AuthScopeDefinition = z5.strictObject({
1061
+ id: NonEmptyString,
1062
+ label: NonEmptyString,
1063
+ description: NonEmptyString,
1064
+ documentationUrl: z5.url().optional()
1065
+ });
1066
+ var ScopeNeed = z5.strictObject({
1067
+ scope: NonEmptyString,
1068
+ reason: NonEmptyString.optional()
1069
+ });
1070
+ var ScopeAlternative = z5.strictObject({
1071
+ allOf: z5.array(ScopeNeed).min(1)
1072
+ });
1073
+ var ScopeRequirement = z5.strictObject({
1074
+ purpose: NonEmptyString,
1075
+ alternatives: z5.array(ScopeAlternative).min(1)
1076
+ });
1077
+ var AuthGrantRequestBase = z5.strictObject({
1078
+ // A local (undotted) service must be declared in this same manifest; a
1079
+ // qualified (dotted) service names another plugin's global id and is an
1080
+ // external service dependency, recorded on the resolved contract's
1081
+ // `consumesAuthServices` rather than resolved here.
1082
+ service: AuthServiceReference,
1083
+ // Optional for backward compatibility with grants whose service has a
1084
+ // single unambiguous scheme. Local ids resolve under the service owner's
1085
+ // namespace; qualified ids are preserved.
1086
+ schemes: z5.array(AuthSchemeReference).min(1).optional(),
1087
+ operations: z5.array(AuthGrantOperation).min(1),
1088
+ purposes: z5.array(NonEmptyString).optional(),
1089
+ // Bridged by the runtime into an enforceable `CredentialGrant`:
1090
+ // `allowedScopes` is the caller's scope ENVELOPE and
1091
+ // `accountSelectors` map through. Absent `accountSelectors` resolves to the
1092
+ // narrowest bound (see `ResolvedAuthGrantRequest`); absent scope envelope
1093
+ // leaves the bridged grant scope-agnostic.
1094
+ allowedScopes: z5.array(NonEmptyString).min(1).optional(),
1095
+ // Legacy spelling for `allowedScopes`; new manifests should use the authority-shaped name.
1096
+ requiredScopes: z5.array(NonEmptyString).min(1).optional(),
1097
+ // Preferred proactive consent bundle; runtime resolution still uses `scopeRequirement`.
1098
+ connectScopes: z5.array(NonEmptyString).min(1).optional(),
1099
+ scopeRequirement: ScopeRequirement.optional(),
1100
+ scopeDefinitions: z5.array(AuthScopeDefinition).min(1).optional(),
1101
+ accountSelectors: z5.array(AuthAccountSelector).min(1).optional()
1102
+ });
1103
+ function validateAuthGrantRequest(grant, ctx) {
1104
+ if (grant.allowedScopes !== void 0 && grant.requiredScopes !== void 0) {
1105
+ ctx.addIssue({
1106
+ code: "custom",
1107
+ path: ["allowedScopes"],
1108
+ message: "allowedScopes and legacy requiredScopes are mutually exclusive"
1109
+ });
1110
+ }
1111
+ if (grant.requiredScopes !== void 0 && (grant.connectScopes !== void 0 || grant.scopeRequirement !== void 0 || grant.scopeDefinitions !== void 0)) {
1112
+ ctx.addIssue({
1113
+ code: "custom",
1114
+ path: ["requiredScopes"],
1115
+ message: "legacy requiredScopes cannot be combined with connectScopes, scopeRequirement, or scopeDefinitions"
1116
+ });
1117
+ }
1118
+ const envelope = grant.allowedScopes ?? grant.requiredScopes;
1119
+ if (grant.connectScopes !== void 0) {
1120
+ if (grant.allowedScopes === void 0) {
1121
+ ctx.addIssue({
1122
+ code: "custom",
1123
+ path: ["connectScopes"],
1124
+ message: "connectScopes requires an allowedScopes envelope"
1125
+ });
1126
+ } else {
1127
+ const allowed = new Set(grant.allowedScopes);
1128
+ for (const [scopeIndex, scope] of grant.connectScopes.entries()) {
1129
+ if (!allowed.has(scope)) {
1130
+ ctx.addIssue({
1131
+ code: "custom",
1132
+ path: ["connectScopes", scopeIndex],
1133
+ message: `scope '${scope}' is outside allowedScopes`
1134
+ });
1135
+ }
1136
+ }
1137
+ }
1138
+ }
1139
+ if (grant.scopeRequirement !== void 0) {
1140
+ if (envelope === void 0) {
1141
+ ctx.addIssue({
1142
+ code: "custom",
1143
+ path: ["scopeRequirement"],
1144
+ message: "scopeRequirement requires an allowedScopes envelope"
1145
+ });
1146
+ } else {
1147
+ const allowed = new Set(envelope);
1148
+ for (const [alternativeIndex, alternative] of grant.scopeRequirement.alternatives.entries()) {
1149
+ for (const [needIndex, need] of alternative.allOf.entries()) {
1150
+ if (!allowed.has(need.scope)) {
1151
+ ctx.addIssue({
1152
+ code: "custom",
1153
+ path: [
1154
+ "scopeRequirement",
1155
+ "alternatives",
1156
+ alternativeIndex,
1157
+ "allOf",
1158
+ needIndex,
1159
+ "scope"
1160
+ ],
1161
+ message: `scope '${need.scope}' is outside allowedScopes`
1162
+ });
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+ const seenDefinitions = /* @__PURE__ */ new Set();
1169
+ for (const [index, definition] of (grant.scopeDefinitions ?? []).entries()) {
1170
+ if (seenDefinitions.has(definition.id)) {
1171
+ ctx.addIssue({
1172
+ code: "custom",
1173
+ path: ["scopeDefinitions", index, "id"],
1174
+ message: `duplicate scope definition '${definition.id}'`
1175
+ });
1176
+ }
1177
+ seenDefinitions.add(definition.id);
1178
+ }
1179
+ }
1180
+ var AuthGrantRequest = AuthGrantRequestBase.superRefine(validateAuthGrantRequest);
1181
+ var StorageGrantRequest = z5.strictObject({
1182
+ kind: z5.enum(["sqlite", "dataDir"]),
1183
+ operations: z5.array(z5.enum(["open", "read", "write"])).min(1)
1184
+ });
1185
+ var EventGrantRequest = z5.strictObject({
1186
+ event: NonEmptyString,
1187
+ operations: z5.array(z5.enum(["subscribe", "publish"])).min(1)
1188
+ });
1189
+ var EndpointGrantRequest = z5.strictObject({
1190
+ endpoint: LocalContributionId,
1191
+ operations: z5.array(z5.enum(["register", "invoke"])).min(1)
1192
+ });
1193
+ var WebSessionOrigin = z5.string().superRefine((value, ctx) => {
1194
+ try {
1195
+ const url = new URL(value);
1196
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "" || url.origin !== value) {
1197
+ ctx.addIssue({
1198
+ code: "custom",
1199
+ message: "must be a canonical HTTPS origin without credentials, path, query, or fragment"
1200
+ });
1201
+ }
1202
+ } catch {
1203
+ ctx.addIssue({ code: "custom", message: "must be a valid HTTPS origin" });
1204
+ }
1205
+ });
1206
+ var WebSessionCsrfSourceUrl = z5.url({ protocol: /^https$/ }).superRefine((value, ctx) => {
1207
+ const url = new URL(value);
1208
+ if (url.username !== "" || url.password !== "" || url.hash !== "") {
1209
+ ctx.addIssue({
1210
+ code: "custom",
1211
+ message: "must not contain credentials or a fragment"
1212
+ });
1213
+ }
1214
+ });
1215
+ var WebSessionPathPrefix = NonEmptyString.superRefine((value, ctx) => {
1216
+ if (!value.startsWith("/") || value.includes("?") || value.includes("#")) {
1217
+ ctx.addIssue({
1218
+ code: "custom",
1219
+ message: "must be an absolute path prefix without a query or fragment"
1220
+ });
1221
+ return;
1222
+ }
1223
+ if (/%(?:25|2e|2f|5c)/iu.test(value)) {
1224
+ ctx.addIssue({
1225
+ code: "custom",
1226
+ message: "must not contain encoded percent, dot, or path-separator characters"
1227
+ });
1228
+ return;
1229
+ }
1230
+ let decoded;
1231
+ try {
1232
+ decoded = decodeURIComponent(value);
1233
+ } catch {
1234
+ ctx.addIssue({
1235
+ code: "custom",
1236
+ message: "contains invalid percent encoding"
1237
+ });
1238
+ return;
1239
+ }
1240
+ if (decoded.split("/").some((segment) => segment === "..")) {
1241
+ ctx.addIssue({
1242
+ code: "custom",
1243
+ message: "must not contain parent traversal"
1244
+ });
1245
+ }
1246
+ });
1247
+ function webSessionPathWithinPrefix(pathname, pathPrefix) {
1248
+ if (pathPrefix.endsWith("/"))
1249
+ return pathname.startsWith(pathPrefix);
1250
+ return pathname === pathPrefix || pathname.startsWith(`${pathPrefix}/`);
1251
+ }
1252
+ var WEB_SESSION_CSRF_SELECTOR_PATTERN = /^(?=.)(?:[a-zA-Z][a-zA-Z0-9-]*)?(?:#[a-zA-Z_][\w-]*|\[[a-zA-Z_:][\w:.-]*(?:=(?:"[^"]*"|'[^']*'|[\w-]+))?\])*$/u;
1253
+ var WebSessionCsrfSelector = NonEmptyString.regex(WEB_SESSION_CSRF_SELECTOR_PATTERN, "must be a compound simple selector: optional tag, then #id or [attr=value] predicates on that one element (no combinators, classes, or pseudo-classes)");
1254
+ var WebSessionGrantRequest = z5.strictObject({
1255
+ origin: WebSessionOrigin,
1256
+ pathPrefixes: z5.array(WebSessionPathPrefix).min(1),
1257
+ csrf: z5.strictObject({
1258
+ sourceUrl: WebSessionCsrfSourceUrl,
1259
+ selector: WebSessionCsrfSelector,
1260
+ attribute: NonEmptyString,
1261
+ header: NonEmptyString,
1262
+ /** Optional form field the host also injects for legacy download POSTs. */
1263
+ bodyField: NonEmptyString.optional()
1264
+ }).optional()
1265
+ });
1266
+ var KnowledgeSearchGrantRequest = z5.strictObject({
1267
+ scope: z5.literal("own-schemes")
1268
+ });
1269
+ var KnownResourcesGrantRequest = z5.strictObject({
1270
+ scope: z5.literal("own-modules")
1271
+ });
1272
+ var PluginAgentDefinitionPermissionRule = z5.strictObject({
1273
+ effect: z5.enum(["allow", "deny", "ask"]),
1274
+ /** Restrict to one tool name. */
1275
+ tool: NonEmptyString.optional(),
1276
+ /** Restrict to one classified capability key. */
1277
+ ruleKey: NonEmptyString.optional(),
1278
+ /** Restrict to one URI scheme / owning service id. */
1279
+ scheme: NonEmptyString.optional(),
1280
+ /** Restrict to matching resources (a path glob, or a URI-shaped policy address). */
1281
+ resource: NonEmptyString.optional(),
1282
+ /** Restrict the rule to one permission mode; omit to apply in all modes. */
1283
+ mode: z5.enum(["default", "ask", "allow"]).optional()
1284
+ });
1285
+ var RETIRED_PERMISSION_ASK_KEYS = [
1286
+ "network",
1287
+ "process",
1288
+ "filesystem",
1289
+ "corpus",
1290
+ "paths",
1291
+ "workspace",
1292
+ "sessions",
1293
+ "automations",
1294
+ "hindsight",
1295
+ "agents",
1296
+ "dynamicContext",
1297
+ "appData",
1298
+ "toolInterception",
1299
+ "account",
1300
+ "device",
1301
+ "config",
1302
+ "workflows",
1303
+ "whiteboard"
1304
+ ];
1305
+ var RETIRED_WEB_SESSION_ROW_KEYS = ["operations"];
1306
+ function stripRetiredPermissionAsks(value) {
1307
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1308
+ return value;
1309
+ }
1310
+ const remaining = { ...value };
1311
+ for (const key of RETIRED_PERMISSION_ASK_KEYS)
1312
+ delete remaining[key];
1313
+ if (Array.isArray(remaining["webSession"])) {
1314
+ remaining["webSession"] = remaining["webSession"].map((row) => {
1315
+ if (typeof row !== "object" || row === null || Array.isArray(row)) {
1316
+ return row;
1317
+ }
1318
+ const rest = { ...row };
1319
+ for (const key of RETIRED_WEB_SESSION_ROW_KEYS)
1320
+ delete rest[key];
1321
+ return rest;
1322
+ });
1323
+ }
1324
+ return remaining;
1325
+ }
1326
+ function retiredPermissionAsksIn(rawManifest) {
1327
+ if (typeof rawManifest !== "object" || rawManifest === null)
1328
+ return [];
1329
+ const permissions = rawManifest["permissions"];
1330
+ if (typeof permissions !== "object" || permissions === null)
1331
+ return [];
1332
+ const byTarget = permissions["byTarget"];
1333
+ if (typeof byTarget !== "object" || byTarget === null)
1334
+ return [];
1335
+ const found = [];
1336
+ for (const [target, set] of Object.entries(byTarget)) {
1337
+ if (typeof set !== "object" || set === null || Array.isArray(set))
1338
+ continue;
1339
+ const record = set;
1340
+ const keys = RETIRED_PERMISSION_ASK_KEYS.filter((key) => key in record);
1341
+ const rows = record["webSession"];
1342
+ const webSessionOperations = Array.isArray(rows) && rows.some((row) => typeof row === "object" && row !== null && RETIRED_WEB_SESSION_ROW_KEYS.some((key) => key in row));
1343
+ if (keys.length === 0 && !webSessionOperations)
1344
+ continue;
1345
+ found.push({ target, keys, webSessionOperations });
1346
+ }
1347
+ return found;
1348
+ }
1349
+ var TargetPermissionWiring = z5.strictObject({
1350
+ auth: z5.array(AuthGrantRequest).optional(),
1351
+ storage: z5.array(StorageGrantRequest).optional(),
1352
+ events: z5.array(EventGrantRequest).optional(),
1353
+ endpoints: z5.array(EndpointGrantRequest).optional(),
1354
+ webSession: z5.array(WebSessionGrantRequest).optional(),
1355
+ knowledgeSearch: z5.array(KnowledgeSearchGrantRequest).optional(),
1356
+ knownResources: z5.array(KnownResourcesGrantRequest).optional()
1357
+ }).superRefine((permissions, ctx) => {
1358
+ const rows = permissions.webSession ?? [];
1359
+ for (const [index, row] of rows.entries()) {
1360
+ const sourceUrl = row.csrf?.sourceUrl;
1361
+ if (sourceUrl === void 0)
1362
+ continue;
1363
+ const source = new URL(sourceUrl);
1364
+ const covered = rows.some((candidate) => candidate.origin === source.origin && candidate.pathPrefixes.some((prefix) => webSessionPathWithinPrefix(source.pathname, prefix)));
1365
+ if (!covered) {
1366
+ ctx.addIssue({
1367
+ code: "custom",
1368
+ path: ["webSession", index, "csrf", "sourceUrl"],
1369
+ message: "must fall within a declared webSession row"
1370
+ });
1371
+ }
1372
+ }
1373
+ });
1374
+ var TargetPermissionSet = z5.preprocess(stripRetiredPermissionAsks, TargetPermissionWiring);
1375
+ var PluginPermissionManifest = z5.strictObject({
1376
+ byTarget: z5.strictObject({
1377
+ "kay.agent": TargetPermissionSet.optional(),
1378
+ "kay.app": TargetPermissionSet.optional(),
1379
+ "kay.ui": TargetPermissionSet.optional()
1380
+ })
1381
+ }).superRefine((manifest, ctx) => {
1382
+ for (const grant of manifest.byTarget["kay.ui"]?.auth ?? []) {
1383
+ if (grant.operations.includes("resolve")) {
1384
+ ctx.addIssue({
1385
+ code: "custom",
1386
+ path: ["byTarget", "kay.ui", "auth"],
1387
+ message: "kay.ui targets cannot request raw auth material resolution"
1388
+ });
1389
+ }
1390
+ }
1391
+ });
1392
+ var ResolvedAuthGrantRequest = AuthGrantRequestBase.extend({
1393
+ service: GlobalContributionId,
1394
+ schemes: z5.array(GlobalContributionId).min(1).optional(),
1395
+ // Resolution fills an absent binding with the narrowest bound, so a resolved
1396
+ // grant ALWAYS carries an explicit, non-broadening account selector — the
1397
+ // runtime never has to invent a default and fail open.
1398
+ accountSelectors: z5.array(AuthAccountSelector).min(1)
1399
+ }).superRefine(validateAuthGrantRequest);
1400
+ var ResolvedEndpointGrantRequest = EndpointGrantRequest.extend({
1401
+ endpoint: GlobalContributionId
1402
+ });
1403
+ var ResolvedTargetGrantSet = z5.strictObject({
1404
+ auth: z5.array(ResolvedAuthGrantRequest).optional(),
1405
+ storage: z5.array(StorageGrantRequest).optional(),
1406
+ events: z5.array(EventGrantRequest).optional(),
1407
+ endpoints: z5.array(ResolvedEndpointGrantRequest).optional(),
1408
+ webSession: z5.array(WebSessionGrantRequest).optional(),
1409
+ knowledgeSearch: z5.array(KnowledgeSearchGrantRequest).optional(),
1410
+ knownResources: z5.array(KnownResourcesGrantRequest).optional()
1411
+ });
1412
+ var ResolvedPluginGrants = z5.strictObject({
1413
+ byTarget: z5.strictObject({
1414
+ "kay.agent": ResolvedTargetGrantSet.optional(),
1415
+ "kay.app": ResolvedTargetGrantSet.optional(),
1416
+ "kay.ui": ResolvedTargetGrantSet.optional()
1417
+ })
1418
+ });
1419
+
1420
+ // packages/plugin-protocol/dist/authority.js
1421
+ var WhiteboardCapabilityScope = z6.enum(["own-types", "all-types"]);
1422
+ var WhiteboardCapabilityOperation = z6.enum([
1423
+ "discover",
1424
+ "read",
1425
+ "create",
1426
+ "update",
1427
+ "remove"
1428
+ ]);
1429
+ var WhiteboardCapabilityClaimV1 = z6.strictObject({
1430
+ target: z6.enum(["kay.agent", "kay.app"]),
1431
+ slug: z6.enum([
1432
+ "whiteboard.discover",
1433
+ "whiteboard.read",
1434
+ "whiteboard.create",
1435
+ "whiteboard.update",
1436
+ "whiteboard.remove"
1437
+ ]),
1438
+ envelope: z6.strictObject({ scope: WhiteboardCapabilityScope })
1439
+ }).superRefine((claim, ctx) => {
1440
+ if (claim.target === "kay.app" && claim.slug !== "whiteboard.discover" && claim.slug !== "whiteboard.read") {
1441
+ ctx.addIssue({
1442
+ code: "custom",
1443
+ path: ["slug"],
1444
+ message: "kay.app Whiteboard claims are read-only"
1445
+ });
1446
+ }
1447
+ });
1448
+ var PluginCapabilityClaimV1 = z6.discriminatedUnion("slug", [
1449
+ z6.strictObject({
1450
+ target: z6.literal("kay.app"),
1451
+ slug: z6.literal("sessions.run"),
1452
+ envelope: z6.strictObject({})
1453
+ }),
1454
+ z6.strictObject({
1455
+ target: z6.literal("kay.agent"),
1456
+ slug: z6.literal("network.connect"),
1457
+ envelope: z6.strictObject({ connect: NonEmptyString })
1458
+ }),
1459
+ z6.strictObject({
1460
+ target: z6.literal("kay.agent"),
1461
+ slug: z6.literal("web-session.request"),
1462
+ envelope: z6.strictObject({
1463
+ origin: NonEmptyString,
1464
+ pathPrefix: NonEmptyString
1465
+ })
1466
+ }),
1467
+ z6.strictObject({
1468
+ target: z6.literal("kay.agent"),
1469
+ slug: z6.literal("web-session.download"),
1470
+ envelope: z6.strictObject({
1471
+ origin: NonEmptyString,
1472
+ pathPrefix: NonEmptyString
1473
+ })
1474
+ }),
1475
+ z6.strictObject({
1476
+ target: z6.literal("kay.agent"),
1477
+ slug: z6.literal("filesystem.read"),
1478
+ envelope: z6.strictObject({ root: NonEmptyString })
1479
+ }),
1480
+ z6.strictObject({
1481
+ target: z6.literal("kay.agent"),
1482
+ slug: z6.literal("filesystem.write"),
1483
+ envelope: z6.strictObject({ root: NonEmptyString })
1484
+ }),
1485
+ z6.strictObject({
1486
+ target: z6.literal("kay.agent"),
1487
+ slug: z6.literal("process.spawn"),
1488
+ envelope: z6.strictObject({
1489
+ command: NonEmptyString,
1490
+ argvClass: NonEmptyString
1491
+ })
1492
+ }),
1493
+ z6.strictObject({
1494
+ target: z6.literal("kay.app"),
1495
+ slug: z6.literal("corpus.read"),
1496
+ envelope: z6.strictObject({})
1497
+ }),
1498
+ z6.strictObject({
1499
+ // Every plugin's recorded Jev signals, not only the caller's own: an empty envelope because
1500
+ // the grant itself is the visibility decision. Both first consumers (a session timeline, and
1501
+ // an agent loop turning signals into skills) read rows another plugin recorded.
1502
+ target: z6.literal("kay.app"),
1503
+ slug: z6.literal("signals.read"),
1504
+ envelope: z6.strictObject({})
1505
+ }),
1506
+ z6.strictObject({
1507
+ target: z6.literal("kay.app"),
1508
+ slug: z6.literal("workspace.command"),
1509
+ envelope: z6.strictObject({})
1510
+ }),
1511
+ z6.strictObject({
1512
+ target: z6.literal("kay.app"),
1513
+ slug: z6.literal("sessions.create"),
1514
+ envelope: z6.strictObject({})
1515
+ }),
1516
+ z6.strictObject({
1517
+ target: z6.literal("kay.app"),
1518
+ slug: z6.literal("sessions.update"),
1519
+ envelope: z6.strictObject({})
1520
+ }),
1521
+ z6.strictObject({
1522
+ target: z6.literal("kay.app"),
1523
+ slug: z6.literal("models.complete"),
1524
+ envelope: z6.strictObject({})
1525
+ }),
1526
+ z6.strictObject({
1527
+ // Held to `kay.app`, the daemon-lifetime tier, even though the same wrapper is exposed on
1528
+ // BOTH the `kay.app` and `kay.agent` plugin SDKs (and to host composition directly): a slug
1529
+ // pins exactly one target (see this file's module doc), so one shared claim is simpler and
1530
+ // more honest than a second, `kay.agent`-scoped sibling slug for the identical capability.
1531
+ // `kay.agent`'s `api.withJev` authorizes against this SAME claim through the calling
1532
+ // plugin's own pinned authority, which already spans both targets for every other capability
1533
+ // (`network.connect`/`tool-interception.register` are decided by the identical per-plugin
1534
+ // handle `kay.app`-target claims use).
1535
+ target: z6.literal("kay.app"),
1536
+ slug: z6.literal("jev.use"),
1537
+ envelope: z6.strictObject({})
1538
+ }),
1539
+ z6.strictObject({
1540
+ target: z6.literal("kay.app"),
1541
+ slug: z6.literal("automations.write"),
1542
+ envelope: z6.strictObject({})
1543
+ }),
1544
+ z6.strictObject({
1545
+ target: z6.literal("kay.app"),
1546
+ slug: z6.literal("automations.run"),
1547
+ envelope: z6.strictObject({})
1548
+ }),
1549
+ z6.strictObject({
1550
+ target: z6.literal("kay.app"),
1551
+ slug: z6.literal("workflows.define"),
1552
+ envelope: z6.strictObject({})
1553
+ }),
1554
+ z6.strictObject({
1555
+ target: z6.literal("kay.app"),
1556
+ slug: z6.literal("session-events.subscribe"),
1557
+ envelope: z6.strictObject({})
1558
+ }),
1559
+ z6.strictObject({
1560
+ target: z6.literal("kay.app"),
1561
+ slug: z6.literal("agents.run"),
1562
+ envelope: z6.strictObject({})
1563
+ }),
1564
+ z6.strictObject({
1565
+ target: z6.literal("kay.app"),
1566
+ slug: z6.literal("dynamic-context.register"),
1567
+ // The declared `contributes.contextContributors` localId, which doubles as the contributor's
1568
+ // section tag at the model boundary — so the row a human consents to names the section.
1569
+ envelope: z6.strictObject({ contributor: NonEmptyString })
1570
+ }),
1571
+ z6.strictObject({
1572
+ target: z6.literal("kay.app"),
1573
+ slug: z6.literal("app-data.register"),
1574
+ /** The declared `contributes.dataSources` localId. */
1575
+ envelope: z6.strictObject({ source: NonEmptyString })
1576
+ }),
1577
+ z6.strictObject({
1578
+ target: z6.literal("kay.app"),
1579
+ slug: z6.literal("account.assert"),
1580
+ envelope: z6.strictObject({})
1581
+ }),
1582
+ z6.strictObject({
1583
+ target: z6.literal("kay.app"),
1584
+ slug: z6.literal("account.update"),
1585
+ envelope: z6.strictObject({})
1586
+ }),
1587
+ z6.strictObject({
1588
+ target: z6.literal("kay.app"),
1589
+ slug: z6.literal("agents.permissions"),
1590
+ envelope: z6.strictObject({
1591
+ /** The definition's local id — WHICH agent these rules are authored onto. */
1592
+ agent: NonEmptyString,
1593
+ /** `definition.permissions`, in declaration order (order is semantic; first match wins). */
1594
+ rules: z6.array(PluginAgentDefinitionPermissionRule),
1595
+ /** `definition.permissionsFallback`, in declaration order. */
1596
+ fallback: z6.array(PluginAgentDefinitionPermissionRule)
1597
+ })
1598
+ }),
1599
+ z6.strictObject({
1600
+ target: z6.literal("kay.agent"),
1601
+ slug: z6.literal("tool-interception.register"),
1602
+ envelope: z6.strictObject({})
1603
+ }),
1604
+ z6.strictObject({
1605
+ target: z6.literal("plugin"),
1606
+ slug: z6.literal("config.declare-section"),
1607
+ /** The settings-document section key, e.g. `models` or `plugins.com.acme.thing`. */
1608
+ envelope: z6.strictObject({ section: NonEmptyString })
1609
+ }),
1610
+ z6.strictObject({
1611
+ target: z6.literal("plugin"),
1612
+ slug: z6.literal("auth.own-service"),
1613
+ /** The minted global service id this plugin would own, e.g. `google.account`. */
1614
+ envelope: z6.strictObject({ service: NonEmptyString })
1615
+ }),
1616
+ z6.strictObject({
1617
+ target: z6.literal("plugin"),
1618
+ slug: z6.literal("auth.shared-identity"),
1619
+ /** The foreign global service id whose identity this plugin would resolve against. */
1620
+ envelope: z6.strictObject({ service: NonEmptyString })
1621
+ }),
1622
+ z6.strictObject({
1623
+ target: z6.literal("plugin"),
1624
+ slug: z6.literal("cli.bind"),
1625
+ /** The binary basename a resolved credential would be injected into. */
1626
+ envelope: z6.strictObject({ binary: NonEmptyString })
1627
+ }),
1628
+ WhiteboardCapabilityClaimV1
1629
+ ]);
1630
+ var PLUGIN_CAPABILITY_SLUGS = [
1631
+ "sessions.run",
1632
+ "network.connect",
1633
+ "web-session.request",
1634
+ "web-session.download",
1635
+ "filesystem.read",
1636
+ "filesystem.write",
1637
+ "process.spawn",
1638
+ "corpus.read",
1639
+ "signals.read",
1640
+ "workspace.command",
1641
+ "sessions.create",
1642
+ "sessions.update",
1643
+ "models.complete",
1644
+ "jev.use",
1645
+ "automations.write",
1646
+ "automations.run",
1647
+ "workflows.define",
1648
+ "session-events.subscribe",
1649
+ "agents.run",
1650
+ "dynamic-context.register",
1651
+ "app-data.register",
1652
+ "account.assert",
1653
+ "account.update",
1654
+ "agents.permissions",
1655
+ "tool-interception.register",
1656
+ "config.declare-section",
1657
+ "auth.own-service",
1658
+ "auth.shared-identity",
1659
+ "cli.bind",
1660
+ "whiteboard.discover",
1661
+ "whiteboard.read",
1662
+ "whiteboard.create",
1663
+ "whiteboard.update",
1664
+ "whiteboard.remove"
1665
+ ];
1666
+ var PluginCapabilityRefusalLayer = z6.enum(["grant", "binding"]);
1667
+ var PluginCapabilityRefusalCode = z6.enum([
1668
+ "not-granted",
1669
+ "grant-read-failed",
1670
+ "binding-missing",
1671
+ "argv-outside-class"
1672
+ ]);
1673
+ var PluginCapabilityRefusalV1 = z6.strictObject({
1674
+ claim: PluginCapabilityClaimV1,
1675
+ layer: PluginCapabilityRefusalLayer,
1676
+ code: PluginCapabilityRefusalCode,
1677
+ message: NonEmptyString,
1678
+ artifactGenerationId: ArtifactGenerationId.optional()
1679
+ });
1680
+ var PluginCapabilityDecisionV1 = z6.discriminatedUnion("kind", [
1681
+ z6.strictObject({
1682
+ kind: z6.literal("allowed"),
1683
+ claim: PluginCapabilityClaimV1,
1684
+ artifactGenerationId: ArtifactGenerationId.optional()
1685
+ }),
1686
+ z6.strictObject({
1687
+ kind: z6.literal("refused"),
1688
+ refusal: PluginCapabilityRefusalV1
1689
+ })
1690
+ ]);
1691
+ function normalizeNetworkConnectHost(value) {
1692
+ const trimmed = value.trim();
1693
+ if (trimmed.length === 0)
1694
+ return void 0;
1695
+ if (/[/\\?#@:[\]*%\s]/u.test(trimmed))
1696
+ return void 0;
1697
+ const withoutRootLabel = trimmed.endsWith(".") ? trimmed.slice(0, -1) : trimmed;
1698
+ if (withoutRootLabel.length === 0)
1699
+ return void 0;
1700
+ let hostname;
1701
+ try {
1702
+ const url = new URL(`https://${withoutRootLabel}`);
1703
+ if (url.port !== "" || url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
1704
+ return void 0;
1705
+ }
1706
+ hostname = url.hostname;
1707
+ } catch {
1708
+ return void 0;
1709
+ }
1710
+ if (hostname.length === 0 || hostname.split(".").some((label) => label.length === 0)) {
1711
+ return void 0;
1712
+ }
1713
+ return hostname;
1714
+ }
1715
+ function canonicalAgentPermissionRules(rules) {
1716
+ return JSON.stringify(rules.map((rule) => {
1717
+ const canonical = {};
1718
+ for (const field of AGENT_PERMISSION_RULE_FIELDS) {
1719
+ const value = rule[field];
1720
+ if (value !== void 0)
1721
+ canonical[field] = value;
1722
+ }
1723
+ return canonical;
1724
+ }));
1725
+ }
1726
+ var AGENT_PERMISSION_RULE_FIELDS = [
1727
+ "effect",
1728
+ "tool",
1729
+ "ruleKey",
1730
+ "scheme",
1731
+ "resource",
1732
+ "mode"
1733
+ ];
1734
+ function sameClaim(a, b) {
1735
+ if (a.slug !== b.slug || a.target !== b.target)
1736
+ return false;
1737
+ switch (a.slug) {
1738
+ // Every empty-envelope claim: the slug IS the whole claim, and the slugs already matched.
1739
+ case "sessions.run":
1740
+ case "sessions.create":
1741
+ case "sessions.update":
1742
+ case "corpus.read":
1743
+ case "signals.read":
1744
+ case "workspace.command":
1745
+ case "models.complete":
1746
+ case "jev.use":
1747
+ case "automations.write":
1748
+ case "automations.run":
1749
+ case "workflows.define":
1750
+ case "session-events.subscribe":
1751
+ case "agents.run":
1752
+ case "account.assert":
1753
+ case "account.update":
1754
+ case "tool-interception.register":
1755
+ return true;
1756
+ case "network.connect":
1757
+ return b.slug === "network.connect" && a.envelope.connect === b.envelope.connect;
1758
+ case "web-session.request":
1759
+ case "web-session.download":
1760
+ return (b.slug === "web-session.request" || b.slug === "web-session.download") && a.envelope.origin === b.envelope.origin && a.envelope.pathPrefix === b.envelope.pathPrefix;
1761
+ case "filesystem.read":
1762
+ case "filesystem.write":
1763
+ return (b.slug === "filesystem.read" || b.slug === "filesystem.write") && a.envelope.root === b.envelope.root;
1764
+ case "process.spawn":
1765
+ return b.slug === "process.spawn" && a.envelope.command === b.envelope.command && a.envelope.argvClass === b.envelope.argvClass;
1766
+ case "dynamic-context.register":
1767
+ return b.slug === "dynamic-context.register" && a.envelope.contributor === b.envelope.contributor;
1768
+ case "app-data.register":
1769
+ return b.slug === "app-data.register" && a.envelope.source === b.envelope.source;
1770
+ case "agents.permissions":
1771
+ return b.slug === "agents.permissions" && a.envelope.agent === b.envelope.agent && canonicalAgentPermissionRules(a.envelope.rules) === canonicalAgentPermissionRules(b.envelope.rules) && canonicalAgentPermissionRules(a.envelope.fallback) === canonicalAgentPermissionRules(b.envelope.fallback);
1772
+ case "config.declare-section":
1773
+ return b.slug === "config.declare-section" && a.envelope.section === b.envelope.section;
1774
+ case "auth.own-service":
1775
+ case "auth.shared-identity":
1776
+ return (b.slug === "auth.own-service" || b.slug === "auth.shared-identity") && a.envelope.service === b.envelope.service;
1777
+ case "cli.bind":
1778
+ return b.slug === "cli.bind" && a.envelope.binary === b.envelope.binary;
1779
+ case "whiteboard.discover":
1780
+ case "whiteboard.read":
1781
+ case "whiteboard.create":
1782
+ case "whiteboard.update":
1783
+ case "whiteboard.remove":
1784
+ return b.slug === a.slug && "scope" in b.envelope && a.envelope.scope === b.envelope.scope;
1785
+ }
1786
+ }
1787
+ function hasClaim(claims, claim) {
1788
+ return claims.some((candidate) => sameClaim(candidate, claim));
1789
+ }
1790
+ function compareClaims(a, b) {
1791
+ if (a.target !== b.target)
1792
+ return a.target < b.target ? -1 : 1;
1793
+ if (a.slug !== b.slug)
1794
+ return a.slug < b.slug ? -1 : 1;
1795
+ for (const [left, right] of envelopePairs(a, b)) {
1796
+ if (left !== right)
1797
+ return left < right ? -1 : 1;
1798
+ }
1799
+ return 0;
1800
+ }
1801
+ function envelopePairs(a, b) {
1802
+ switch (a.slug) {
1803
+ case "sessions.run":
1804
+ case "sessions.create":
1805
+ case "sessions.update":
1806
+ case "corpus.read":
1807
+ case "signals.read":
1808
+ case "workspace.command":
1809
+ case "models.complete":
1810
+ case "jev.use":
1811
+ case "automations.write":
1812
+ case "automations.run":
1813
+ case "workflows.define":
1814
+ case "session-events.subscribe":
1815
+ case "agents.run":
1816
+ case "account.assert":
1817
+ case "account.update":
1818
+ case "tool-interception.register":
1819
+ return [];
1820
+ case "network.connect":
1821
+ return b.slug === "network.connect" ? [[a.envelope.connect, b.envelope.connect]] : [];
1822
+ case "web-session.request":
1823
+ case "web-session.download":
1824
+ return b.slug === "web-session.request" || b.slug === "web-session.download" ? [
1825
+ [a.envelope.origin, b.envelope.origin],
1826
+ [a.envelope.pathPrefix, b.envelope.pathPrefix]
1827
+ ] : [];
1828
+ case "filesystem.read":
1829
+ case "filesystem.write":
1830
+ return b.slug === "filesystem.read" || b.slug === "filesystem.write" ? [[a.envelope.root, b.envelope.root]] : [];
1831
+ case "process.spawn":
1832
+ return b.slug === "process.spawn" ? [
1833
+ [a.envelope.command, b.envelope.command],
1834
+ [a.envelope.argvClass, b.envelope.argvClass]
1835
+ ] : [];
1836
+ case "dynamic-context.register":
1837
+ return b.slug === "dynamic-context.register" ? [[a.envelope.contributor, b.envelope.contributor]] : [];
1838
+ case "app-data.register":
1839
+ return b.slug === "app-data.register" ? [[a.envelope.source, b.envelope.source]] : [];
1840
+ case "agents.permissions":
1841
+ return b.slug === "agents.permissions" ? [
1842
+ [a.envelope.agent, b.envelope.agent],
1843
+ [
1844
+ canonicalAgentPermissionRules(a.envelope.rules),
1845
+ canonicalAgentPermissionRules(b.envelope.rules)
1846
+ ],
1847
+ [
1848
+ canonicalAgentPermissionRules(a.envelope.fallback),
1849
+ canonicalAgentPermissionRules(b.envelope.fallback)
1850
+ ]
1851
+ ] : [];
1852
+ case "config.declare-section":
1853
+ return b.slug === "config.declare-section" ? [[a.envelope.section, b.envelope.section]] : [];
1854
+ case "auth.own-service":
1855
+ case "auth.shared-identity":
1856
+ return b.slug === "auth.own-service" || b.slug === "auth.shared-identity" ? [[a.envelope.service, b.envelope.service]] : [];
1857
+ case "cli.bind":
1858
+ return b.slug === "cli.bind" ? [[a.envelope.binary, b.envelope.binary]] : [];
1859
+ case "whiteboard.discover":
1860
+ case "whiteboard.read":
1861
+ case "whiteboard.create":
1862
+ case "whiteboard.update":
1863
+ case "whiteboard.remove":
1864
+ return b.slug === a.slug && "scope" in b.envelope ? [[a.envelope.scope, b.envelope.scope]] : [];
1865
+ }
1866
+ }
1867
+ function whiteboardCapabilityClaim(operation, scope, target = "kay.agent") {
1868
+ if (target === "kay.app" && operation !== "discover" && operation !== "read") {
1869
+ throw new TypeError("kay.app Whiteboard claims are read-only");
1870
+ }
1871
+ return {
1872
+ target,
1873
+ slug: `whiteboard.${operation}`,
1874
+ envelope: { scope }
1875
+ };
1876
+ }
1877
+ function dedupeClaims(claims) {
1878
+ const unique = [];
1879
+ for (const claim of claims) {
1880
+ if (!hasClaim(unique, claim))
1881
+ unique.push(claim);
1882
+ }
1883
+ return unique;
1884
+ }
1885
+
1886
+ // packages/plugin-protocol/dist/semver.js
1887
+ var SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
1888
+ function parseSemver(value) {
1889
+ const match = SEMVER_PATTERN.exec(value);
1890
+ if (!match) {
1891
+ return void 0;
1892
+ }
1893
+ const [, major, minor, patch, prerelease, build] = match;
1894
+ return {
1895
+ major: Number(major),
1896
+ minor: Number(minor),
1897
+ patch: Number(patch),
1898
+ prerelease: prerelease === void 0 ? [] : prerelease.split(".").map((identifier) => /^\d+$/.test(identifier) ? Number(identifier) : identifier),
1899
+ build
1900
+ };
1901
+ }
1902
+ function compareSemver(a, b) {
1903
+ const parsedA = parseSemver(a);
1904
+ const parsedB = parseSemver(b);
1905
+ if (!parsedA || !parsedB) {
1906
+ return void 0;
1907
+ }
1908
+ if (parsedA.major !== parsedB.major) {
1909
+ return parsedA.major - parsedB.major;
1910
+ }
1911
+ if (parsedA.minor !== parsedB.minor) {
1912
+ return parsedA.minor - parsedB.minor;
1913
+ }
1914
+ if (parsedA.patch !== parsedB.patch) {
1915
+ return parsedA.patch - parsedB.patch;
1916
+ }
1917
+ return comparePrerelease(parsedA.prerelease, parsedB.prerelease);
1918
+ }
1919
+ function comparePrerelease(a, b) {
1920
+ if (a.length === 0 && b.length === 0) {
1921
+ return 0;
1922
+ }
1923
+ if (a.length === 0) {
1924
+ return 1;
1925
+ }
1926
+ if (b.length === 0) {
1927
+ return -1;
1928
+ }
1929
+ const length = Math.max(a.length, b.length);
1930
+ for (let i = 0; i < length; i++) {
1931
+ if (i >= a.length) {
1932
+ return -1;
1933
+ }
1934
+ if (i >= b.length) {
1935
+ return 1;
1936
+ }
1937
+ const identifierA = a[i];
1938
+ const identifierB = b[i];
1939
+ const numericA = typeof identifierA === "number";
1940
+ const numericB = typeof identifierB === "number";
1941
+ if (numericA && numericB) {
1942
+ if (identifierA !== identifierB) {
1943
+ return identifierA - identifierB;
1944
+ }
1945
+ continue;
1946
+ }
1947
+ if (numericA !== numericB) {
1948
+ return numericA ? -1 : 1;
1949
+ }
1950
+ const stringA = identifierA;
1951
+ const stringB = identifierB;
1952
+ if (stringA !== stringB) {
1953
+ return stringA < stringB ? -1 : 1;
1954
+ }
1955
+ }
1956
+ return 0;
1957
+ }
1958
+
1959
+ // packages/plugin-protocol/dist/semver-range.js
1960
+ var COMPARATOR_HEAD = /^\s*(\^|~|>=|<=|>|<|=)?\s*([0-9A-Za-z.+-]+)\s*/;
1961
+ function parseComparators(alternative) {
1962
+ const comparators = [];
1963
+ let rest = alternative;
1964
+ while (rest.trim().length > 0) {
1965
+ const match = COMPARATOR_HEAD.exec(rest);
1966
+ const rawVersion = match?.[2];
1967
+ if (match === null || rawVersion === void 0)
1968
+ return void 0;
1969
+ const parsed = parseSemver(rawVersion);
1970
+ if (parsed === void 0)
1971
+ return void 0;
1972
+ comparators.push({
1973
+ operator: match[1] ?? "=",
1974
+ version: rawVersion,
1975
+ parsed
1976
+ });
1977
+ rest = rest.slice(match[0].length);
1978
+ }
1979
+ return comparators.length === 0 ? void 0 : comparators;
1980
+ }
1981
+ function rangeBounds(comparator) {
1982
+ const { major, minor, patch } = comparator.parsed;
1983
+ if (comparator.operator === "~") {
1984
+ return {
1985
+ lower: comparator.version,
1986
+ upperExclusive: `${String(major)}.${String(minor + 1)}.0-0`
1987
+ };
1988
+ }
1989
+ if (major !== 0) {
1990
+ return {
1991
+ lower: comparator.version,
1992
+ upperExclusive: `${String(major + 1)}.0.0-0`
1993
+ };
1994
+ }
1995
+ if (minor !== 0) {
1996
+ return {
1997
+ lower: comparator.version,
1998
+ upperExclusive: `0.${String(minor + 1)}.0-0`
1999
+ };
2000
+ }
2001
+ return {
2002
+ lower: comparator.version,
2003
+ upperExclusive: `0.0.${String(patch + 1)}-0`
2004
+ };
2005
+ }
2006
+ function satisfiesComparator(version, comparator) {
2007
+ const cmp = compareSemver(version, comparator.version);
2008
+ if (cmp === void 0)
2009
+ return false;
2010
+ switch (comparator.operator) {
2011
+ case "=":
2012
+ return cmp === 0;
2013
+ case ">":
2014
+ return cmp > 0;
2015
+ case ">=":
2016
+ return cmp >= 0;
2017
+ case "<":
2018
+ return cmp < 0;
2019
+ case "<=":
2020
+ return cmp <= 0;
2021
+ case "^":
2022
+ case "~": {
2023
+ const { lower, upperExclusive } = rangeBounds(comparator);
2024
+ const atLeast = compareSemver(version, lower);
2025
+ const below = compareSemver(version, upperExclusive);
2026
+ return atLeast !== void 0 && below !== void 0 && atLeast >= 0 && below < 0;
2027
+ }
2028
+ }
2029
+ }
2030
+ function prereleaseAdmitted(parsedVersion, comparators) {
2031
+ if (parsedVersion.prerelease.length === 0)
2032
+ return true;
2033
+ return comparators.some((comparator) => comparator.parsed.prerelease.length > 0 && comparator.parsed.major === parsedVersion.major && comparator.parsed.minor === parsedVersion.minor && comparator.parsed.patch === parsedVersion.patch);
2034
+ }
2035
+ function satisfiesSemverRange(version, range) {
2036
+ const parsedVersion = parseSemver(version);
2037
+ if (parsedVersion === void 0)
2038
+ return void 0;
2039
+ const alternatives = range.split("||").map((part) => part.trim());
2040
+ if (alternatives.some((alternative) => alternative.length === 0)) {
2041
+ return void 0;
2042
+ }
2043
+ let satisfied = false;
2044
+ for (const alternative of alternatives) {
2045
+ const comparators = parseComparators(alternative);
2046
+ if (comparators === void 0)
2047
+ return void 0;
2048
+ if (prereleaseAdmitted(parsedVersion, comparators) && comparators.every((comparator) => satisfiesComparator(version, comparator))) {
2049
+ satisfied = true;
2050
+ }
2051
+ }
2052
+ return satisfied;
2053
+ }
2054
+ function isSupportedSemverRange(range) {
2055
+ return satisfiesSemverRange("0.0.0", range) !== void 0;
2056
+ }
2057
+
2058
+ // packages/plugin-protocol/dist/compatibility.js
2059
+ function isCompatible(report) {
2060
+ return report.refusal === void 0;
2061
+ }
2062
+ function checkKayPluginHostCompatibility(compatibility, host) {
2063
+ const advisories = [];
2064
+ const declaredProtocols = compatibility?.supportedProtocols;
2065
+ if (declaredProtocols !== void 0) {
2066
+ if (!declaredProtocols.includes(host.protocolVersion)) {
2067
+ const declared = declaredProtocols.length === 0 ? "no protocol versions" : declaredProtocols.join(", ");
2068
+ return {
2069
+ refusal: {
2070
+ code: "unsupported-protocol",
2071
+ message: `declares support for ${declared} but this Kay speaks ${host.protocolVersion}`
2072
+ },
2073
+ advisories
2074
+ };
2075
+ }
2076
+ }
2077
+ const minKayVersion = compatibility?.minKayVersion;
2078
+ if (minKayVersion !== void 0) {
2079
+ if (parseSemver(minKayVersion) === void 0) {
2080
+ advisories.push({
2081
+ code: "min-kay-version-unparseable",
2082
+ message: `declares a minimum Kay version of "${minKayVersion}", which is not a version Kay can compare`
2083
+ });
2084
+ } else {
2085
+ const order = compareSemver(host.appVersion, minKayVersion);
2086
+ if (order === void 0) {
2087
+ advisories.push({
2088
+ code: "min-kay-version-unparseable",
2089
+ message: `asks for Kay ${minKayVersion} or newer, which cannot be checked against this Kay's version "${host.appVersion}"`
2090
+ });
2091
+ } else if (order < 0) {
2092
+ advisories.push({
2093
+ code: "min-kay-version-unmet",
2094
+ message: `asks for Kay ${minKayVersion} or newer; this is ${host.appVersion}`
2095
+ });
2096
+ }
2097
+ }
2098
+ }
2099
+ const range = compatibility?.sdk;
2100
+ if (range === void 0) {
2101
+ if (host.bundled !== true) {
2102
+ advisories.push({
2103
+ code: "sdk-range-undeclared",
2104
+ message: `declares no compatibility.sdk range; this Kay ships plugin SDK ${host.sdkVersion}`
2105
+ });
2106
+ }
2107
+ return { refusal: void 0, advisories };
2108
+ }
2109
+ if (parseSemver(host.sdkVersion) === void 0) {
2110
+ return {
2111
+ refusal: {
2112
+ code: "host-sdk-version-invalid",
2113
+ message: `cannot be checked: this Kay reports plugin SDK "${host.sdkVersion}", which is not valid semver`
2114
+ },
2115
+ advisories
2116
+ };
2117
+ }
2118
+ if (!isSupportedSemverRange(range)) {
2119
+ return {
2120
+ refusal: {
2121
+ code: "sdk-range-unsupported",
2122
+ message: `declares an SDK range of "${range}", which Kay cannot interpret (use a caret, tilde, or comparator range such as ^${host.sdkVersion})`
2123
+ },
2124
+ advisories
2125
+ };
2126
+ }
2127
+ if (satisfiesSemverRange(host.sdkVersion, range) !== true) {
2128
+ return {
2129
+ refusal: {
2130
+ code: "sdk-range-unsatisfied",
2131
+ message: `needs plugin SDK ${range}, this Kay ships ${host.sdkVersion}`
2132
+ },
2133
+ advisories
2134
+ };
2135
+ }
2136
+ return { refusal: void 0, advisories };
2137
+ }
2138
+
2139
+ // packages/plugin-protocol/dist/contributions.js
2140
+ import { z as z7 } from "zod";
2141
+ var OptionalDisplayName = NonEmptyString.optional();
2142
+ var UriFilesystemBacking = z7.discriminatedUnion("root", [
2143
+ z7.strictObject({ root: z7.literal("dataDir") }),
2144
+ z7.strictObject({
2145
+ root: z7.literal("filesystem"),
2146
+ selector: NonEmptyString
2147
+ })
2148
+ ]);
2149
+ var ToolContribution = z7.strictObject({
2150
+ localId: LocalContributionId,
2151
+ toolName: PluginToolName,
2152
+ displayName: OptionalDisplayName,
2153
+ description: NonEmptyString.optional()
2154
+ });
2155
+ var ConverterContribution = z7.strictObject({
2156
+ localId: LocalContributionId,
2157
+ displayName: OptionalDisplayName,
2158
+ description: NonEmptyString.optional(),
2159
+ /** MIME types this converter claims — contract data for introspection and
2160
+ * collision checks; runtime match behavior stays the converter's `matches()`. */
2161
+ mimeTypes: z7.array(MimeTypeString).min(1),
2162
+ /** Extension fallbacks (no dot) used when a probe has no usable MIME type. */
2163
+ extensions: z7.array(FileExtensionToken).min(1).optional()
2164
+ });
2165
+ var UriMutationVerbs = z7.strictObject({
2166
+ writable: z7.boolean().optional(),
2167
+ editable: z7.boolean().optional(),
2168
+ deletable: z7.boolean().optional()
2169
+ });
2170
+ var UriModuleContributionBase = z7.strictObject({
2171
+ localId: LocalContributionId,
2172
+ schemes: z7.array(UriScheme).min(1),
2173
+ displayName: OptionalDisplayName,
2174
+ // The plugin only requests a projection. The daemon binds the effective root
2175
+ // and retains access control at finalization.
2176
+ filesystemBacking: UriFilesystemBacking.optional(),
2177
+ /** Host-native mutation verbs requested for an external filesystem projection. */
2178
+ verbs: UriMutationVerbs.optional(),
2179
+ // A local ref (no dot) must name an auth service declared in this same
2180
+ // manifest; a qualified ref (dotted, e.g. `google.account`) names another
2181
+ // plugin's already-minted global id and is recorded, not resolved, here (see
2182
+ // `consumesAuthServices` on the resolved contract).
2183
+ authService: AuthServiceReference.optional(),
2184
+ // Opt this module's traffic into the daemon's known-resources ledger: the
2185
+ // HOST records the canonical URIs (plus title/summary/mime facts) that flow
2186
+ // through the module's successful read/list/search resolutions, filtered by
2187
+ // the module's own `observes` predicate, and marks rows gone on a principled
2188
+ // not-found. Recording is host behavior consented to here, not a capability
2189
+ // handed to the plugin — reading the rows back is the separate
2190
+ // `knownResources` permission grant (`api.knownResources`). Present-means-on
2191
+ // (`true` or absent; `false` is not a state). Decision record:
2192
+ // `lab/user-plugins/decisions/014-known-resources-ledger.md`.
2193
+ knownResources: z7.literal(true).optional()
2194
+ });
2195
+ var UriModuleContribution = UriModuleContributionBase.superRefine((module, ctx) => {
2196
+ if (module.verbs !== void 0 && module.filesystemBacking?.root !== "filesystem") {
2197
+ ctx.addIssue({
2198
+ code: "custom",
2199
+ path: ["verbs"],
2200
+ message: "URI mutation verbs require external filesystem backing (root: 'filesystem')."
2201
+ });
2202
+ }
2203
+ });
2204
+ var AppUriModuleContribution = UriModuleContributionBase.omit({
2205
+ authService: true,
2206
+ filesystemBacking: true,
2207
+ verbs: true,
2208
+ knownResources: true
2209
+ });
2210
+ var AppDataSourceContribution = z7.strictObject({
2211
+ localId: LocalContributionId,
2212
+ scope: z7.enum(["runtime", "account", "session"]),
2213
+ snapshot: z7.strictObject({
2214
+ version: z7.number().int().positive(),
2215
+ schema: JsonSchema
2216
+ }),
2217
+ change: z7.strictObject({
2218
+ version: z7.number().int().positive(),
2219
+ schema: JsonSchema,
2220
+ /** Review-only claim; source schemas and behavioral tests enforce it because the daemon treats changes as opaque. */
2221
+ semantics: z7.discriminatedUnion("kind", [
2222
+ z7.strictObject({ kind: z7.literal("incremental") }),
2223
+ z7.strictObject({
2224
+ kind: z7.literal("snapshot-replacement"),
2225
+ maxEncodedBytes: z7.number().int().positive(),
2226
+ rationale: z7.string().trim().min(1).max(1024)
2227
+ })
2228
+ ]).optional()
2229
+ }),
2230
+ read: AppDataSourceRead.optional()
2231
+ }).superRefine((source, ctx) => {
2232
+ if (source.read !== void 0 && source.scope !== "session") {
2233
+ ctx.addIssue({
2234
+ code: "custom",
2235
+ path: ["read"],
2236
+ message: "App-data read operations require session scope."
2237
+ });
2238
+ }
2239
+ });
2240
+ var WhiteboardTypeStorage = z7.discriminatedUnion("kind", [
2241
+ z7.strictObject({ kind: z7.literal("json") }),
2242
+ z7.strictObject({ kind: z7.literal("payload") }),
2243
+ z7.strictObject({
2244
+ kind: z7.literal("markdown"),
2245
+ bodyField: NonEmptyString
2246
+ })
2247
+ ]);
2248
+ var WhiteboardTypeContribution = z7.strictObject({
2249
+ localId: LocalContributionId,
2250
+ displayName: NonEmptyString,
2251
+ schemaVersion: z7.number().int().positive(),
2252
+ storage: WhiteboardTypeStorage,
2253
+ valueSchema: JsonSchema,
2254
+ presentation: z7.strictObject({
2255
+ app: LocalContributionId
2256
+ }).optional()
2257
+ });
2258
+ var AppDataMaterializerContribution = z7.strictObject({
2259
+ source: LocalContributionId
2260
+ });
2261
+ var ArtifactUriModuleContribution = z7.strictObject({
2262
+ localId: LocalContributionId,
2263
+ /** The single `scheme://` this module owns; also the on-disk artifact kind. */
2264
+ scheme: UriScheme,
2265
+ /**
2266
+ * Opt in to exact-text `edit` support (in addition to read/write). Default false — a
2267
+ * write-only artifact scheme (agent replaces the whole document each time). Mirrors
2268
+ * `CreateArtifactUriModuleOptions.editable`.
2269
+ *
2270
+ * No `displayName`: unlike a plugin-implemented `uriModule` (whose `activate()` code writes its
2271
+ * own description/agentHint), an artifact-backed module ships no plugin code, and the host
2272
+ * composes its description from the plugin's own display name + scheme — a per-scheme label
2273
+ * would have no path to matter, so it is intentionally omitted rather than accepted-and-ignored.
2274
+ */
2275
+ editable: z7.boolean().optional(),
2276
+ /** Opt in to `list` over the kind's stored names. Default false. Mirrors
2277
+ * `CreateArtifactUriModuleOptions.listable`. */
2278
+ listable: z7.boolean().optional(),
2279
+ /**
2280
+ * Opt in to `delete` for same-session entries. Default false. Deletion
2281
+ * unlinks the stored file — no trash tier; a kind that wants recoverable
2282
+ * deletion is not artifact-backed. Mirrors
2283
+ * `CreateArtifactUriModuleOptions.deletable`.
2284
+ */
2285
+ deletable: z7.boolean().optional(),
2286
+ /**
2287
+ * Opt in to the `{ content, mode: "append" }` write envelope, which extends
2288
+ * stored text instead of replacing it (concurrent-appender-safe: the host
2289
+ * serializes appends per file). Default false. Payloads without a `mode` key
2290
+ * keep their verbatim round-trip. Mirrors
2291
+ * `CreateArtifactUriModuleOptions.appendable`.
2292
+ */
2293
+ appendable: z7.boolean().optional(),
2294
+ /**
2295
+ * Which sessions share one storage namespace. Default `"session"` — the existing
2296
+ * per-session behavior (each session gets its own kind directory; other sessions'
2297
+ * entries are readable via the qualified `<scheme>://<sessionId>/<name>` form).
2298
+ * `"family"` keys storage by the session FAMILY's root instead — the session and every
2299
+ * subagent in its spawn tree mount one namespace — and makes addressing session-local
2300
+ * (the qualified cross-session form is refused on every verb). The plugin only NAMES
2301
+ * the scope; the host derives the family root from provenance it already holds, so
2302
+ * family identity never crosses the SDK boundary.
2303
+ */
2304
+ storageScope: z7.enum(["session", "family"]).optional()
2305
+ });
2306
+ var CliBindingContribution = z7.strictObject({
2307
+ localId: LocalContributionId,
2308
+ binary: CliBinaryName,
2309
+ aliases: z7.array(CliBinaryName).optional(),
2310
+ /** Where the binary comes from. Omitted ⇒ `"vendored"` (decision 007). */
2311
+ provider: z7.enum(["vendored", "path"]).optional(),
2312
+ // A local ref (no dot) must name an auth service declared in this same
2313
+ // manifest; a qualified ref (dotted, e.g. `google.account`) names another
2314
+ // plugin's already-minted global id and is recorded, not resolved, here (see
2315
+ // `consumesAuthServices` on the resolved contract) — same discriminator as
2316
+ // `UriModuleContribution.authService`.
2317
+ authService: AuthServiceReference,
2318
+ /**
2319
+ * Env var the resolved credential is injected under. Required in practice for a
2320
+ * `vendored` binding (a binding that injects nothing has no reason to name one);
2321
+ * MUST be absent for a `path` binding, which can never receive a token.
2322
+ */
2323
+ authEnvVar: CliAuthEnvVarName.optional(),
2324
+ readOnly: z7.array(NonEmptyString),
2325
+ write: z7.array(NonEmptyString)
2326
+ });
2327
+ var GitHostContribution = z7.strictObject({
2328
+ host: GitHostName,
2329
+ authService: LocalContributionId,
2330
+ basicAuthUser: GitBasicAuthUser
2331
+ });
2332
+ var ResourceContribution = z7.strictObject({
2333
+ localId: LocalContributionId,
2334
+ displayName: OptionalDisplayName,
2335
+ description: NonEmptyString.optional()
2336
+ });
2337
+ var SkillContribution = z7.strictObject({
2338
+ localId: LocalContributionId,
2339
+ displayName: OptionalDisplayName,
2340
+ /** Model-visible one-liner: when to reach for this skill. */
2341
+ description: NonEmptyString,
2342
+ /** The SKILL.md-style file inside the package carrying the full instructions. */
2343
+ path: PackageAssetPath
2344
+ });
2345
+ var PluginSkillSourceDeclaration = z7.discriminatedUnion("channel", [
2346
+ z7.strictObject({
2347
+ channel: z7.literal("installer"),
2348
+ localId: LocalContributionId,
2349
+ displayName: OptionalDisplayName,
2350
+ /** The package-runner executable, e.g. `npx` — checked against the daemon's allowlist. */
2351
+ command: NonEmptyString,
2352
+ /** Its argv, e.g. `["skills", "add", "clerk/skills"]`. */
2353
+ args: z7.array(NonEmptyString)
2354
+ }),
2355
+ z7.strictObject({
2356
+ channel: z7.literal("git-clone"),
2357
+ localId: LocalContributionId,
2358
+ displayName: OptionalDisplayName,
2359
+ /** The repository locator — canonicalized and hygiene-checked by the daemon. */
2360
+ locator: NonEmptyString,
2361
+ /** Optional branch/tag to clone; omitted ⇒ the remote's default branch. */
2362
+ ref: NonEmptyString.optional(),
2363
+ /** Optional subdirectory within the repo to discover skills under. */
2364
+ subdir: NonEmptyString.optional()
2365
+ })
2366
+ ]);
2367
+ var SearchProviderContribution = z7.strictObject({
2368
+ localId: LocalContributionId,
2369
+ displayName: OptionalDisplayName,
2370
+ schemes: z7.array(UriScheme).optional()
2371
+ });
2372
+ var EntityResolverContribution = z7.strictObject({
2373
+ localId: LocalContributionId,
2374
+ displayName: OptionalDisplayName,
2375
+ /** The `kay.agent` URI module (this manifest's own `contributes.uriModules[].localId`) whose
2376
+ * `search` verb answers `@` queries for `entityTypes`. */
2377
+ uriModule: LocalContributionId,
2378
+ /** `person` | `channel` | `repo` | `pull-request` | ... -- drives avatar-first vs glyph-first row
2379
+ * anatomy (N10-R2) and lets the renderer request a narrower `entityTypes` filter. */
2380
+ entityTypes: z7.array(NonEmptyString).min(1),
2381
+ /** The URI namespace/route `search()` is called against, e.g. `slack://directory` or
2382
+ * `github://search/repositories` -- the resolver's fixed search scope; the live user query is
2383
+ * the `search()` call's `query` param, never concatenated into this string. */
2384
+ uriHint: NonEmptyString
2385
+ });
2386
+ var AgentRenderContribution = z7.strictObject({
2387
+ localId: LocalContributionId,
2388
+ resultKinds: z7.array(NonEmptyString).min(1)
2389
+ });
2390
+ var EndpointContribution = z7.strictObject({
2391
+ localId: LocalContributionId,
2392
+ displayName: OptionalDisplayName,
2393
+ requestSchema: JsonSchema.optional(),
2394
+ responseSchema: JsonSchema.optional()
2395
+ });
2396
+ var MCP_SERVER_LOCAL_HTTP_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
2397
+ var McpServerUrl = z7.string().trim().min(1).refine((value) => {
2398
+ let parsed;
2399
+ try {
2400
+ parsed = new URL(value);
2401
+ } catch {
2402
+ return false;
2403
+ }
2404
+ if (parsed.protocol === "https:")
2405
+ return true;
2406
+ return parsed.protocol === "http:" && MCP_SERVER_LOCAL_HTTP_HOSTNAMES.has(parsed.hostname);
2407
+ }, {
2408
+ message: "mcp server url must be https://, or http:// only for localhost/127.0.0.1 test servers"
2409
+ });
2410
+ var McpServerAuthMode = z7.enum(["oauth", "none"]);
2411
+ var McpServerContribution = z7.strictObject({
2412
+ url: McpServerUrl,
2413
+ auth: McpServerAuthMode.optional(),
2414
+ authService: LocalContributionId.optional()
2415
+ }).superRefine((server, ctx) => {
2416
+ if (server.auth !== "none" && server.authService === void 0) {
2417
+ ctx.addIssue({
2418
+ code: "custom",
2419
+ path: ["authService"],
2420
+ message: "oauth mcp servers must name an authService"
2421
+ });
2422
+ }
2423
+ });
2424
+ var McpServerContributions = z7.record(LocalContributionId, McpServerContribution);
2425
+ var AppEventSubscription = z7.strictObject({
2426
+ localId: LocalContributionId,
2427
+ event: NonEmptyString
2428
+ });
2429
+ var ContextContributorContribution = z7.strictObject({
2430
+ localId: LocalContributionId.max(64),
2431
+ displayName: OptionalDisplayName,
2432
+ description: NonEmptyString.optional()
2433
+ });
2434
+ var PluginEventDefinitionContribution = z7.strictObject({
2435
+ localId: LocalContributionId,
2436
+ displayName: OptionalDisplayName,
2437
+ description: NonEmptyString.optional(),
2438
+ version: z7.number().int().positive().default(1),
2439
+ payloadSchema: JsonSchema
2440
+ });
2441
+ var PluginNotificationRuleContribution = z7.strictObject({
2442
+ localId: LocalContributionId,
2443
+ /** The same-plugin `eventDefinitions` local id this rule matches. */
2444
+ event: LocalContributionId,
2445
+ where: z7.array(JsonObject).optional(),
2446
+ rule: JsonObject
2447
+ });
2448
+ var PLUGIN_NOTIFICATION_RULE_HOST_KEYS = [
2449
+ "id",
2450
+ "match",
2451
+ "sender"
2452
+ ];
2453
+ var UiEventSubscriptionContribution = z7.strictObject({
2454
+ localId: LocalContributionId,
2455
+ event: NonEmptyString
2456
+ });
2457
+ var DeviceDelegationContribution = z7.strictObject({
2458
+ localId: LocalContributionId,
2459
+ device: z7.enum(["microphone"]),
2460
+ delegateAs: z7.enum(["clip", "stream", "status"])
2461
+ });
2462
+ var CorpusContribution = z7.strictObject({
2463
+ localId: LocalContributionId,
2464
+ displayName: OptionalDisplayName,
2465
+ access: z7.enum(["read"]).default("read")
2466
+ });
2467
+ var UiCommandContribution = z7.strictObject({
2468
+ localId: LocalContributionId,
2469
+ title: NonEmptyString,
2470
+ icon: NonEmptyString.optional(),
2471
+ placement: NonEmptyString.optional()
2472
+ });
2473
+ var UiSlotContribution = z7.strictObject({
2474
+ localId: LocalContributionId,
2475
+ slot: NonEmptyString,
2476
+ title: NonEmptyString.optional()
2477
+ });
2478
+ var SettingsPanelContribution = z7.strictObject({
2479
+ localId: LocalContributionId,
2480
+ title: NonEmptyString,
2481
+ configSection: NonEmptyString.optional()
2482
+ });
2483
+ var ResultRendererContribution = z7.strictObject({
2484
+ localId: LocalContributionId,
2485
+ resultKinds: z7.array(NonEmptyString).min(1)
2486
+ });
2487
+ var ReferenceOpenBehavior = z7.discriminatedUnion("kind", [
2488
+ z7.strictObject({ kind: z7.literal("working-document") }),
2489
+ z7.strictObject({ kind: z7.literal("browser-pane") })
2490
+ ]);
2491
+ var ReferencePresentationVariant = z7.strictObject({
2492
+ resourceKind: NonEmptyString,
2493
+ presentationPluginId: PluginId.optional()
2494
+ });
2495
+ var ReferencePresentationVariants = z7.record(MimeTypeString, ReferencePresentationVariant);
2496
+ var InlineSvg = z7.string().max(64 * 1024).refine((value) => /^\s*(?:<\?xml[^>]*>\s*)?(?:<!--[\s\S]*?-->\s*)*(?:<!DOCTYPE[^>]*>\s*)?<svg[\s>]/iu.test(value), { message: "brandIcon must be inline SVG markup." });
2497
+ var ReferencePresentationUrlClaim = z7.strictObject({
2498
+ kind: z7.literal("urlPattern"),
2499
+ pattern: z7.strictObject({
2500
+ protocol: z7.enum(["http", "https"]),
2501
+ hostname: NonEmptyString,
2502
+ pathname: z7.string().optional(),
2503
+ search: z7.string().optional()
2504
+ }),
2505
+ brandIcon: InlineSvg.optional()
2506
+ });
2507
+ var ReferencePresentationContribution = z7.strictObject({
2508
+ localId: LocalContributionId,
2509
+ uriModule: LocalContributionId,
2510
+ resourceKind: NonEmptyString,
2511
+ open: ReferenceOpenBehavior,
2512
+ /** MIME-specific resource kinds; absent keys use the base presentation. */
2513
+ variants: ReferencePresentationVariants.optional(),
2514
+ /** Lexical public URL claims used only for host-rendered browser presentations. */
2515
+ claims: z7.array(ReferencePresentationUrlClaim).optional()
2516
+ }).superRefine((value, ctx) => {
2517
+ if (value.claims !== void 0 && value.claims.length > 0 && value.open.kind !== "browser-pane") {
2518
+ ctx.addIssue({
2519
+ code: "custom",
2520
+ path: ["claims"],
2521
+ message: "URL claims require browser-pane opening."
2522
+ });
2523
+ }
2524
+ });
2525
+ var RichViewContribution = z7.strictObject({
2526
+ localId: LocalContributionId,
2527
+ title: NonEmptyString,
2528
+ route: NonEmptyString.optional()
2529
+ });
2530
+ var KayUiAppHostCapability = z7.enum([
2531
+ "navigation.open-session",
2532
+ "cost.read",
2533
+ // Server-rated organization billing, distinct from local/provider cost.
2534
+ "billing.read",
2535
+ // Context, Skills, and Memory analytics reads (full snapshots via the capability broker).
2536
+ "context.read",
2537
+ "skills.read",
2538
+ "memory.read",
2539
+ // The "session trees" lens: root-session USD/token roll-ups from the
2540
+ // typed usage-query surface (telemetry-unification RFC Phase 3, PR 3
2541
+ // contract `docs/telemetry-lane-pr3-contracts.md`, amendment A7). Follows
2542
+ // the `cost.read`/`context.read`/`skills.read`/`memory.read` convention —
2543
+ // names the data (`usage`), not the tab that happens to render it.
2544
+ "usage.read",
2545
+ // The self-improvement lens: reads the telemetry lane's self-improvement rollup
2546
+ // (memory/skill/notebook activity). Follows the same "names the data" convention.
2547
+ "self-improvement.read",
2548
+ "automations.read",
2549
+ "automations.run",
2550
+ // Grants exactly `panes.place` (see `WORKSPACE_VERB_TIERS`/`KayWorkspaceCommand` in
2551
+ // `@kay/agent-runtime-protocol`) through the `kay.ui` capability broker's `workspace.place`.
2552
+ "workspace.place-pane",
2553
+ // Grants presentation into the native header of the pane hosting this app instance.
2554
+ "workspace.pane-header",
2555
+ // Grants the non-destructive arrangement verbs: tabs.rename/select/move/reopen, panes.focus/maximize/
2556
+ // restore/split, and both shell toggles — through the broker's `workspace.arrange`. Deliberately
2557
+ // EXCLUDES the two `destructive`-tier verbs (`tabs.close`, `panes.close`); a `kay.ui` app can
2558
+ // rearrange the workspace but never remove something the user would have to notice going missing.
2559
+ "workspace.arrange"
2560
+ ]);
2561
+ var UiAppCapabilities = z7.strictObject({
2562
+ endpoints: z7.array(LocalContributionId).optional(),
2563
+ host: z7.array(KayUiAppHostCapability).optional()
2564
+ }).superRefine((capabilities, ctx) => {
2565
+ for (const field of ["endpoints", "host"]) {
2566
+ const seen = /* @__PURE__ */ new Set();
2567
+ for (const [index, value] of (capabilities[field] ?? []).entries()) {
2568
+ if (seen.has(value)) {
2569
+ ctx.addIssue({
2570
+ code: "custom",
2571
+ path: [field, index],
2572
+ message: `duplicate ui app ${field} capability ${value}`
2573
+ });
2574
+ }
2575
+ seen.add(value);
2576
+ }
2577
+ }
2578
+ });
2579
+ var UiAppContribution = z7.strictObject({
2580
+ localId: LocalContributionId,
2581
+ displayName: NonEmptyString,
2582
+ description: NonEmptyString.optional(),
2583
+ icon: PackageAssetPath.optional(),
2584
+ runtime: z7.literal("kay.react-app.v1"),
2585
+ sdk: z7.strictObject({ range: NonEmptyString }),
2586
+ entrypoint: TargetEntrypoint,
2587
+ styles: z7.array(PackageAssetPath).optional(),
2588
+ assets: z7.array(PackageAssetPath).optional(),
2589
+ capabilities: UiAppCapabilities.optional()
2590
+ });
2591
+ var ThemeContribution = z7.strictObject({
2592
+ localId: LocalContributionId,
2593
+ displayName: NonEmptyString,
2594
+ appearance: z7.enum(["light", "dark"]),
2595
+ tokens: PackageAssetPath
2596
+ });
2597
+ var CommandHandler = z7.discriminatedUnion("kind", [
2598
+ z7.strictObject({ kind: z7.literal("skill"), skill: LocalContributionId }),
2599
+ z7.strictObject({
2600
+ kind: z7.literal("tool"),
2601
+ tool: PluginToolName,
2602
+ /** Positional mapping from tokenized `argsText` onto the tool's own input fields — see this
2603
+ * union's doc for the exact tokenize/map/last-field-absorbs rule. */
2604
+ argumentFields: z7.array(NonEmptyString)
2605
+ }),
2606
+ z7.strictObject({
2607
+ kind: z7.literal("endpoint"),
2608
+ endpoint: LocalContributionId
2609
+ })
2610
+ ]);
2611
+ var CommandContribution = z7.strictObject({
2612
+ localId: LocalContributionId,
2613
+ displayName: OptionalDisplayName,
2614
+ description: NonEmptyString,
2615
+ aliases: z7.array(LocalContributionId).optional(),
2616
+ argumentHint: NonEmptyString.optional(),
2617
+ handler: CommandHandler
2618
+ });
2619
+ var PluginContributionKind = z7.enum([
2620
+ "tool",
2621
+ "converter",
2622
+ "uriModule",
2623
+ "artifactUriModule",
2624
+ "cliBinding",
2625
+ "resource",
2626
+ "skill",
2627
+ "searchProvider",
2628
+ "entityResolver",
2629
+ "agentRenderer",
2630
+ "endpoint",
2631
+ "command",
2632
+ "eventDefinition",
2633
+ "eventSubscription",
2634
+ "appDataSource",
2635
+ "whiteboardType",
2636
+ "contextContributor",
2637
+ "transcriptionProvider",
2638
+ "deviceDelegation",
2639
+ "corpusConsumer",
2640
+ "mcpServer",
2641
+ "uiCommand",
2642
+ "uiSlot",
2643
+ "settingsPanel",
2644
+ "resultRenderer",
2645
+ "referencePresentation",
2646
+ "view",
2647
+ "app",
2648
+ "theme",
2649
+ "authService",
2650
+ "storage",
2651
+ "configSection"
2652
+ ]);
2653
+
2654
+ // packages/plugin-protocol/dist/ids.js
2655
+ function resolvePluginNamespace(pluginId, namespace) {
2656
+ return namespace ?? pluginId;
2657
+ }
2658
+ function resolveGlobalContributionId(namespace, localId) {
2659
+ return `${namespace}.${localId}`;
2660
+ }
2661
+ function resolvePluginEventType(namespace, localId) {
2662
+ return resolveGlobalContributionId(namespace, localId);
2663
+ }
2664
+ function isQualifiedAuthServiceRef(ref) {
2665
+ return ref.includes(".");
2666
+ }
2667
+ function isQualifiedAuthSchemeRef(ref) {
2668
+ return ref.includes(".");
2669
+ }
2670
+ function resolveAdapterId(globalId, declaredAdapterId) {
2671
+ return declaredAdapterId ?? globalId;
2672
+ }
2673
+
2674
+ // packages/plugin-protocol/dist/manifest.js
2675
+ import { z as z8 } from "zod";
2676
+
2677
+ // packages/plugin-protocol/dist/synthetic-contributions.js
2678
+ function resolveConfigSectionContributionLocalId(section) {
2679
+ return `config-${toLocalContributionIdFragment(section)}`;
2680
+ }
2681
+ function resolveStorageContributionLocalId(kind) {
2682
+ return kind === "sqlite" ? "storage-sqlite" : "storage-data-dir";
2683
+ }
2684
+ function toLocalContributionIdFragment(value) {
2685
+ const fragment = value.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2686
+ return /^[a-z]/.test(fragment) ? fragment : "section";
2687
+ }
2688
+
2689
+ // packages/plugin-protocol/dist/manifest.js
2690
+ var KAY_UI_NATIVE_EVENT_SUBSCRIPTIONS = [
2691
+ "native:renderer.session.projection.changed",
2692
+ "native:renderer.contributions.changed"
2693
+ ];
2694
+ var KAY_UI_NATIVE_EVENT_ALLOWLIST = new Set(KAY_UI_NATIVE_EVENT_SUBSCRIPTIONS);
2695
+ var KayAgentTargetManifest = z8.strictObject({
2696
+ entrypoint: TargetEntrypoint,
2697
+ contributes: z8.strictObject({
2698
+ tools: z8.array(ToolContribution).optional(),
2699
+ converters: z8.array(ConverterContribution).optional(),
2700
+ uriModules: z8.array(UriModuleContribution).optional(),
2701
+ // Host-composed artifact-backed schemes (see `ArtifactUriModuleContribution`): the plugin
2702
+ // declares scheme + editability; the daemon binds each to the kernel's artifact lifecycle
2703
+ // per session. Separate from `uriModules` (plugin-implemented backends) precisely because
2704
+ // the plugin registers NOTHING for these at activation.
2705
+ artifactUriModules: z8.array(ArtifactUriModuleContribution).optional(),
2706
+ cliBindings: z8.array(CliBindingContribution).optional(),
2707
+ // Which git hosts this plugin's credential serves (see `GitHostContribution`): a
2708
+ // declaration, so a plugin for any git backend gets credentialed clones without
2709
+ // the daemon learning its name. Sibling of `cliBindings` because both answer the
2710
+ // same question — which of this plugin's auth services a host-side consumer
2711
+ // resolves — for two different transports.
2712
+ gitHosts: z8.array(GitHostContribution).optional(),
2713
+ resources: z8.array(ResourceContribution).optional(),
2714
+ skills: z8.array(SkillContribution).optional(),
2715
+ searchProviders: z8.array(SearchProviderContribution).optional(),
2716
+ entityResolvers: z8.array(EntityResolverContribution).optional(),
2717
+ renderers: z8.array(AgentRenderContribution).optional(),
2718
+ // Handler restricted to skill | tool by the manifest-level refinement below (see
2719
+ // `CommandContribution`'s doc for why the restriction is not expressed in the type, and
2720
+ // `CommandHandler`'s doc for why there is no promptTemplate variant to declare here).
2721
+ commands: z8.array(CommandContribution).optional()
2722
+ }).optional(),
2723
+ /**
2724
+ * The plugin's primer skill — the `localId` of one of its own declared
2725
+ * `contributes.skills[]` entries, designating that skill as the plugin's agent-facing front
2726
+ * door (the doc a session is told to read before first using the plugin). A pointer at an
2727
+ * existing contribution, not a contribution kind of its own; the reference is validated by the
2728
+ * manifest's superRefine. Plugins that declare no primer get a synthesized fallback at
2729
+ * composition (host-side), so the field stays optional.
2730
+ */
2731
+ primer: LocalContributionId.optional(),
2732
+ /**
2733
+ * The plugin's prompt doc — a package-relative path (by convention `./prompt.md`) to
2734
+ * a free-form Markdown section the host renders INTO the agent's system prompt, under
2735
+ * `# Your workbench`, for every session the plugin is enabled in. Declaring it is the opt-in:
2736
+ * most plugins should NOT be resident (their `<installed_plugins>` line plus primer is the
2737
+ * default advertisement); a plugin that is part of how the agent works every turn (memory,
2738
+ * scratchpad, variables) earns a section here. The body is prose the agent reads before any
2739
+ * routing, so it should say what the capability is and when to reach for it, not how to
2740
+ * operate it — that is the primer's job, and the host appends the primer route after the body.
2741
+ *
2742
+ * Policy the HOST applies (never trust this field alone): a body over 500 words is dropped,
2743
+ * and a directory/repository-origin plugin's declaration is ignored — a third-party manifest
2744
+ * does not buy prompt residency (mirrors the skill-exposure clamp).
2745
+ */
2746
+ prompt: PackageAssetPath.optional(),
2747
+ /**
2748
+ * External origins of skills this plugin claims as its own (see
2749
+ * {@link PluginSkillSourceDeclaration}). Acquired by the daemon at boot and landed under the
2750
+ * plugin's own skill-source slug, so a vendor's officially-published skills behave exactly
2751
+ * like the ones the plugin ships in-tree.
2752
+ *
2753
+ * A sibling of `contributes`, not a member of it: these skills' names are discovered by
2754
+ * acquisition, not declared here, so there is nothing for the resolved contract to mint a
2755
+ * `globalId` for at parse time.
2756
+ */
2757
+ skillSources: z8.array(PluginSkillSourceDeclaration).optional()
2758
+ });
2759
+ var KayAppTargetManifest = z8.strictObject({
2760
+ // Optional, like `kay.ui`'s: a target may carry only declarative `contributes` (e.g. a plugin
2761
+ // that ships nothing but `mcpServers`) with no app-lifetime module for the host to load.
2762
+ entrypoint: TargetEntrypoint.optional(),
2763
+ contributes: z8.strictObject({
2764
+ // Session tools implemented by the app-lifetime activation (registered once, injected into
2765
+ // every session with a host-stamped session context) — the tool counterpart of `uriModules`
2766
+ // below. Same descriptor shape as kay.agent tools; `toolName` shares ONE flat session
2767
+ // namespace with them (enforced by the manifest-level refinement below).
2768
+ tools: z8.array(ToolContribution).optional(),
2769
+ endpoints: z8.array(EndpointContribution).optional(),
2770
+ uriModules: z8.array(AppUriModuleContribution).optional(),
2771
+ dataSources: z8.array(AppDataSourceContribution).optional(),
2772
+ whiteboardTypes: z8.array(WhiteboardTypeContribution).optional(),
2773
+ eventDefinitions: z8.array(PluginEventDefinitionContribution).optional(),
2774
+ // Default notification rules over this plugin's own `eventDefinitions`; seeded by the host
2775
+ // (see `PluginNotificationRuleContribution`).
2776
+ notificationRules: z8.array(PluginNotificationRuleContribution).optional(),
2777
+ eventSubscriptions: z8.array(AppEventSubscription).optional(),
2778
+ deviceDelegations: z8.array(DeviceDelegationContribution).optional(),
2779
+ corpusConsumers: z8.array(CorpusContribution).optional(),
2780
+ contextContributors: z8.array(ContextContributorContribution).optional(),
2781
+ // Vendored MCP servers this plugin ships (see `McpServerContributions`). Bundled-plugin-only
2782
+ // in practice; this package cannot see plugin origin, so that ceiling is enforced at
2783
+ // composition, not here (see ARCHITECTURE.md "Validation Law").
2784
+ mcpServers: McpServerContributions.optional(),
2785
+ // Handler restricted to endpoint-only by the manifest-level refinement below.
2786
+ commands: z8.array(CommandContribution).optional()
2787
+ }).optional()
2788
+ });
2789
+ var KayUiTargetManifest = z8.strictObject({
2790
+ entrypoint: TargetEntrypoint.optional(),
2791
+ contributes: z8.strictObject({
2792
+ commands: z8.array(UiCommandContribution).optional(),
2793
+ slots: z8.array(UiSlotContribution).optional(),
2794
+ settingsPanels: z8.array(SettingsPanelContribution).optional(),
2795
+ resultRenderers: z8.array(ResultRendererContribution).optional(),
2796
+ referencePresentations: z8.array(ReferencePresentationContribution).optional(),
2797
+ views: z8.array(RichViewContribution).optional(),
2798
+ apps: z8.array(UiAppContribution).optional(),
2799
+ themes: z8.array(ThemeContribution).optional(),
2800
+ eventSubscriptions: z8.array(UiEventSubscriptionContribution).optional(),
2801
+ dataMaterializers: z8.array(AppDataMaterializerContribution).optional()
2802
+ }).optional()
2803
+ });
2804
+ var PluginConfigScope = z8.enum(["global", "project", "agent"]);
2805
+ var PluginConfigSectionManifest = z8.strictObject({
2806
+ section: NonEmptyString.optional(),
2807
+ schema: JsonSchema,
2808
+ /**
2809
+ * DECLARED-DEAD: validated and carried, but no runtime maps it — plugin
2810
+ * config values come from the plugin's compiled defaults plus the settings
2811
+ * document, never from this field. Mapping it into the fold is a future
2812
+ * ruling (it would change folded output for declaring plugins).
2813
+ */
2814
+ defaults: JsonValue.optional(),
2815
+ /**
2816
+ * The scopes a section's values may come from. `project` is the only scope
2817
+ * with live semantics: a manifest that declares scopes WITHOUT `"project"`
2818
+ * gets `layering.projectOverlay: false` on its registered section
2819
+ * (restrictive-only), so per-cwd project files cannot override it. `global`
2820
+ * is the baseline every section has; `agent` has no runtime mapping (the
2821
+ * agent/entity fold is downstream of the document protocol).
2822
+ */
2823
+ scopes: z8.array(PluginConfigScope).optional()
2824
+ });
2825
+ var PluginConfigManifest = z8.array(PluginConfigSectionManifest).min(1);
2826
+ function configSectionKeysForManifest(manifest) {
2827
+ return manifest.config?.map((config) => config.section ?? `plugins.${manifest.id}`) ?? [];
2828
+ }
2829
+ var PluginStorageManifest = z8.strictObject({
2830
+ sqlite: z8.strictObject({
2831
+ enabled: z8.boolean(),
2832
+ ownerTarget: z8.literal("kay.app").optional(),
2833
+ migrations: NonEmptyString.optional()
2834
+ }).optional(),
2835
+ dataDir: z8.strictObject({
2836
+ enabled: z8.boolean(),
2837
+ ownerTarget: z8.literal("kay.app").optional()
2838
+ }).optional()
2839
+ });
2840
+ var PluginOAuthClientSupplier = z8.enum(["kay", "user"]);
2841
+ var PluginProvisioningProbe = z8.strictObject({
2842
+ /**
2843
+ * The API endpoint that answers the question. The host sends the service's OWN credential here,
2844
+ * so this must belong to the provider that minted it — enforced by the daemon against the
2845
+ * scheme's composed token endpoint, not trusted from this manifest.
2846
+ */
2847
+ url: HttpsUrl,
2848
+ /** Absent ⇒ `GET`. */
2849
+ method: z8.enum(["GET"]).optional(),
2850
+ /**
2851
+ * The statuses that make the response a VERDICT at all. Anything else (a 401 from a stale
2852
+ * token, a 5xx, a rate limit) means the probe learned nothing and reports `unknown` — never
2853
+ * `unsatisfied`, which would tell a user to go fix setup that is fine.
2854
+ */
2855
+ verdictStatuses: z8.array(SignedInStatusCode).min(1),
2856
+ /**
2857
+ * Dot-path to a JSON array in the response body that must be NON-EMPTY for the requirement to
2858
+ * count as satisfied (e.g. `installations`). Absent means a verdict status alone is enough.
2859
+ * An empty array is the shape "you are authenticated, and you have none of these" — exactly the
2860
+ * GitHub App case where the token is valid and the app is installed nowhere.
2861
+ */
2862
+ nonEmptyArrayAt: NonEmptyString.optional()
2863
+ });
2864
+ var PluginProvisioningRequirement = z8.strictObject({
2865
+ /** Stable id, unique within the scheme. */
2866
+ id: LocalContributionId,
2867
+ /** Human-facing requirement name, e.g. "GitHub App installation". */
2868
+ displayName: NonEmptyString,
2869
+ /** One sentence saying what is missing and why reconnecting will not fix it. */
2870
+ description: NonEmptyString,
2871
+ /**
2872
+ * Where the user completes the setup. OPTIONAL: the URL can vary per environment, and a plugin
2873
+ * cannot know it at manifest-declaration time — the broker supplies it at runtime. When neither
2874
+ * a declared nor a resolved URL exists, the UI degrades to "Reconnect" instead of showing a
2875
+ * broken link.
2876
+ */
2877
+ remediationUrl: HttpsUrl.optional(),
2878
+ probe: PluginProvisioningProbe
2879
+ });
2880
+ var PluginProvisioningField = {
2881
+ provisioning: z8.array(PluginProvisioningRequirement).optional()
2882
+ };
2883
+ var PluginAuthSchemeDescriptionFields = {
2884
+ /** Human-facing flow name; the runtime scheme name is the fallback. */
2885
+ displayName: NonEmptyString.optional(),
2886
+ /** What this flow unlocks and when an agent should choose it. */
2887
+ agentHint: NonEmptyString.optional(),
2888
+ /**
2889
+ * What this flow unlocks, for the person: one plain sentence with no identifiers or URIs, shown
2890
+ * on the Plugins page beside the sign-in. The agent reads `agentHint`, not this.
2891
+ */
2892
+ summary: NonEmptyString.optional()
2893
+ };
2894
+ var PluginAuthSchemeOAuthPkce = z8.strictObject({
2895
+ ...PluginProvisioningField,
2896
+ ...PluginAuthSchemeDescriptionFields,
2897
+ localId: LocalContributionId,
2898
+ kind: z8.literal("oauth2"),
2899
+ exchange: z8.literal("pkce"),
2900
+ /**
2901
+ * Unordered SET of who may supply the client — non-empty, and every entry must be distinct
2902
+ * (fail-closed re-assertion of the kernel's construction-time dedupe: TS exhaustiveness never
2903
+ * runs on an untrusted manifest, so a hand-built `["kay","kay"]` must be refused here, not
2904
+ * silently normalized).
2905
+ */
2906
+ clientSuppliers: z8.array(PluginOAuthClientSupplier).nonempty().superRefine((suppliers, ctx) => {
2907
+ if (new Set(suppliers).size !== suppliers.length) {
2908
+ ctx.addIssue({
2909
+ code: "custom",
2910
+ message: `clientSuppliers must not repeat a value (got ${JSON.stringify(suppliers)})`
2911
+ });
2912
+ }
2913
+ }),
2914
+ scopes: z8.array(NonEmptyString).optional()
2915
+ });
2916
+ var PluginAuthSchemeOAuthConfidential = z8.strictObject({
2917
+ ...PluginProvisioningField,
2918
+ ...PluginAuthSchemeDescriptionFields,
2919
+ localId: LocalContributionId,
2920
+ kind: z8.literal("oauth2"),
2921
+ exchange: z8.literal("confidential"),
2922
+ /** Absent means `["kay"]` — a confidential exchange's secret may only ever be Kay's. */
2923
+ clientSuppliers: z8.tuple([z8.literal("kay")]).optional(),
2924
+ scopes: z8.array(NonEmptyString).optional()
2925
+ });
2926
+ var PluginAuthSchemeOAuth2 = z8.discriminatedUnion("exchange", [
2927
+ PluginAuthSchemeOAuthPkce,
2928
+ PluginAuthSchemeOAuthConfidential
2929
+ ]);
2930
+ var PluginAuthSchemeApiKey = z8.strictObject({
2931
+ ...PluginProvisioningField,
2932
+ ...PluginAuthSchemeDescriptionFields,
2933
+ localId: LocalContributionId,
2934
+ kind: z8.literal("api-key"),
2935
+ scopes: z8.array(NonEmptyString).optional()
2936
+ });
2937
+ var PluginAuthSchemeDelegated = z8.strictObject({
2938
+ ...PluginAuthSchemeDescriptionFields,
2939
+ localId: LocalContributionId,
2940
+ kind: z8.literal("delegated"),
2941
+ scopes: z8.array(NonEmptyString).optional()
2942
+ });
2943
+ var PluginAuthSchemeExternalCli = z8.strictObject({
2944
+ ...PluginAuthSchemeDescriptionFields,
2945
+ localId: LocalContributionId,
2946
+ kind: z8.literal("external-cli"),
2947
+ scopes: z8.array(NonEmptyString).optional()
2948
+ });
2949
+ var PluginAuthScheme = z8.discriminatedUnion("kind", [
2950
+ PluginAuthSchemeOAuth2,
2951
+ PluginAuthSchemeApiKey,
2952
+ PluginAuthSchemeDelegated,
2953
+ PluginAuthSchemeExternalCli
2954
+ ]);
2955
+ var PluginWebSessionProbe = z8.strictObject({
2956
+ url: HttpsUrl,
2957
+ /** Absent ⇒ `GET`. `POST` sends an empty JSON object body. */
2958
+ method: z8.enum(["GET", "POST"]).optional(),
2959
+ signedInStatuses: z8.array(SignedInStatusCode).min(1)
2960
+ });
2961
+ var PluginWebSessionDeclaration = z8.strictObject({
2962
+ /** `required`: capabilities degrade without the site login; `recommended`: it only smooths UX. */
2963
+ importance: z8.enum(["required", "recommended"]),
2964
+ loginUrl: HttpsUrl,
2965
+ cookieDomains: z8.array(RegistrableDomain).min(1),
2966
+ probe: PluginWebSessionProbe,
2967
+ oauthVenue: z8.enum(["embedded", "external"])
2968
+ }).superRefine(refineWebSessionScopeConfined);
2969
+ var PluginAuthService = z8.strictObject({
2970
+ localId: LocalContributionId,
2971
+ displayName: NonEmptyString,
2972
+ schemes: z8.array(PluginAuthScheme).min(1),
2973
+ purposes: z8.array(NonEmptyString).min(1),
2974
+ webSession: PluginWebSessionDeclaration.optional()
2975
+ });
2976
+ var PluginAuthManifest = z8.strictObject({
2977
+ services: z8.array(PluginAuthService).optional()
2978
+ });
2979
+ var RETIRED_COMPATIBILITY_KEYS = ["policy"];
2980
+ function stripRetiredCompatibilityKeys(value) {
2981
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2982
+ return value;
2983
+ }
2984
+ const remaining = { ...value };
2985
+ for (const key of RETIRED_COMPATIBILITY_KEYS)
2986
+ delete remaining[key];
2987
+ return remaining;
2988
+ }
2989
+ var PluginCompatibilityManifest = z8.preprocess(stripRetiredCompatibilityKeys, z8.strictObject({
2990
+ supportedProtocols: z8.array(KayPluginProtocolVersion).optional(),
2991
+ minKayVersion: NonEmptyString.optional(),
2992
+ sdk: NonEmptyString.optional()
2993
+ }));
2994
+ function retiredCompatibilityKeysIn(rawManifest) {
2995
+ if (typeof rawManifest !== "object" || rawManifest === null)
2996
+ return [];
2997
+ const compatibility = rawManifest["compatibility"];
2998
+ if (typeof compatibility !== "object" || compatibility === null)
2999
+ return [];
3000
+ const record = compatibility;
3001
+ return RETIRED_COMPATIBILITY_KEYS.filter((key) => key in record);
3002
+ }
3003
+ var KayVersionString = NonEmptyString.regex(SEMVER_PATTERN, {
3004
+ message: "version must be semver, for example 1.2.3"
3005
+ });
3006
+ var KayPluginManifestV1 = z8.strictObject({
3007
+ protocol: z8.literal(KAY_PLUGIN_PROTOCOL_VERSION),
3008
+ id: PluginId,
3009
+ displayName: NonEmptyString,
3010
+ version: KayVersionString,
3011
+ publisher: NonEmptyString.optional(),
3012
+ /**
3013
+ * What this plugin lets an agent do, in one line. Required, and **model-visible**: it is
3014
+ * the plugin's entry in the `plugin://` address space, and for most agents it is the first
3015
+ * and only sentence they will read about this plugin. Write it for the agent deciding
3016
+ * whether to reach for this plugin at all — not as marketing copy.
3017
+ */
3018
+ description: NonEmptyString,
3019
+ // Display icons resolved relative to the package root and traversal-guarded
3020
+ // by `PackageAssetPath`. `iconDark` is an optional dark-appearance variant;
3021
+ // when absent the host falls back to `icon`. This is a *raster/asset* icon —
3022
+ // distinct from the *symbolic* `UiCommandContribution.icon` (an icon-set
3023
+ // name), see LEXICON `icon`.
3024
+ icon: PackageAssetPath.optional(),
3025
+ iconDark: PackageAssetPath.optional(),
3026
+ namespace: PluginNamespace.optional(),
3027
+ targets: z8.strictObject({
3028
+ kay: z8.strictObject({
3029
+ agent: KayAgentTargetManifest.optional(),
3030
+ app: KayAppTargetManifest.optional(),
3031
+ ui: KayUiTargetManifest.optional()
3032
+ }).optional()
3033
+ }).optional(),
3034
+ config: PluginConfigManifest.optional(),
3035
+ storage: PluginStorageManifest.optional(),
3036
+ auth: PluginAuthManifest.optional(),
3037
+ permissions: PluginPermissionManifest.optional(),
3038
+ compatibility: PluginCompatibilityManifest.optional(),
3039
+ /**
3040
+ * Explicit plugin-level dependencies — other plugins this plugin requires to be present in the
3041
+ * composed roster. At composition time the runtime checks each id against the accepted
3042
+ * registration list and derives a provenance-complete dependency graph (together with implicit
3043
+ * auth-service edges from `consumesAuthServices`). Absent ⇒ no explicit dependencies.
3044
+ */
3045
+ dependencies: z8.strictObject({
3046
+ plugins: z8.array(PluginId).optional()
3047
+ }).optional()
3048
+ }).superRefine((manifest, ctx) => {
3049
+ const authServices = new Set(manifest.auth?.services?.map((service) => service.localId) ?? []);
3050
+ const authSchemeIdsByService = new Map((manifest.auth?.services ?? []).map((service) => [
3051
+ service.localId,
3052
+ new Set(service.schemes.flatMap((scheme) => [
3053
+ scheme.localId,
3054
+ `${manifest.namespace ?? manifest.id}.${scheme.localId}`
3055
+ ]))
3056
+ ]));
3057
+ const endpointIds = new Set(manifest.targets?.kay?.app?.contributes?.endpoints?.map((endpoint) => endpoint.localId) ?? []);
3058
+ const storageKinds = /* @__PURE__ */ new Set();
3059
+ if (manifest.storage?.sqlite?.enabled)
3060
+ storageKinds.add("sqlite");
3061
+ if (manifest.storage?.dataDir?.enabled)
3062
+ storageKinds.add("dataDir");
3063
+ const configSections = /* @__PURE__ */ new Set();
3064
+ for (const [index, section] of configSectionKeysForManifest(manifest).entries()) {
3065
+ if (configSections.has(section)) {
3066
+ ctx.addIssue({
3067
+ code: "custom",
3068
+ path: ["config", index, "section"],
3069
+ message: `duplicate config section ${section}`
3070
+ });
3071
+ }
3072
+ configSections.add(section);
3073
+ }
3074
+ const subscribedAppEvents = new Set(manifest.targets?.kay?.app?.contributes?.eventSubscriptions?.map((subscription) => subscription.event) ?? []);
3075
+ const definedPluginEvents = new Set(manifest.targets?.kay?.app?.contributes?.eventDefinitions?.map((definition) => definition.localId) ?? []);
3076
+ for (const [index, rule] of (manifest.targets?.kay?.app?.contributes?.notificationRules ?? []).entries()) {
3077
+ const path = [
3078
+ "targets",
3079
+ "kay",
3080
+ "app",
3081
+ "contributes",
3082
+ "notificationRules",
3083
+ index
3084
+ ];
3085
+ if (!definedPluginEvents.has(rule.event)) {
3086
+ ctx.addIssue({
3087
+ code: "custom",
3088
+ path: [...path, "event"],
3089
+ message: `notification rule ${rule.localId} references unknown plugin event ${rule.event}`
3090
+ });
3091
+ }
3092
+ for (const key of PLUGIN_NOTIFICATION_RULE_HOST_KEYS) {
3093
+ if (Object.hasOwn(rule.rule, key)) {
3094
+ ctx.addIssue({
3095
+ code: "custom",
3096
+ path: [...path, "rule", key],
3097
+ message: `notification rule ${rule.localId} sets '${key}', which the host stamps for a plugin's default rule`
3098
+ });
3099
+ }
3100
+ }
3101
+ }
3102
+ reportDuplicateLocalIds(ctx, ["targets", "kay", "app", "contributes", "notificationRules"], manifest.targets?.kay?.app?.contributes?.notificationRules, "notification rule");
3103
+ const declaredAppDataSources = new Set(manifest.targets?.kay?.app?.contributes?.dataSources?.map((source) => source.localId) ?? []);
3104
+ const materializedAppDataSources = /* @__PURE__ */ new Set();
3105
+ for (const [index, materializer] of (manifest.targets?.kay?.ui?.contributes?.dataMaterializers ?? []).entries()) {
3106
+ if (!declaredAppDataSources.has(materializer.source)) {
3107
+ ctx.addIssue({
3108
+ code: "custom",
3109
+ path: [
3110
+ "targets",
3111
+ "kay",
3112
+ "ui",
3113
+ "contributes",
3114
+ "dataMaterializers",
3115
+ index,
3116
+ "source"
3117
+ ],
3118
+ message: `app-data materializer references unknown app source ${materializer.source}`
3119
+ });
3120
+ }
3121
+ if (materializedAppDataSources.has(materializer.source)) {
3122
+ ctx.addIssue({
3123
+ code: "custom",
3124
+ path: [
3125
+ "targets",
3126
+ "kay",
3127
+ "ui",
3128
+ "contributes",
3129
+ "dataMaterializers",
3130
+ index,
3131
+ "source"
3132
+ ],
3133
+ message: `app-data source ${materializer.source} has more than one materializer`
3134
+ });
3135
+ }
3136
+ materializedAppDataSources.add(materializer.source);
3137
+ }
3138
+ for (const source of declaredAppDataSources) {
3139
+ if (materializedAppDataSources.has(source))
3140
+ continue;
3141
+ ctx.addIssue({
3142
+ code: "custom",
3143
+ path: ["targets", "kay", "app", "contributes", "dataSources"],
3144
+ message: `app-data source ${source} has no same-plugin UI materializer`
3145
+ });
3146
+ }
3147
+ reportDuplicateLocalIds(ctx, ["targets", "kay", "app", "contributes", "whiteboardTypes"], manifest.targets?.kay?.app?.contributes?.whiteboardTypes, "Whiteboard type");
3148
+ const uiAppIds = new Set(manifest.targets?.kay?.ui?.contributes?.apps?.map((app) => app.localId) ?? []);
3149
+ for (const [index, type] of (manifest.targets?.kay?.app?.contributes?.whiteboardTypes ?? []).entries()) {
3150
+ if (type.presentation !== void 0 && !uiAppIds.has(type.presentation.app)) {
3151
+ ctx.addIssue({
3152
+ code: "custom",
3153
+ path: [
3154
+ "targets",
3155
+ "kay",
3156
+ "app",
3157
+ "contributes",
3158
+ "whiteboardTypes",
3159
+ index,
3160
+ "presentation",
3161
+ "app"
3162
+ ],
3163
+ message: `Whiteboard type ${type.localId} references unknown ui app ${type.presentation.app}`
3164
+ });
3165
+ }
3166
+ }
3167
+ const subscribedUiEvents = new Set(manifest.targets?.kay?.ui?.contributes?.eventSubscriptions?.map((subscription) => subscription.event) ?? []);
3168
+ const invokedUiEndpoints = new Set(manifest.permissions?.byTarget["kay.ui"]?.endpoints?.filter((grant) => grant.operations.includes("invoke")).map((grant) => grant.endpoint) ?? []);
3169
+ const uriModuleIds = new Set(manifest.targets?.kay?.agent?.contributes?.uriModules?.map((module) => module.localId) ?? []);
3170
+ const presentationByUriModule = /* @__PURE__ */ new Map();
3171
+ for (const [index, presentation] of (manifest.targets?.kay?.ui?.contributes?.referencePresentations ?? []).entries()) {
3172
+ const path = [
3173
+ "targets",
3174
+ "kay",
3175
+ "ui",
3176
+ "contributes",
3177
+ "referencePresentations",
3178
+ index
3179
+ ];
3180
+ if (!uriModuleIds.has(presentation.uriModule)) {
3181
+ ctx.addIssue({
3182
+ code: "custom",
3183
+ path: [...path, "uriModule"],
3184
+ message: `reference presentation ${presentation.localId} references unknown uri module ${presentation.uriModule}`
3185
+ });
3186
+ }
3187
+ const existing = presentationByUriModule.get(presentation.uriModule);
3188
+ if (existing !== void 0) {
3189
+ ctx.addIssue({
3190
+ code: "custom",
3191
+ path: [...path, "uriModule"],
3192
+ message: `uri module ${presentation.uriModule} is presented by both ${existing} and ${presentation.localId}`
3193
+ });
3194
+ } else {
3195
+ presentationByUriModule.set(presentation.uriModule, presentation.localId);
3196
+ }
3197
+ }
3198
+ reportDuplicateLocalIds(ctx, ["targets", "kay", "ui", "contributes", "referencePresentations"], manifest.targets?.kay?.ui?.contributes?.referencePresentations, "reference presentation");
3199
+ reportDuplicateLocalIds(ctx, ["targets", "kay", "app", "contributes", "eventDefinitions"], manifest.targets?.kay?.app?.contributes?.eventDefinitions, "plugin event definition");
3200
+ reportDuplicateLocalIds(ctx, ["targets", "kay", "app", "contributes", "contextContributors"], manifest.targets?.kay?.app?.contributes?.contextContributors, "context contributor");
3201
+ const syntheticContributionIds = collectSyntheticContributionIds(manifest);
3202
+ for (const contribution of collectDeclaredContributionIds(manifest)) {
3203
+ if (syntheticContributionIds.has(contribution.localId)) {
3204
+ ctx.addIssue({
3205
+ code: "custom",
3206
+ path: contribution.path,
3207
+ message: `contribution local id ${contribution.localId} is reserved by a manifest-level declaration`
3208
+ });
3209
+ }
3210
+ }
3211
+ const toolNames = /* @__PURE__ */ new Map();
3212
+ for (const target of ["agent", "app"]) {
3213
+ const contributes = target === "agent" ? manifest.targets?.kay?.agent?.contributes : manifest.targets?.kay?.app?.contributes;
3214
+ for (const tool of contributes?.tools ?? []) {
3215
+ const existing = toolNames.get(tool.toolName);
3216
+ if (existing !== void 0) {
3217
+ ctx.addIssue({
3218
+ code: "custom",
3219
+ path: ["targets", "kay", target, "contributes", "tools"],
3220
+ message: `toolName ${tool.toolName} is declared by both ${existing} and ${tool.localId}`
3221
+ });
3222
+ }
3223
+ toolNames.set(tool.toolName, tool.localId);
3224
+ }
3225
+ }
3226
+ const converterMimeTypes = /* @__PURE__ */ new Map();
3227
+ for (const converter of manifest.targets?.kay?.agent?.contributes?.converters ?? []) {
3228
+ for (const mimeType of converter.mimeTypes) {
3229
+ const existing = converterMimeTypes.get(mimeType);
3230
+ if (existing !== void 0) {
3231
+ ctx.addIssue({
3232
+ code: "custom",
3233
+ path: ["targets", "kay", "agent", "contributes", "converters"],
3234
+ message: `mime type ${mimeType} is declared by both ${existing} and ${converter.localId}`
3235
+ });
3236
+ }
3237
+ converterMimeTypes.set(mimeType, converter.localId);
3238
+ }
3239
+ }
3240
+ const agentPrimer = manifest.targets?.kay?.agent?.primer;
3241
+ if (agentPrimer !== void 0) {
3242
+ const declaredSkillIds = new Set((manifest.targets?.kay?.agent?.contributes?.skills ?? []).map((skill) => skill.localId));
3243
+ if (!declaredSkillIds.has(agentPrimer)) {
3244
+ ctx.addIssue({
3245
+ code: "custom",
3246
+ path: ["targets", "kay", "agent", "primer"],
3247
+ message: `primer '${agentPrimer}' does not name a declared kay.agent skill contribution`
3248
+ });
3249
+ }
3250
+ }
3251
+ for (const skill of manifest.targets?.kay?.agent?.contributes?.skills ?? []) {
3252
+ if (skill.localId === "primer" && agentPrimer !== "primer") {
3253
+ ctx.addIssue({
3254
+ code: "custom",
3255
+ path: ["targets", "kay", "agent", "contributes", "skills"],
3256
+ message: `skill localId 'primer' is reserved for the plugin's designated primer, so set targets.kay.agent.primer to 'primer' or rename the skill`
3257
+ });
3258
+ }
3259
+ }
3260
+ for (const module of manifest.targets?.kay?.agent?.contributes?.uriModules ?? []) {
3261
+ if (module.authService && !isQualifiedAuthServiceRef(module.authService) && !authServices.has(module.authService)) {
3262
+ ctx.addIssue({
3263
+ code: "custom",
3264
+ path: ["targets", "kay", "agent", "contributes", "uriModules"],
3265
+ message: `uri module ${module.localId} references unknown auth service ${module.authService}`
3266
+ });
3267
+ }
3268
+ }
3269
+ for (const [index, resolver] of (manifest.targets?.kay?.agent?.contributes?.entityResolvers ?? []).entries()) {
3270
+ if (!uriModuleIds.has(resolver.uriModule)) {
3271
+ ctx.addIssue({
3272
+ code: "custom",
3273
+ path: [
3274
+ "targets",
3275
+ "kay",
3276
+ "agent",
3277
+ "contributes",
3278
+ "entityResolvers",
3279
+ index,
3280
+ "uriModule"
3281
+ ],
3282
+ message: `entity resolver ${resolver.localId} references unknown uri module ${resolver.uriModule}`
3283
+ });
3284
+ }
3285
+ }
3286
+ reportDuplicateLocalIds(ctx, ["targets", "kay", "agent", "contributes", "entityResolvers"], manifest.targets?.kay?.agent?.contributes?.entityResolvers, "entity resolver");
3287
+ reportDuplicateLocalIds(ctx, ["targets", "kay", "agent", "contributes", "artifactUriModules"], manifest.targets?.kay?.agent?.contributes?.artifactUriModules, "artifact uri module");
3288
+ const artifactSchemeOwners = /* @__PURE__ */ new Map();
3289
+ const uriModuleSchemeOwners = /* @__PURE__ */ new Map();
3290
+ for (const module of manifest.targets?.kay?.agent?.contributes?.uriModules ?? []) {
3291
+ for (const scheme of module.schemes)
3292
+ uriModuleSchemeOwners.set(scheme, module.localId);
3293
+ }
3294
+ for (const module of manifest.targets?.kay?.app?.contributes?.uriModules ?? []) {
3295
+ for (const scheme of module.schemes)
3296
+ uriModuleSchemeOwners.set(scheme, module.localId);
3297
+ }
3298
+ for (const module of manifest.targets?.kay?.agent?.contributes?.artifactUriModules ?? []) {
3299
+ const uriOwner = uriModuleSchemeOwners.get(module.scheme);
3300
+ if (uriOwner !== void 0) {
3301
+ ctx.addIssue({
3302
+ code: "custom",
3303
+ path: [
3304
+ "targets",
3305
+ "kay",
3306
+ "agent",
3307
+ "contributes",
3308
+ "artifactUriModules"
3309
+ ],
3310
+ message: `artifact uri module ${module.localId} claims scheme ${module.scheme} already declared by uri module ${uriOwner}`
3311
+ });
3312
+ }
3313
+ const artifactOwner = artifactSchemeOwners.get(module.scheme);
3314
+ if (artifactOwner !== void 0) {
3315
+ ctx.addIssue({
3316
+ code: "custom",
3317
+ path: [
3318
+ "targets",
3319
+ "kay",
3320
+ "agent",
3321
+ "contributes",
3322
+ "artifactUriModules"
3323
+ ],
3324
+ message: `artifact uri module scheme ${module.scheme} is declared by both ${artifactOwner} and ${module.localId}`
3325
+ });
3326
+ }
3327
+ artifactSchemeOwners.set(module.scheme, module.localId);
3328
+ }
3329
+ const cliBindingNames = /* @__PURE__ */ new Map();
3330
+ for (const binding of manifest.targets?.kay?.agent?.contributes?.cliBindings ?? []) {
3331
+ if (!isQualifiedAuthServiceRef(binding.authService) && !authServices.has(binding.authService)) {
3332
+ ctx.addIssue({
3333
+ code: "custom",
3334
+ path: ["targets", "kay", "agent", "contributes", "cliBindings"],
3335
+ message: `cli binding ${binding.localId} references unknown auth service ${binding.authService}`
3336
+ });
3337
+ }
3338
+ for (const name of [binding.binary, ...binding.aliases ?? []]) {
3339
+ const owner = cliBindingNames.get(name);
3340
+ if (owner !== void 0) {
3341
+ ctx.addIssue({
3342
+ code: "custom",
3343
+ path: ["targets", "kay", "agent", "contributes", "cliBindings"],
3344
+ message: `cli binding binary/alias ${name} is declared by both ${owner} and ${binding.localId}`
3345
+ });
3346
+ continue;
3347
+ }
3348
+ cliBindingNames.set(name, binding.localId);
3349
+ }
3350
+ }
3351
+ const gitHostClaimants = /* @__PURE__ */ new Set();
3352
+ for (const claim of manifest.targets?.kay?.agent?.contributes?.gitHosts ?? []) {
3353
+ if (!authServices.has(claim.authService)) {
3354
+ ctx.addIssue({
3355
+ code: "custom",
3356
+ path: ["targets", "kay", "agent", "contributes", "gitHosts"],
3357
+ message: `git host ${claim.host} references unknown auth service ${claim.authService}`
3358
+ });
3359
+ }
3360
+ if (gitHostClaimants.has(claim.host)) {
3361
+ ctx.addIssue({
3362
+ code: "custom",
3363
+ path: ["targets", "kay", "agent", "contributes", "gitHosts"],
3364
+ message: `git host ${claim.host} is claimed twice by this manifest`
3365
+ });
3366
+ }
3367
+ gitHostClaimants.add(claim.host);
3368
+ }
3369
+ for (const [name, server] of Object.entries(manifest.targets?.kay?.app?.contributes?.mcpServers ?? {})) {
3370
+ if (server.authService !== void 0 && !isQualifiedAuthServiceRef(server.authService) && !authServices.has(server.authService)) {
3371
+ ctx.addIssue({
3372
+ code: "custom",
3373
+ path: [
3374
+ "targets",
3375
+ "kay",
3376
+ "app",
3377
+ "contributes",
3378
+ "mcpServers",
3379
+ name,
3380
+ "authService"
3381
+ ],
3382
+ message: `mcp server ${name} references unknown auth service ${server.authService}`
3383
+ });
3384
+ }
3385
+ }
3386
+ const skillLocalIds = new Set(manifest.targets?.kay?.agent?.contributes?.skills?.map((skill) => skill.localId) ?? []);
3387
+ const commandNames = /* @__PURE__ */ new Map();
3388
+ const validateCommandName = (path, command) => {
3389
+ for (const name of [command.localId, ...command.aliases ?? []]) {
3390
+ const owner = commandNames.get(name);
3391
+ if (owner !== void 0) {
3392
+ ctx.addIssue({
3393
+ code: "custom",
3394
+ path: [...path, "localId"],
3395
+ message: `command name ${name} is declared by both ${owner} and ${command.localId}`
3396
+ });
3397
+ continue;
3398
+ }
3399
+ commandNames.set(name, command.localId);
3400
+ }
3401
+ };
3402
+ for (const [index, command] of (manifest.targets?.kay?.agent?.contributes?.commands ?? []).entries()) {
3403
+ const path = [
3404
+ "targets",
3405
+ "kay",
3406
+ "agent",
3407
+ "contributes",
3408
+ "commands",
3409
+ index
3410
+ ];
3411
+ validateCommandName(path, command);
3412
+ if (command.handler.kind === "endpoint") {
3413
+ ctx.addIssue({
3414
+ code: "custom",
3415
+ path: [...path, "handler", "kind"],
3416
+ message: `command ${command.localId} declares an endpoint handler on kay.agent; endpoint handlers are kay.app-only`
3417
+ });
3418
+ } else if (command.handler.kind === "skill" && !skillLocalIds.has(command.handler.skill)) {
3419
+ ctx.addIssue({
3420
+ code: "custom",
3421
+ path: [...path, "handler", "skill"],
3422
+ message: `command ${command.localId} references unknown skill ${command.handler.skill}`
3423
+ });
3424
+ } else if (command.handler.kind === "tool" && !toolNames.has(command.handler.tool)) {
3425
+ ctx.addIssue({
3426
+ code: "custom",
3427
+ path: [...path, "handler", "tool"],
3428
+ message: `command ${command.localId} references unknown tool ${command.handler.tool}`
3429
+ });
3430
+ }
3431
+ }
3432
+ for (const [index, command] of (manifest.targets?.kay?.app?.contributes?.commands ?? []).entries()) {
3433
+ const path = ["targets", "kay", "app", "contributes", "commands", index];
3434
+ validateCommandName(path, command);
3435
+ if (command.handler.kind !== "endpoint") {
3436
+ ctx.addIssue({
3437
+ code: "custom",
3438
+ path: [...path, "handler", "kind"],
3439
+ message: `command ${command.localId} declares a ${command.handler.kind} handler on kay.app; kay.app commands must use an endpoint handler`
3440
+ });
3441
+ } else if (!endpointIds.has(command.handler.endpoint)) {
3442
+ ctx.addIssue({
3443
+ code: "custom",
3444
+ path: [...path, "handler", "endpoint"],
3445
+ message: `command ${command.localId} references unknown endpoint ${command.handler.endpoint}`
3446
+ });
3447
+ }
3448
+ }
3449
+ for (const [appIndex, app] of (manifest.targets?.kay?.ui?.contributes?.apps ?? []).entries()) {
3450
+ for (const [endpointIndex, endpoint] of (app.capabilities?.endpoints ?? []).entries()) {
3451
+ const path = [
3452
+ "targets",
3453
+ "kay",
3454
+ "ui",
3455
+ "contributes",
3456
+ "apps",
3457
+ appIndex,
3458
+ "capabilities",
3459
+ "endpoints",
3460
+ endpointIndex
3461
+ ];
3462
+ if (!endpointIds.has(endpoint)) {
3463
+ ctx.addIssue({
3464
+ code: "custom",
3465
+ path,
3466
+ message: `ui app ${app.localId} references unknown endpoint ${endpoint}`
3467
+ });
3468
+ } else if (!invokedUiEndpoints.has(endpoint)) {
3469
+ ctx.addIssue({
3470
+ code: "custom",
3471
+ path,
3472
+ message: `ui app ${app.localId} endpoint ${endpoint} requires a kay.ui invoke grant`
3473
+ });
3474
+ }
3475
+ }
3476
+ }
3477
+ for (const [index, subscription] of (manifest.targets?.kay?.ui?.contributes?.eventSubscriptions ?? []).entries()) {
3478
+ const path = [
3479
+ "targets",
3480
+ "kay",
3481
+ "ui",
3482
+ "contributes",
3483
+ "eventSubscriptions",
3484
+ index,
3485
+ "event"
3486
+ ];
3487
+ const validation = validateKayUiEventSubscription(subscription.event, definedPluginEvents);
3488
+ if (validation.valid)
3489
+ continue;
3490
+ ctx.addIssue({
3491
+ code: "custom",
3492
+ path,
3493
+ message: validation.message
3494
+ });
3495
+ }
3496
+ for (const target of KAY_PLUGIN_TARGET_NAMES) {
3497
+ const targetPermissions = manifest.permissions?.byTarget[target];
3498
+ for (const grant of targetPermissions?.auth ?? []) {
3499
+ if (!isQualifiedAuthServiceRef(grant.service) && !authServices.has(grant.service)) {
3500
+ ctx.addIssue({
3501
+ code: "custom",
3502
+ path: ["permissions", "byTarget", target, "auth"],
3503
+ message: `permission references unknown auth service ${grant.service}`
3504
+ });
3505
+ }
3506
+ if (!isQualifiedAuthServiceRef(grant.service)) {
3507
+ const declaredSchemes = authSchemeIdsByService.get(grant.service);
3508
+ for (const scheme of grant.schemes ?? []) {
3509
+ if (declaredSchemes !== void 0 && !declaredSchemes.has(scheme)) {
3510
+ ctx.addIssue({
3511
+ code: "custom",
3512
+ path: ["permissions", "byTarget", target, "auth"],
3513
+ message: `permission references unknown auth scheme ${scheme} for service ${grant.service}`
3514
+ });
3515
+ }
3516
+ }
3517
+ }
3518
+ }
3519
+ for (const grant of targetPermissions?.endpoints ?? []) {
3520
+ if (!endpointIds.has(grant.endpoint)) {
3521
+ ctx.addIssue({
3522
+ code: "custom",
3523
+ path: ["permissions", "byTarget", target, "endpoints"],
3524
+ message: `permission references unknown endpoint contribution ${grant.endpoint}`
3525
+ });
3526
+ }
3527
+ }
3528
+ for (const grant of targetPermissions?.storage ?? []) {
3529
+ if (!storageKinds.has(grant.kind)) {
3530
+ ctx.addIssue({
3531
+ code: "custom",
3532
+ path: ["permissions", "byTarget", target, "storage"],
3533
+ message: `permission references undeclared storage kind ${grant.kind}`
3534
+ });
3535
+ }
3536
+ }
3537
+ for (const grant of targetPermissions?.events ?? []) {
3538
+ if (target === "kay.agent") {
3539
+ ctx.addIssue({
3540
+ code: "custom",
3541
+ path: ["permissions", "byTarget", target, "events"],
3542
+ message: `kay.agent targets cannot declare events grants: no runtime event capability backs them (grant references ${grant.event})`
3543
+ });
3544
+ continue;
3545
+ }
3546
+ if (grant.operations.includes("subscribe") && target === "kay.app" && !subscribedAppEvents.has(grant.event)) {
3547
+ ctx.addIssue({
3548
+ code: "custom",
3549
+ path: ["permissions", "byTarget", target, "events"],
3550
+ message: `event subscribe permission references undeclared app event subscription ${grant.event}`
3551
+ });
3552
+ }
3553
+ if (grant.operations.includes("subscribe") && target === "kay.ui" && !subscribedUiEvents.has(grant.event)) {
3554
+ ctx.addIssue({
3555
+ code: "custom",
3556
+ path: ["permissions", "byTarget", target, "events"],
3557
+ message: `event subscribe permission references undeclared ui event subscription ${grant.event}`
3558
+ });
3559
+ }
3560
+ if (grant.operations.includes("publish") && target === "kay.app") {
3561
+ const localId = grant.event.startsWith("self:") ? grant.event.slice("self:".length) : grant.event;
3562
+ if (!definedPluginEvents.has(localId)) {
3563
+ ctx.addIssue({
3564
+ code: "custom",
3565
+ path: ["permissions", "byTarget", target, "events"],
3566
+ message: `event publish permission references undeclared plugin event ${grant.event}`
3567
+ });
3568
+ }
3569
+ }
3570
+ }
3571
+ }
3572
+ const declaredDeps = manifest.dependencies?.plugins;
3573
+ if (declaredDeps !== void 0 && declaredDeps.length > 0) {
3574
+ const seen = /* @__PURE__ */ new Set();
3575
+ for (const [index, depId] of declaredDeps.entries()) {
3576
+ if (depId === manifest.id) {
3577
+ ctx.addIssue({
3578
+ code: "custom",
3579
+ path: ["dependencies", "plugins", index],
3580
+ message: `plugin '${manifest.id}' cannot declare itself as a dependency`
3581
+ });
3582
+ continue;
3583
+ }
3584
+ if (seen.has(depId)) {
3585
+ ctx.addIssue({
3586
+ code: "custom",
3587
+ path: ["dependencies", "plugins", index],
3588
+ message: `duplicate dependency plugin id '${depId}'`
3589
+ });
3590
+ } else {
3591
+ seen.add(depId);
3592
+ }
3593
+ }
3594
+ }
3595
+ });
3596
+ function parseKayPluginManifestV1(value) {
3597
+ return KayPluginManifestV1.parse(value);
3598
+ }
3599
+ function kayUiEventSubscriptionsFromManifest(manifest) {
3600
+ return manifest.targets?.kay?.ui?.contributes?.eventSubscriptions ?? [];
3601
+ }
3602
+ function validateKayUiEventSubscription(event, definedPluginEvents) {
3603
+ if (event.startsWith("self:")) {
3604
+ const localId = event.slice("self:".length);
3605
+ if (!definedPluginEvents.has(localId)) {
3606
+ return {
3607
+ valid: false,
3608
+ message: `ui event subscription references unknown plugin event ${event}`
3609
+ };
3610
+ }
3611
+ return { valid: true };
3612
+ }
3613
+ if (event.startsWith("native:")) {
3614
+ if (!KAY_UI_NATIVE_EVENT_ALLOWLIST.has(event)) {
3615
+ return {
3616
+ valid: false,
3617
+ message: `ui event subscription references unsupported native event ${event}`
3618
+ };
3619
+ }
3620
+ return { valid: true };
3621
+ }
3622
+ return {
3623
+ valid: false,
3624
+ message: "ui event subscriptions must reference self:<event-local-id> or an allowlisted native:<event>"
3625
+ };
3626
+ }
3627
+ function reportDuplicateLocalIds(ctx, path, contributions, label) {
3628
+ const seen = /* @__PURE__ */ new Set();
3629
+ for (const [index, contribution] of (contributions ?? []).entries()) {
3630
+ if (!seen.has(contribution.localId)) {
3631
+ seen.add(contribution.localId);
3632
+ continue;
3633
+ }
3634
+ ctx.addIssue({
3635
+ code: "custom",
3636
+ path: [...path, index, "localId"],
3637
+ message: `duplicate ${label} local id ${contribution.localId}`
3638
+ });
3639
+ }
3640
+ }
3641
+ function collectKayAppContributionIds(manifest) {
3642
+ const contributes = manifest.targets?.kay?.app?.contributes;
3643
+ const output = [];
3644
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "tools"], contributes?.tools);
3645
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "endpoints"], contributes?.endpoints);
3646
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "uriModules"], contributes?.uriModules);
3647
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "dataSources"], contributes?.dataSources);
3648
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "whiteboardTypes"], contributes?.whiteboardTypes);
3649
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "eventDefinitions"], contributes?.eventDefinitions);
3650
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "eventSubscriptions"], contributes?.eventSubscriptions);
3651
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "deviceDelegations"], contributes?.deviceDelegations);
3652
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "corpusConsumers"], contributes?.corpusConsumers);
3653
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "contextContributors"], contributes?.contextContributors);
3654
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "mcpServers"], contributes?.mcpServers && Object.keys(contributes.mcpServers).map((localId) => ({ localId })));
3655
+ pushContributionIds(output, ["targets", "kay", "app", "contributes", "commands"], contributes?.commands);
3656
+ return output;
3657
+ }
3658
+ function collectDeclaredContributionIds(manifest) {
3659
+ const output = [];
3660
+ const agent = manifest.targets?.kay?.agent?.contributes;
3661
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "tools"], agent?.tools);
3662
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "converters"], agent?.converters);
3663
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "uriModules"], agent?.uriModules);
3664
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "artifactUriModules"], agent?.artifactUriModules);
3665
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "cliBindings"], agent?.cliBindings);
3666
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "resources"], agent?.resources);
3667
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "skills"], agent?.skills);
3668
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "searchProviders"], agent?.searchProviders);
3669
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "entityResolvers"], agent?.entityResolvers);
3670
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "renderers"], agent?.renderers);
3671
+ pushContributionIds(output, ["targets", "kay", "agent", "contributes", "commands"], agent?.commands);
3672
+ output.push(...collectKayAppContributionIds(manifest));
3673
+ const ui = manifest.targets?.kay?.ui?.contributes;
3674
+ pushContributionIds(output, ["targets", "kay", "ui", "contributes", "commands"], ui?.commands);
3675
+ pushContributionIds(output, ["targets", "kay", "ui", "contributes", "slots"], ui?.slots);
3676
+ pushContributionIds(output, ["targets", "kay", "ui", "contributes", "settingsPanels"], ui?.settingsPanels);
3677
+ pushContributionIds(output, ["targets", "kay", "ui", "contributes", "resultRenderers"], ui?.resultRenderers);
3678
+ pushContributionIds(output, ["targets", "kay", "ui", "contributes", "referencePresentations"], ui?.referencePresentations);
3679
+ pushContributionIds(output, ["targets", "kay", "ui", "contributes", "views"], ui?.views);
3680
+ pushContributionIds(output, ["targets", "kay", "ui", "contributes", "apps"], ui?.apps);
3681
+ pushContributionIds(output, ["targets", "kay", "ui", "contributes", "themes"], ui?.themes);
3682
+ pushContributionIds(output, ["targets", "kay", "ui", "contributes", "eventSubscriptions"], ui?.eventSubscriptions);
3683
+ pushContributionIds(output, ["auth", "services"], manifest.auth?.services);
3684
+ return output;
3685
+ }
3686
+ function collectSyntheticContributionIds(manifest) {
3687
+ const output = /* @__PURE__ */ new Set();
3688
+ if (manifest.storage?.sqlite?.enabled) {
3689
+ output.add(resolveStorageContributionLocalId("sqlite"));
3690
+ }
3691
+ if (manifest.storage?.dataDir?.enabled) {
3692
+ output.add(resolveStorageContributionLocalId("dataDir"));
3693
+ }
3694
+ for (const section of configSectionKeysForManifest(manifest)) {
3695
+ output.add(resolveConfigSectionContributionLocalId(section));
3696
+ }
3697
+ return output;
3698
+ }
3699
+ function pushContributionIds(output, path, items) {
3700
+ for (const [index, item] of (items ?? []).entries()) {
3701
+ output.push({ localId: item.localId, path: [...path, index, "localId"] });
3702
+ }
3703
+ }
3704
+
3705
+ // packages/plugin-protocol/dist/resolved-contract.js
3706
+ import { z as z9 } from "zod";
3707
+ var ResolvedPluginContributionId = z9.strictObject({
3708
+ target: PluginTargetName,
3709
+ kind: PluginContributionKind,
3710
+ localId: LocalContributionId,
3711
+ globalId: GlobalContributionId
3712
+ });
3713
+ var ResolvedKayAgentTool = z9.strictObject({
3714
+ localId: LocalContributionId,
3715
+ globalId: GlobalContributionId,
3716
+ toolName: PluginToolName
3717
+ });
3718
+ var ResolvedKayAppTool = ResolvedKayAgentTool;
3719
+ var ResolvedKayAppDataSource = z9.strictObject({
3720
+ localId: LocalContributionId,
3721
+ globalId: GlobalContributionId,
3722
+ scope: z9.enum(["runtime", "account", "session"]),
3723
+ snapshot: z9.strictObject({
3724
+ version: z9.number().int().positive(),
3725
+ schema: z9.record(z9.string(), z9.unknown())
3726
+ }),
3727
+ change: z9.strictObject({
3728
+ version: z9.number().int().positive(),
3729
+ schema: z9.record(z9.string(), z9.unknown()),
3730
+ semantics: z9.discriminatedUnion("kind", [
3731
+ z9.strictObject({ kind: z9.literal("incremental") }),
3732
+ z9.strictObject({
3733
+ kind: z9.literal("snapshot-replacement"),
3734
+ maxEncodedBytes: z9.number().int().positive(),
3735
+ rationale: z9.string().trim().min(1).max(1024)
3736
+ })
3737
+ ]).optional()
3738
+ }),
3739
+ read: AppDataSourceRead.optional()
3740
+ }).superRefine((source, ctx) => {
3741
+ if (source.read !== void 0 && source.scope !== "session") {
3742
+ ctx.addIssue({
3743
+ code: "custom",
3744
+ path: ["read"],
3745
+ message: "Resolved app-data read operations require session scope."
3746
+ });
3747
+ }
3748
+ });
3749
+ var ResolvedKayAppWhiteboardType = z9.strictObject({
3750
+ localId: LocalContributionId,
3751
+ globalId: GlobalContributionId,
3752
+ displayName: NonEmptyString,
3753
+ schemaVersion: z9.number().int().positive(),
3754
+ storage: WhiteboardTypeStorage,
3755
+ valueSchema: JsonSchema,
3756
+ presentation: z9.strictObject({
3757
+ app: GlobalContributionId
3758
+ }).optional()
3759
+ });
3760
+ var ResolvedKayAppMcpServer = z9.strictObject({
3761
+ name: LocalContributionId,
3762
+ pluginId: PluginId,
3763
+ url: McpServerUrl,
3764
+ auth: McpServerAuthMode,
3765
+ authServiceId: GlobalContributionId.optional()
3766
+ });
3767
+ var ResolvedKayAgentUriModule = z9.strictObject({
3768
+ localId: LocalContributionId,
3769
+ globalId: GlobalContributionId,
3770
+ schemes: z9.array(UriScheme).min(1),
3771
+ filesystemBacking: UriFilesystemBacking.optional(),
3772
+ verbs: z9.strictObject({
3773
+ writable: z9.boolean().optional(),
3774
+ editable: z9.boolean().optional(),
3775
+ deletable: z9.boolean().optional()
3776
+ }).optional(),
3777
+ /** Mirrors `UriModuleContribution.knownResources` (host-side recording opt-in). */
3778
+ knownResources: z9.literal(true).optional()
3779
+ }).superRefine((module, ctx) => {
3780
+ if (module.verbs !== void 0 && module.filesystemBacking?.root !== "filesystem") {
3781
+ ctx.addIssue({
3782
+ code: "custom",
3783
+ path: ["verbs"],
3784
+ message: "Resolved URI mutation verbs require external filesystem backing (root: 'filesystem')."
3785
+ });
3786
+ }
3787
+ });
3788
+ var ResolvedKayAgentArtifactUriModule = z9.strictObject({
3789
+ localId: LocalContributionId,
3790
+ globalId: GlobalContributionId,
3791
+ scheme: UriScheme,
3792
+ editable: z9.boolean(),
3793
+ listable: z9.boolean(),
3794
+ deletable: z9.boolean(),
3795
+ appendable: z9.boolean(),
3796
+ storageScope: z9.enum(["session", "family"])
3797
+ });
3798
+ var ResolvedKayAgentEntityResolver = z9.strictObject({
3799
+ localId: LocalContributionId,
3800
+ globalId: GlobalContributionId,
3801
+ displayName: NonEmptyString.optional(),
3802
+ uriModule: GlobalContributionId,
3803
+ entityTypes: z9.array(NonEmptyString).min(1),
3804
+ uriHint: NonEmptyString
3805
+ });
3806
+ var ResolvedKayAgentGitHostClaim = z9.strictObject({
3807
+ host: GitHostName,
3808
+ authServiceId: GlobalContributionId,
3809
+ /** Manifest-declared order; the clone ladder tries them in sequence. */
3810
+ authSchemeIds: z9.array(GlobalContributionId).min(1),
3811
+ /** Non-secret display copy for the claiming service, for clone failure text. */
3812
+ authServiceDisplayName: z9.string(),
3813
+ basicAuthUser: GitBasicAuthUser
3814
+ });
3815
+ var ResolvedKayAgentConverter = z9.strictObject({
3816
+ localId: LocalContributionId,
3817
+ globalId: GlobalContributionId,
3818
+ mimeTypes: z9.array(MimeTypeString).min(1),
3819
+ extensions: z9.array(FileExtensionToken).min(1).optional()
3820
+ });
3821
+ var ResolvedReferencePresentation = z9.strictObject({
3822
+ localId: LocalContributionId,
3823
+ globalId: GlobalContributionId,
3824
+ uriModule: GlobalContributionId,
3825
+ resourceKind: NonEmptyString,
3826
+ open: ReferenceOpenBehavior,
3827
+ /** MIME-specific resource kinds; absent keys use the base presentation. */
3828
+ variants: ReferencePresentationVariants.optional(),
3829
+ claims: z9.array(ReferencePresentationUrlClaim).optional()
3830
+ });
3831
+ var ResolvedPluginProvisioningRequirement = z9.strictObject({
3832
+ id: LocalContributionId,
3833
+ displayName: NonEmptyString,
3834
+ description: NonEmptyString,
3835
+ /**
3836
+ * Where the user completes the setup. Optional mirror of the manifest field: a per-environment
3837
+ * URL the broker supplies at runtime, so a requirement with neither a declared nor a resolved
3838
+ * URL degrades to "Reconnect" in the UI instead of a broken link.
3839
+ */
3840
+ remediationUrl: HttpsUrl.optional(),
3841
+ probe: z9.strictObject({
3842
+ url: HttpsUrl,
3843
+ method: z9.enum(["GET"]),
3844
+ verdictStatuses: z9.array(SignedInStatusCode).min(1),
3845
+ nonEmptyArrayAt: NonEmptyString.optional()
3846
+ })
3847
+ });
3848
+ var ResolvedProvisioningField = {
3849
+ provisioning: z9.array(ResolvedPluginProvisioningRequirement).optional()
3850
+ };
3851
+ var ResolvedPluginAuthSchemeDescriptionFields = {
3852
+ displayName: NonEmptyString.optional(),
3853
+ agentHint: NonEmptyString.optional(),
3854
+ summary: NonEmptyString.optional()
3855
+ };
3856
+ var ResolvedPluginAuthSchemeOAuthBase = {
3857
+ ...ResolvedProvisioningField,
3858
+ ...ResolvedPluginAuthSchemeDescriptionFields,
3859
+ localId: LocalContributionId,
3860
+ globalId: GlobalContributionId,
3861
+ kind: z9.literal("oauth2"),
3862
+ scopes: z9.array(z9.string()).optional()
3863
+ };
3864
+ var ResolvedPluginAuthSchemeOAuthPkce = z9.strictObject({
3865
+ ...ResolvedPluginAuthSchemeOAuthBase,
3866
+ exchange: z9.literal("pkce"),
3867
+ clientSuppliers: z9.array(PluginOAuthClientSupplier).nonempty()
3868
+ });
3869
+ var ResolvedPluginAuthSchemeOAuthConfidential = z9.strictObject({
3870
+ ...ResolvedPluginAuthSchemeOAuthBase,
3871
+ exchange: z9.literal("confidential"),
3872
+ clientSuppliers: z9.tuple([z9.literal("kay")])
3873
+ });
3874
+ var ResolvedPluginAuthSchemeOAuth2 = z9.discriminatedUnion("exchange", [
3875
+ ResolvedPluginAuthSchemeOAuthPkce,
3876
+ ResolvedPluginAuthSchemeOAuthConfidential
3877
+ ]);
3878
+ var ResolvedPluginAuthSchemeApiKey = z9.strictObject({
3879
+ ...ResolvedProvisioningField,
3880
+ ...ResolvedPluginAuthSchemeDescriptionFields,
3881
+ localId: LocalContributionId,
3882
+ globalId: GlobalContributionId,
3883
+ kind: z9.literal("api-key"),
3884
+ scopes: z9.array(z9.string()).optional()
3885
+ });
3886
+ var ResolvedPluginAuthSchemeDelegated = z9.strictObject({
3887
+ ...ResolvedPluginAuthSchemeDescriptionFields,
3888
+ localId: LocalContributionId,
3889
+ globalId: GlobalContributionId,
3890
+ kind: z9.literal("delegated"),
3891
+ scopes: z9.array(z9.string()).optional()
3892
+ });
3893
+ var ResolvedPluginAuthSchemeExternalCli = z9.strictObject({
3894
+ ...ResolvedPluginAuthSchemeDescriptionFields,
3895
+ localId: LocalContributionId,
3896
+ globalId: GlobalContributionId,
3897
+ kind: z9.literal("external-cli"),
3898
+ scopes: z9.array(z9.string()).optional()
3899
+ });
3900
+ var ResolvedPluginAuthScheme = z9.discriminatedUnion("kind", [
3901
+ ResolvedPluginAuthSchemeOAuth2,
3902
+ ResolvedPluginAuthSchemeApiKey,
3903
+ ResolvedPluginAuthSchemeDelegated,
3904
+ ResolvedPluginAuthSchemeExternalCli
3905
+ ]);
3906
+ var ResolvedPluginWebSession = z9.strictObject({
3907
+ importance: z9.enum(["required", "recommended"]),
3908
+ loginUrl: HttpsUrl,
3909
+ cookieDomains: z9.array(RegistrableDomain).min(1),
3910
+ probe: z9.strictObject({
3911
+ url: HttpsUrl,
3912
+ method: z9.enum(["GET", "POST"]),
3913
+ signedInStatuses: z9.array(SignedInStatusCode).min(1)
3914
+ }),
3915
+ oauthVenue: z9.enum(["embedded", "external"])
3916
+ }).superRefine(refineWebSessionScopeConfined);
3917
+ var ResolvedPluginAuthService = z9.strictObject({
3918
+ localId: LocalContributionId,
3919
+ globalId: GlobalContributionId,
3920
+ displayName: z9.string(),
3921
+ schemes: z9.array(ResolvedPluginAuthScheme).min(1),
3922
+ purposes: z9.array(z9.string()).min(1),
3923
+ /** The service's browser half, when the manifest declares one. */
3924
+ webSession: ResolvedPluginWebSession.optional()
3925
+ });
3926
+ var ResolvedKayUiAppEndpointCapability = z9.strictObject({
3927
+ localId: LocalContributionId,
3928
+ globalId: GlobalContributionId
3929
+ });
3930
+ var ResolvedKayUiApp = z9.strictObject({
3931
+ localId: LocalContributionId,
3932
+ globalId: GlobalContributionId,
3933
+ displayName: NonEmptyString,
3934
+ description: NonEmptyString.optional(),
3935
+ icon: PackageAssetPath.optional(),
3936
+ runtime: z9.literal("kay.react-app.v1"),
3937
+ sdk: z9.strictObject({ range: NonEmptyString }),
3938
+ entrypoint: TargetEntrypoint,
3939
+ styles: z9.array(PackageAssetPath).optional(),
3940
+ assets: z9.array(PackageAssetPath).optional(),
3941
+ capabilities: z9.strictObject({
3942
+ endpoints: z9.array(ResolvedKayUiAppEndpointCapability).optional(),
3943
+ host: z9.array(KayUiAppHostCapability).optional()
3944
+ }).optional()
3945
+ });
3946
+ var ResolvedPluginContractV1 = z9.strictObject({
3947
+ protocol: z9.literal("kay.plugin.v1"),
3948
+ pluginId: PluginId,
3949
+ namespace: PluginNamespace,
3950
+ contributions: z9.array(ResolvedPluginContributionId),
3951
+ // The deduplicated set of qualified (dotted) external auth-service ids this
3952
+ // plugin references, from agent uriModule `authService` refs, cliBinding
3953
+ // `authService` refs, and auth-grant `service` refs. Always present — a plugin
3954
+ // that only references its own local services carries an empty array. This
3955
+ // is a recorded dependency, not a resolution: there is no runtime plugin
3956
+ // loader in v1, so nothing here verifies the reference resolves within a
3957
+ // composed roster (a composition-time concern owned elsewhere).
3958
+ consumesAuthServices: z9.array(GlobalContributionId),
3959
+ // The explicit plugin-level dependency ids declared in `dependencies.plugins` on the manifest,
3960
+ // deduped and validated at parse time. Always present; a plugin with no explicit dependencies
3961
+ // carries an empty array. Composition checks each id against the accepted roster and derives the
3962
+ // full dependency graph together with auth-inferred edges from `consumesAuthServices`.
3963
+ explicitDependencies: z9.array(PluginId),
3964
+ // The auth services this plugin OWNS, carrying the global ids its harness
3965
+ // target must register and the credential authority keys on. Always present;
3966
+ // a plugin that declares no `auth.services` carries an empty array. The dual
3967
+ // of `consumesAuthServices` (services it merely references).
3968
+ authServices: z9.array(ResolvedPluginAuthService),
3969
+ grants: ResolvedPluginGrants.optional(),
3970
+ kayAgentUriModules: z9.array(ResolvedKayAgentUriModule).optional(),
3971
+ kayAgentArtifactUriModules: z9.array(ResolvedKayAgentArtifactUriModule).optional(),
3972
+ kayAgentEntityResolvers: z9.array(ResolvedKayAgentEntityResolver).optional(),
3973
+ kayAgentGitHostClaims: z9.array(ResolvedKayAgentGitHostClaim).optional(),
3974
+ kayAgentTools: z9.array(ResolvedKayAgentTool).optional(),
3975
+ kayAppTools: z9.array(ResolvedKayAppTool).optional(),
3976
+ kayAppDataSources: z9.array(ResolvedKayAppDataSource).optional(),
3977
+ kayAppWhiteboardTypes: z9.array(ResolvedKayAppWhiteboardType).optional(),
3978
+ kayAppMcpServers: z9.array(ResolvedKayAppMcpServer).optional(),
3979
+ kayAgentConverters: z9.array(ResolvedKayAgentConverter).optional(),
3980
+ referencePresentations: z9.array(ResolvedReferencePresentation).optional(),
3981
+ kayUiApps: z9.array(ResolvedKayUiApp).optional()
3982
+ });
3983
+ function narrowestAccountSelectors() {
3984
+ return [{ kind: "active" }];
3985
+ }
3986
+ var KayPluginContractResolutionError = class extends Error {
3987
+ constructor(message) {
3988
+ super(message);
3989
+ this.name = "KayPluginContractResolutionError";
3990
+ }
3991
+ };
3992
+ function parseResolvedPluginContractV1(value) {
3993
+ return ResolvedPluginContractV1.parse(value);
3994
+ }
3995
+ function pushAll(output, target, kind, items) {
3996
+ for (const item of items ?? [])
3997
+ output.push({ target, kind, localId: item.localId });
3998
+ }
3999
+ function collectContributionSources(manifest) {
4000
+ const output = [];
4001
+ const agent = manifest.targets?.kay?.agent?.contributes;
4002
+ pushAll(output, "kay.agent", "tool", agent?.tools);
4003
+ pushAll(output, "kay.agent", "converter", agent?.converters);
4004
+ pushAll(output, "kay.agent", "uriModule", agent?.uriModules);
4005
+ pushAll(output, "kay.agent", "artifactUriModule", agent?.artifactUriModules);
4006
+ pushAll(output, "kay.agent", "cliBinding", agent?.cliBindings);
4007
+ pushAll(output, "kay.agent", "resource", agent?.resources);
4008
+ pushAll(output, "kay.agent", "skill", agent?.skills);
4009
+ pushAll(output, "kay.agent", "searchProvider", agent?.searchProviders);
4010
+ pushAll(output, "kay.agent", "entityResolver", agent?.entityResolvers);
4011
+ pushAll(output, "kay.agent", "agentRenderer", agent?.renderers);
4012
+ pushAll(output, "kay.agent", "command", agent?.commands);
4013
+ const app = manifest.targets?.kay?.app?.contributes;
4014
+ pushAll(output, "kay.app", "tool", app?.tools);
4015
+ pushAll(output, "kay.app", "endpoint", app?.endpoints);
4016
+ pushAll(output, "kay.app", "uriModule", app?.uriModules);
4017
+ pushAll(output, "kay.app", "appDataSource", app?.dataSources);
4018
+ pushAll(output, "kay.app", "whiteboardType", app?.whiteboardTypes);
4019
+ pushAll(output, "kay.app", "command", app?.commands);
4020
+ pushAll(output, "kay.app", "eventDefinition", app?.eventDefinitions);
4021
+ pushAll(output, "kay.app", "eventSubscription", app?.eventSubscriptions);
4022
+ pushAll(output, "kay.app", "deviceDelegation", app?.deviceDelegations);
4023
+ pushAll(output, "kay.app", "corpusConsumer", app?.corpusConsumers);
4024
+ pushAll(output, "kay.app", "contextContributor", app?.contextContributors);
4025
+ pushAll(output, "kay.app", "mcpServer", app?.mcpServers && Object.keys(app.mcpServers).map((name) => ({ localId: name })));
4026
+ const ui = manifest.targets?.kay?.ui?.contributes;
4027
+ pushAll(output, "kay.ui", "uiCommand", ui?.commands);
4028
+ pushAll(output, "kay.ui", "uiSlot", ui?.slots);
4029
+ pushAll(output, "kay.ui", "settingsPanel", ui?.settingsPanels);
4030
+ pushAll(output, "kay.ui", "resultRenderer", ui?.resultRenderers);
4031
+ pushAll(output, "kay.ui", "referencePresentation", ui?.referencePresentations);
4032
+ pushAll(output, "kay.ui", "view", ui?.views);
4033
+ pushAll(output, "kay.ui", "app", ui?.apps);
4034
+ pushAll(output, "kay.ui", "theme", ui?.themes);
4035
+ pushAll(output, "kay.ui", "eventSubscription", ui?.eventSubscriptions);
4036
+ pushAll(output, "kay.app", "authService", manifest.auth?.services);
4037
+ if (manifest.storage?.sqlite?.enabled) {
4038
+ output.push({
4039
+ target: "kay.app",
4040
+ kind: "storage",
4041
+ localId: resolveStorageContributionLocalId("sqlite")
4042
+ });
4043
+ }
4044
+ if (manifest.storage?.dataDir?.enabled) {
4045
+ output.push({
4046
+ target: "kay.app",
4047
+ kind: "storage",
4048
+ localId: resolveStorageContributionLocalId("dataDir")
4049
+ });
4050
+ }
4051
+ for (const section of configSectionKeysForManifest(manifest)) {
4052
+ output.push({
4053
+ target: "kay.app",
4054
+ kind: "configSection",
4055
+ localId: resolveConfigSectionContributionLocalId(section)
4056
+ });
4057
+ }
4058
+ return output;
4059
+ }
4060
+ function resolveGrantSet(namespace, targetGrants) {
4061
+ const result = {};
4062
+ if (targetGrants?.auth) {
4063
+ result.auth = targetGrants.auth.map((grant) => {
4064
+ const serviceOwnerNamespace = isQualifiedAuthServiceRef(grant.service) ? PluginNamespace.parse(grant.service.slice(0, grant.service.lastIndexOf("."))) : namespace;
4065
+ return {
4066
+ ...grant,
4067
+ // A qualified ref already names another plugin's global id and is kept
4068
+ // as-is; only a local ref is minted under this plugin's own namespace.
4069
+ service: isQualifiedAuthServiceRef(grant.service) ? grant.service : resolveGlobalContributionId(namespace, grant.service),
4070
+ ...grant.schemes === void 0 ? {} : {
4071
+ schemes: grant.schemes.map((scheme) => isQualifiedAuthSchemeRef(scheme) ? scheme : resolveGlobalContributionId(serviceOwnerNamespace, scheme))
4072
+ },
4073
+ accountSelectors: grant.accountSelectors ?? narrowestAccountSelectors()
4074
+ };
4075
+ });
4076
+ }
4077
+ if (targetGrants?.storage) {
4078
+ result.storage = targetGrants.storage;
4079
+ }
4080
+ if (targetGrants?.events) {
4081
+ result.events = targetGrants.events;
4082
+ }
4083
+ if (targetGrants?.endpoints) {
4084
+ result.endpoints = targetGrants.endpoints.map((grant) => ({
4085
+ ...grant,
4086
+ endpoint: resolveGlobalContributionId(namespace, grant.endpoint)
4087
+ }));
4088
+ }
4089
+ if (targetGrants?.webSession) {
4090
+ result.webSession = targetGrants.webSession;
4091
+ }
4092
+ if (targetGrants?.knowledgeSearch) {
4093
+ result.knowledgeSearch = targetGrants.knowledgeSearch;
4094
+ }
4095
+ if (targetGrants?.knownResources) {
4096
+ result.knownResources = targetGrants.knownResources;
4097
+ }
4098
+ return result;
4099
+ }
4100
+ function hasAnyGrant(set) {
4101
+ return Object.values(set).some((value) => Array.isArray(value) && value.length > 0);
4102
+ }
4103
+ function collectConsumedAuthServiceIds(manifest) {
4104
+ const output = /* @__PURE__ */ new Set();
4105
+ const add = (ref) => {
4106
+ if (ref && isQualifiedAuthServiceRef(ref))
4107
+ output.add(ref);
4108
+ };
4109
+ for (const module of manifest.targets?.kay?.agent?.contributes?.uriModules ?? []) {
4110
+ add(module.authService);
4111
+ }
4112
+ for (const binding of manifest.targets?.kay?.agent?.contributes?.cliBindings ?? []) {
4113
+ add(binding.authService);
4114
+ }
4115
+ for (const target of KAY_PLUGIN_TARGET_NAMES) {
4116
+ for (const grant of manifest.permissions?.byTarget[target]?.auth ?? []) {
4117
+ add(grant.service);
4118
+ }
4119
+ }
4120
+ return Array.from(output);
4121
+ }
4122
+ function resolveAuthServices(namespace, manifest) {
4123
+ const seenSchemeIds = /* @__PURE__ */ new Map();
4124
+ return (manifest.auth?.services ?? []).map((service) => ({
4125
+ localId: service.localId,
4126
+ globalId: resolveGlobalContributionId(namespace, service.localId),
4127
+ displayName: service.displayName,
4128
+ purposes: [...service.purposes],
4129
+ ...service.webSession === void 0 ? {} : {
4130
+ webSession: {
4131
+ importance: service.webSession.importance,
4132
+ loginUrl: service.webSession.loginUrl,
4133
+ cookieDomains: [...service.webSession.cookieDomains],
4134
+ probe: {
4135
+ url: service.webSession.probe.url,
4136
+ // Absent means GET; resolved carries the applied default so no
4137
+ // downstream consumer re-defaults it.
4138
+ method: service.webSession.probe.method ?? "GET",
4139
+ signedInStatuses: [...service.webSession.probe.signedInStatuses]
4140
+ },
4141
+ oauthVenue: service.webSession.oauthVenue
4142
+ }
4143
+ },
4144
+ schemes: service.schemes.map((scheme) => {
4145
+ const globalId = resolveGlobalContributionId(namespace, scheme.localId);
4146
+ const owner = seenSchemeIds.get(globalId);
4147
+ if (owner !== void 0) {
4148
+ throw new KayPluginContractResolutionError(owner === service.localId ? `duplicate resolved auth scheme id ${globalId} declared twice by service ${service.localId}` : `duplicate resolved auth scheme id ${globalId} from services ${owner} and ${service.localId}`);
4149
+ }
4150
+ seenSchemeIds.set(globalId, service.localId);
4151
+ const scopesField = scheme.scopes ? { scopes: [...scheme.scopes] } : {};
4152
+ const descriptionFields = {
4153
+ ...scheme.displayName === void 0 ? {} : { displayName: scheme.displayName },
4154
+ ...scheme.agentHint === void 0 ? {} : { agentHint: scheme.agentHint },
4155
+ ...scheme.summary === void 0 ? {} : { summary: scheme.summary }
4156
+ };
4157
+ const provisioningField = "provisioning" in scheme && scheme.provisioning !== void 0 ? {
4158
+ provisioning: scheme.provisioning.map((requirement) => ({
4159
+ id: requirement.id,
4160
+ displayName: requirement.displayName,
4161
+ description: requirement.description,
4162
+ ...requirement.remediationUrl === void 0 ? {} : { remediationUrl: requirement.remediationUrl },
4163
+ probe: {
4164
+ url: requirement.probe.url,
4165
+ method: requirement.probe.method ?? "GET",
4166
+ verdictStatuses: [...requirement.probe.verdictStatuses],
4167
+ ...requirement.probe.nonEmptyArrayAt === void 0 ? {} : { nonEmptyArrayAt: requirement.probe.nonEmptyArrayAt }
4168
+ }
4169
+ }))
4170
+ } : {};
4171
+ if (scheme.kind === "oauth2") {
4172
+ return scheme.exchange === "pkce" ? {
4173
+ localId: scheme.localId,
4174
+ globalId,
4175
+ kind: "oauth2",
4176
+ exchange: "pkce",
4177
+ clientSuppliers: scheme.clientSuppliers,
4178
+ ...scopesField,
4179
+ ...descriptionFields,
4180
+ ...provisioningField
4181
+ } : {
4182
+ localId: scheme.localId,
4183
+ globalId,
4184
+ kind: "oauth2",
4185
+ exchange: "confidential",
4186
+ clientSuppliers: scheme.clientSuppliers ?? ["kay"],
4187
+ ...scopesField,
4188
+ ...descriptionFields,
4189
+ ...provisioningField
4190
+ };
4191
+ }
4192
+ return {
4193
+ localId: scheme.localId,
4194
+ globalId,
4195
+ kind: scheme.kind,
4196
+ ...scopesField,
4197
+ ...descriptionFields,
4198
+ ...provisioningField
4199
+ };
4200
+ })
4201
+ }));
4202
+ }
4203
+ function resolveGitHostClaims(manifest, authServices) {
4204
+ const byLocalId = new Map(authServices.map((service) => [service.localId, service]));
4205
+ return (manifest.targets?.kay?.agent?.contributes?.gitHosts ?? []).map((claim) => {
4206
+ const service = byLocalId.get(claim.authService);
4207
+ if (service === void 0) {
4208
+ throw new KayPluginContractResolutionError(`git host ${claim.host} references auth service ${claim.authService}, which this manifest does not declare`);
4209
+ }
4210
+ return {
4211
+ host: claim.host,
4212
+ authServiceId: service.globalId,
4213
+ authSchemeIds: service.schemes.map((scheme) => scheme.globalId),
4214
+ authServiceDisplayName: service.displayName,
4215
+ basicAuthUser: claim.basicAuthUser
4216
+ };
4217
+ });
4218
+ }
4219
+ function resolveKayPluginContractV1(manifest) {
4220
+ const namespace = resolvePluginNamespace(manifest.id, manifest.namespace);
4221
+ const seenGlobalIds = /* @__PURE__ */ new Map();
4222
+ const contributions = collectContributionSources(manifest).map((source) => {
4223
+ const globalId = resolveGlobalContributionId(namespace, source.localId);
4224
+ const existing = seenGlobalIds.get(globalId);
4225
+ if (existing) {
4226
+ throw new KayPluginContractResolutionError(`duplicate resolved contribution id ${globalId} from ${existing.kind} and ${source.kind}`);
4227
+ }
4228
+ seenGlobalIds.set(globalId, source);
4229
+ return {
4230
+ target: source.target,
4231
+ kind: source.kind,
4232
+ localId: source.localId,
4233
+ globalId
4234
+ };
4235
+ });
4236
+ const byTarget = manifest.permissions?.byTarget;
4237
+ const grantByTarget = {
4238
+ "kay.agent": byTarget?.["kay.agent"] ? resolveGrantSet(namespace, byTarget["kay.agent"]) : void 0,
4239
+ "kay.app": byTarget?.["kay.app"] ? resolveGrantSet(namespace, byTarget["kay.app"]) : void 0,
4240
+ "kay.ui": byTarget?.["kay.ui"] ? resolveGrantSet(namespace, byTarget["kay.ui"]) : void 0
4241
+ };
4242
+ const grants = {
4243
+ byTarget: Object.fromEntries(Object.entries(grantByTarget).filter(([, set]) => set && hasAnyGrant(set)))
4244
+ };
4245
+ const kayAgentTools = manifest.targets?.kay?.agent?.contributes?.tools?.map((tool) => ({
4246
+ localId: tool.localId,
4247
+ globalId: resolveGlobalContributionId(namespace, tool.localId),
4248
+ toolName: tool.toolName
4249
+ }));
4250
+ const kayAppTools = manifest.targets?.kay?.app?.contributes?.tools?.map((tool) => ({
4251
+ localId: tool.localId,
4252
+ globalId: resolveGlobalContributionId(namespace, tool.localId),
4253
+ toolName: tool.toolName
4254
+ }));
4255
+ const kayAppDataSources = manifest.targets?.kay?.app?.contributes?.dataSources?.map((source) => ({
4256
+ localId: source.localId,
4257
+ globalId: resolveGlobalContributionId(namespace, source.localId),
4258
+ scope: source.scope,
4259
+ snapshot: source.snapshot,
4260
+ change: source.change,
4261
+ ...source.read === void 0 ? {} : { read: source.read }
4262
+ }));
4263
+ const kayAppWhiteboardTypes = manifest.targets?.kay?.app?.contributes?.whiteboardTypes?.map((type) => ({
4264
+ localId: type.localId,
4265
+ globalId: resolveGlobalContributionId(namespace, type.localId),
4266
+ displayName: type.displayName,
4267
+ schemaVersion: type.schemaVersion,
4268
+ storage: type.storage,
4269
+ valueSchema: type.valueSchema,
4270
+ ...type.presentation === void 0 ? {} : {
4271
+ presentation: {
4272
+ app: resolveGlobalContributionId(namespace, type.presentation.app)
4273
+ }
4274
+ }
4275
+ }));
4276
+ const kayAppMcpServers = Object.entries(manifest.targets?.kay?.app?.contributes?.mcpServers ?? {}).map(([name, server]) => ({
4277
+ name,
4278
+ pluginId: manifest.id,
4279
+ url: server.url,
4280
+ auth: server.auth ?? "oauth",
4281
+ ...server.authService === void 0 ? {} : {
4282
+ authServiceId: resolveGlobalContributionId(namespace, server.authService)
4283
+ }
4284
+ }));
4285
+ const kayAgentUriModules = manifest.targets?.kay?.agent?.contributes?.uriModules?.map((module) => ({
4286
+ localId: module.localId,
4287
+ globalId: resolveGlobalContributionId(namespace, module.localId),
4288
+ schemes: [...module.schemes],
4289
+ ...module.filesystemBacking === void 0 ? {} : { filesystemBacking: module.filesystemBacking },
4290
+ ...module.verbs === void 0 ? {} : { verbs: module.verbs },
4291
+ ...module.knownResources === void 0 ? {} : { knownResources: module.knownResources }
4292
+ }));
4293
+ const kayAgentArtifactUriModules = manifest.targets?.kay?.agent?.contributes?.artifactUriModules?.map((module) => ({
4294
+ localId: module.localId,
4295
+ globalId: resolveGlobalContributionId(namespace, module.localId),
4296
+ scheme: module.scheme,
4297
+ editable: module.editable ?? false,
4298
+ listable: module.listable ?? false,
4299
+ deletable: module.deletable ?? false,
4300
+ appendable: module.appendable ?? false,
4301
+ storageScope: module.storageScope ?? "session"
4302
+ }));
4303
+ const authServices = resolveAuthServices(namespace, manifest);
4304
+ const kayAgentGitHostClaims = resolveGitHostClaims(manifest, authServices);
4305
+ const kayAgentEntityResolvers = manifest.targets?.kay?.agent?.contributes?.entityResolvers?.map((resolver) => ({
4306
+ localId: resolver.localId,
4307
+ globalId: resolveGlobalContributionId(namespace, resolver.localId),
4308
+ ...resolver.displayName === void 0 ? {} : { displayName: resolver.displayName },
4309
+ uriModule: resolveGlobalContributionId(namespace, resolver.uriModule),
4310
+ entityTypes: [...resolver.entityTypes],
4311
+ uriHint: resolver.uriHint
4312
+ }));
4313
+ const kayAgentConverters = manifest.targets?.kay?.agent?.contributes?.converters?.map((converter) => ({
4314
+ localId: converter.localId,
4315
+ globalId: resolveGlobalContributionId(namespace, converter.localId),
4316
+ mimeTypes: [...converter.mimeTypes],
4317
+ ...converter.extensions ? { extensions: [...converter.extensions] } : {}
4318
+ }));
4319
+ const referencePresentations = manifest.targets?.kay?.ui?.contributes?.referencePresentations?.map((presentation) => ({
4320
+ localId: presentation.localId,
4321
+ globalId: resolveGlobalContributionId(namespace, presentation.localId),
4322
+ uriModule: resolveGlobalContributionId(namespace, presentation.uriModule),
4323
+ resourceKind: presentation.resourceKind,
4324
+ open: presentation.open,
4325
+ ...presentation.variants ? {
4326
+ variants: Object.fromEntries(Object.entries(presentation.variants).map(([mimeType, variant]) => [mimeType, { ...variant }]))
4327
+ } : {},
4328
+ ...presentation.claims === void 0 ? {} : {
4329
+ claims: presentation.claims.map((claim) => ({
4330
+ ...claim,
4331
+ pattern: { ...claim.pattern }
4332
+ }))
4333
+ }
4334
+ }));
4335
+ const kayUiApps = manifest.targets?.kay?.ui?.contributes?.apps?.map((app) => ({
4336
+ localId: app.localId,
4337
+ globalId: resolveGlobalContributionId(namespace, app.localId),
4338
+ displayName: app.displayName,
4339
+ ...app.description === void 0 ? {} : { description: app.description },
4340
+ ...app.icon === void 0 ? {} : { icon: app.icon },
4341
+ runtime: app.runtime,
4342
+ sdk: app.sdk,
4343
+ entrypoint: app.entrypoint,
4344
+ ...app.styles === void 0 ? {} : { styles: [...app.styles] },
4345
+ ...app.assets === void 0 ? {} : { assets: [...app.assets] },
4346
+ ...app.capabilities === void 0 ? {} : {
4347
+ capabilities: {
4348
+ ...app.capabilities.endpoints === void 0 ? {} : {
4349
+ endpoints: app.capabilities.endpoints.map((endpoint) => ({
4350
+ localId: endpoint,
4351
+ globalId: resolveGlobalContributionId(namespace, endpoint)
4352
+ }))
4353
+ },
4354
+ ...app.capabilities.host === void 0 ? {} : { host: [...app.capabilities.host] }
4355
+ }
4356
+ }
4357
+ }));
4358
+ return {
4359
+ protocol: "kay.plugin.v1",
4360
+ pluginId: manifest.id,
4361
+ namespace,
4362
+ contributions,
4363
+ consumesAuthServices: collectConsumedAuthServiceIds(manifest),
4364
+ // Dedup preserving first-occurrence order; manifest superRefine already
4365
+ // rejected self-references and duplicates, so this mirrors the validated array.
4366
+ explicitDependencies: [
4367
+ ...manifest.dependencies?.plugins ?? []
4368
+ ],
4369
+ authServices,
4370
+ ...grants && Object.keys(grants.byTarget).length > 0 ? { grants } : {},
4371
+ ...kayAgentUriModules && kayAgentUriModules.length > 0 ? { kayAgentUriModules } : {},
4372
+ ...kayAgentArtifactUriModules && kayAgentArtifactUriModules.length > 0 ? { kayAgentArtifactUriModules } : {},
4373
+ ...kayAgentEntityResolvers && kayAgentEntityResolvers.length > 0 ? { kayAgentEntityResolvers } : {},
4374
+ ...kayAgentGitHostClaims.length > 0 ? { kayAgentGitHostClaims } : {},
4375
+ ...kayAgentTools && kayAgentTools.length > 0 ? { kayAgentTools } : {},
4376
+ ...kayAppTools && kayAppTools.length > 0 ? { kayAppTools } : {},
4377
+ ...kayAppDataSources && kayAppDataSources.length > 0 ? { kayAppDataSources } : {},
4378
+ ...kayAppWhiteboardTypes && kayAppWhiteboardTypes.length > 0 ? { kayAppWhiteboardTypes } : {},
4379
+ ...kayAppMcpServers.length > 0 ? { kayAppMcpServers } : {},
4380
+ ...kayAgentConverters && kayAgentConverters.length > 0 ? { kayAgentConverters } : {},
4381
+ ...referencePresentations && referencePresentations.length > 0 ? { referencePresentations } : {},
4382
+ ...kayUiApps && kayUiApps.length > 0 ? { kayUiApps } : {}
4383
+ };
4384
+ }
4385
+ function safeResolveKayPluginContractV1(manifest) {
4386
+ try {
4387
+ return {
4388
+ success: true,
4389
+ data: resolveKayPluginContractV1(manifest)
4390
+ };
4391
+ } catch (error) {
4392
+ if (error instanceof KayPluginContractResolutionError) {
4393
+ return { success: false, error };
4394
+ }
4395
+ throw error;
4396
+ }
4397
+ }
4398
+
4399
+ // packages/plugin-protocol/dist/session-health.js
4400
+ import { z as z10 } from "zod";
4401
+ var SESSION_HEALTH_EXTENSION_ID = "kay.session-health";
4402
+ var SESSION_HEALTH_ENTRY_NAME = "session_health";
4403
+ var SESSION_HEALTH_CUSTOM_TYPE = `ext:${SESSION_HEALTH_EXTENSION_ID}:${SESSION_HEALTH_ENTRY_NAME}`;
4404
+ var SESSION_HEALTH_CLASSIFIER_IDS = ["jev"];
4405
+ var sessionHealthEntrySchema = z10.object({
4406
+ v: z10.literal(1),
4407
+ verdict: z10.enum(["well", "bad", "unknown"]),
4408
+ /**
4409
+ * The model's calibrated confidence in `well`, 0..1, or null when the
4410
+ * classifier declined to produce one. Never exported raw: the telemetry
4411
+ * event carries only the decile bucket.
4412
+ */
4413
+ p_well: z10.number().min(0).max(1).nullable(),
4414
+ classifier: z10.enum(SESSION_HEALTH_CLASSIFIER_IDS),
4415
+ classifier_version: z10.string().min(1).max(64),
4416
+ judged_at: z10.number().int().nonnegative(),
4417
+ user_entry_id: z10.string().min(1).max(256),
4418
+ through_entry_id: z10.string().min(1).max(256),
4419
+ usage: z10.strictObject({
4420
+ input_tokens: z10.number().int().nonnegative(),
4421
+ output_tokens: z10.number().int().nonnegative()
4422
+ }).optional()
4423
+ });
4424
+ function sessionHealthPWellDecile(pWell) {
4425
+ if (pWell === null)
4426
+ return null;
4427
+ return Math.min(9, Math.floor(pWell * 10));
4428
+ }
4429
+
4430
+ // packages/plugin-protocol/dist/ui-app-package.js
4431
+ import { z as z11 } from "zod";
4432
+ var KAY_UI_APP_PACKAGE_PROTOCOL = "kay.ui.app-package.v1";
4433
+ var KAY_UI_BUNDLE_INDEX_PROTOCOL = "kay.ui.bundle-index.v1";
4434
+ var KayUiBundleOutputPath = z11.string().trim().min(1).refine((value) => !value.startsWith("/") && !value.startsWith("./") && !value.includes("\\") && !value.split("/").includes(".."), { message: "bundle output path must stay relative to the bundle root" });
4435
+ var KayUiBundleIndexV1 = z11.strictObject({
4436
+ protocol: z11.literal(KAY_UI_BUNDLE_INDEX_PROTOCOL),
4437
+ entryPath: KayUiBundleOutputPath,
4438
+ files: z11.array(z11.strictObject({
4439
+ path: KayUiBundleOutputPath,
4440
+ mediaType: NonEmptyString
4441
+ })).min(1)
4442
+ }).superRefine((index, ctx) => {
4443
+ const paths = /* @__PURE__ */ new Set();
4444
+ for (const [fileIndex, file] of index.files.entries()) {
4445
+ if (paths.has(file.path)) {
4446
+ ctx.addIssue({
4447
+ code: "custom",
4448
+ path: ["files", fileIndex, "path"],
4449
+ message: `duplicate bundle output path ${file.path}`
4450
+ });
4451
+ }
4452
+ paths.add(file.path);
4453
+ }
4454
+ if (!paths.has(index.entryPath)) {
4455
+ ctx.addIssue({
4456
+ code: "custom",
4457
+ path: ["entryPath"],
4458
+ message: "bundle index entryPath must name an indexed file"
4459
+ });
4460
+ }
4461
+ });
4462
+ function parseKayUiBundleIndexV1(input) {
4463
+ return KayUiBundleIndexV1.parse(input);
4464
+ }
4465
+ var KayUiAppPackageFile = z11.strictObject({
4466
+ path: PackageAssetPath,
4467
+ mediaType: NonEmptyString,
4468
+ bytes: z11.instanceof(Uint8Array).refine((bytes) => bytes.byteLength > 0, {
4469
+ message: "app package file bytes must not be empty"
4470
+ })
4471
+ });
4472
+ var KayUiAppPackageV1 = z11.strictObject({
4473
+ protocol: z11.literal(KAY_UI_APP_PACKAGE_PROTOCOL),
4474
+ appLocalId: LocalContributionId,
4475
+ entry: KayUiAppPackageFile,
4476
+ styles: z11.array(KayUiAppPackageFile).optional(),
4477
+ assets: z11.array(KayUiAppPackageFile).optional()
4478
+ }).superRefine((appPackage, ctx) => {
4479
+ if (appPackage.entry.mediaType !== "application/javascript") {
4480
+ ctx.addIssue({
4481
+ code: "custom",
4482
+ path: ["entry", "mediaType"],
4483
+ message: "app package entry media type must be application/javascript"
4484
+ });
4485
+ }
4486
+ const seenPaths = /* @__PURE__ */ new Set();
4487
+ const files = [
4488
+ { file: appPackage.entry, path: ["entry", "path"] },
4489
+ ...(appPackage.styles ?? []).map((file, index) => ({
4490
+ file,
4491
+ path: ["styles", index, "path"]
4492
+ })),
4493
+ ...(appPackage.assets ?? []).map((file, index) => ({
4494
+ file,
4495
+ path: ["assets", index, "path"]
4496
+ }))
4497
+ ];
4498
+ for (const { file, path } of files) {
4499
+ if (seenPaths.has(file.path)) {
4500
+ ctx.addIssue({
4501
+ code: "custom",
4502
+ path: [...path],
4503
+ message: `duplicate app package file path ${file.path}`
4504
+ });
4505
+ }
4506
+ seenPaths.add(file.path);
4507
+ }
4508
+ });
4509
+ function parseKayUiAppPackageV1(input) {
4510
+ return KayUiAppPackageV1.parse(input);
4511
+ }
4512
+
4513
+ // packages/plugin-protocol/dist/whiteboard.js
4514
+ import { z as z12 } from "zod";
4515
+ var WhiteboardValue = JsonValue;
4516
+ var WhiteboardEntryEnvelope = z12.strictObject({
4517
+ id: NonEmptyString,
4518
+ typeId: GlobalContributionId,
4519
+ schemaVersion: z12.number().int().positive(),
4520
+ title: z12.string(),
4521
+ authorSessionId: NonEmptyString.nullable()
4522
+ });
4523
+ var WhiteboardEntrySummary = z12.strictObject({
4524
+ name: NonEmptyString,
4525
+ entry: WhiteboardEntryEnvelope,
4526
+ revision: NonEmptyString
4527
+ });
4528
+ var WhiteboardEntrySnapshot = WhiteboardEntrySummary.extend({
4529
+ value: WhiteboardValue
4530
+ });
4531
+ var WhiteboardDiagnostic = z12.strictObject({
4532
+ ruleId: NonEmptyString,
4533
+ severity: z12.enum(["error", "warning"]),
4534
+ message: NonEmptyString
4535
+ });
4536
+ var WhiteboardEntryInfo = z12.strictObject({
4537
+ name: NonEmptyString,
4538
+ revision: NonEmptyString,
4539
+ entry: WhiteboardEntryEnvelope.optional(),
4540
+ state: z12.enum(["available", "unavailable", "unsupported-version", "invalid"]),
4541
+ mutable: z12.boolean(),
4542
+ diagnostics: z12.array(z12.string())
4543
+ });
4544
+ var WhiteboardEntryInspection = WhiteboardEntryInfo.extend({
4545
+ value: WhiteboardValue.optional(),
4546
+ text: z12.string(),
4547
+ truncated: z12.boolean()
4548
+ });
4549
+ var WhiteboardTypeInfo = z12.strictObject({
4550
+ typeId: GlobalContributionId,
4551
+ displayName: NonEmptyString,
4552
+ schemaVersion: z12.number().int().positive(),
4553
+ available: z12.boolean(),
4554
+ /** A plugin-owned readonly presentation; absent types use the host fallback. */
4555
+ presentation: z12.strictObject({
4556
+ app: GlobalContributionId
4557
+ }).optional()
4558
+ });
4559
+ export {
4560
+ AGENT_RUNTIME_SESSION_CREATOR_KINDS,
4561
+ ARTIFACT_PROVENANCE_VERSION,
4562
+ AdapterPlacement,
4563
+ AdapterSelectionKey,
4564
+ AgentRenderContribution,
4565
+ AppDataMaterializerContribution,
4566
+ AppDataSourceContribution,
4567
+ AppDataSourceRead,
4568
+ AppEventSubscription,
4569
+ AppUriModuleContribution,
4570
+ ArtifactGenerationId,
4571
+ ArtifactProvenanceActor,
4572
+ ArtifactProvenanceCaptureContextSchema,
4573
+ ArtifactProvenanceCause,
4574
+ ArtifactProvenanceEnvelopeV1,
4575
+ ArtifactProvenanceGap,
4576
+ ArtifactProvenanceMutation,
4577
+ ArtifactProvenanceOperation,
4578
+ ArtifactProvenanceReference,
4579
+ ArtifactProvenanceRevision,
4580
+ ArtifactProvenanceSession,
4581
+ ArtifactProvenanceSource,
4582
+ ArtifactUriModuleContribution,
4583
+ AuthAccountSelector,
4584
+ AuthGrantOperation,
4585
+ AuthGrantRequest,
4586
+ AuthSchemeReference,
4587
+ AuthScopeDefinition,
4588
+ AuthServiceReference,
4589
+ CliAuthEnvVarName,
4590
+ CliBinaryName,
4591
+ CliBindingContribution,
4592
+ CommandContribution,
4593
+ CommandHandler,
4594
+ ContextContributorContribution,
4595
+ ConverterContribution,
4596
+ CorpusContribution,
4597
+ DeviceDelegationContribution,
4598
+ EndpointContribution,
4599
+ EndpointGrantRequest,
4600
+ EntityResolverContribution,
4601
+ EventGrantRequest,
4602
+ FileExtensionToken,
4603
+ GitBasicAuthUser,
4604
+ GitHostContribution,
4605
+ GitHostName,
4606
+ GlobalContributionId,
4607
+ HttpStatusCode,
4608
+ HttpsUrl,
4609
+ JsonObject,
4610
+ JsonPointer,
4611
+ JsonSchema,
4612
+ JsonValue,
4613
+ KAY_APP_DATA_CHANGE_MAX_BYTES,
4614
+ KAY_APP_DATA_READ_REQUEST_MAX_BYTES,
4615
+ KAY_APP_DATA_READ_RESULT_MAX_BYTES,
4616
+ KAY_APP_DATA_SNAPSHOT_MAX_BYTES,
4617
+ KAY_EVENT_PUBLISHER_ID,
4618
+ KAY_PLUGIN_PROTOCOL_PACKAGE_NAME,
4619
+ KAY_PLUGIN_PROTOCOL_VERSION,
4620
+ KAY_PLUGIN_SDK_VERSION,
4621
+ KAY_PLUGIN_TARGET_NAMES,
4622
+ KAY_UI_APP_PACKAGE_PROTOCOL,
4623
+ KAY_UI_BUNDLE_INDEX_PROTOCOL,
4624
+ KAY_UI_NATIVE_EVENT_SUBSCRIPTIONS,
4625
+ KayAgentTargetManifest,
4626
+ KayAppTargetManifest,
4627
+ KayPluginContractResolutionError,
4628
+ KayPluginManifestV1,
4629
+ KayPluginProtocolVersion,
4630
+ KayUiAppHostCapability,
4631
+ KayUiAppPackageFile,
4632
+ KayUiAppPackageV1,
4633
+ KayUiBundleIndexV1,
4634
+ KayUiTargetManifest,
4635
+ KayVersionString,
4636
+ KnowledgeSearchGrantRequest,
4637
+ KnownResourcesGrantRequest,
4638
+ LocalContributionId,
4639
+ McpServerAuthMode,
4640
+ McpServerContribution,
4641
+ McpServerContributions,
4642
+ McpServerUrl,
4643
+ MimeTypeString,
4644
+ NonEmptyString,
4645
+ PLUGIN_CAPABILITY_SLUGS,
4646
+ PLUGIN_NOTIFICATION_RULE_HOST_KEYS,
4647
+ PackageAssetPath,
4648
+ PluginAgentDefinitionPermissionRule,
4649
+ PluginAuthManifest,
4650
+ PluginAuthScheme,
4651
+ PluginAuthService,
4652
+ PluginCapabilityClaimV1,
4653
+ PluginCapabilityDecisionV1,
4654
+ PluginCapabilityRefusalCode,
4655
+ PluginCapabilityRefusalLayer,
4656
+ PluginCapabilityRefusalV1,
4657
+ PluginCompatibilityManifest,
4658
+ PluginConfigManifest,
4659
+ PluginConfigScope,
4660
+ PluginConfigSectionManifest,
4661
+ PluginContributionKind,
4662
+ PluginEventDefinitionContribution,
4663
+ PluginId,
4664
+ PluginNamespace,
4665
+ PluginNotificationRuleContribution,
4666
+ PluginOAuthClientSupplier,
4667
+ PluginPermissionManifest,
4668
+ PluginProvisioningProbe,
4669
+ PluginProvisioningRequirement,
4670
+ PluginSkillSourceDeclaration,
4671
+ PluginSourceClass,
4672
+ PluginStorageManifest,
4673
+ PluginTargetName,
4674
+ PluginToolName,
4675
+ PluginWebSessionDeclaration,
4676
+ PluginWebSessionProbe,
4677
+ RETIRED_COMPATIBILITY_KEYS,
4678
+ RETIRED_PERMISSION_ASK_KEYS,
4679
+ RETIRED_WEB_SESSION_ROW_KEYS,
4680
+ ReferenceOpenBehavior,
4681
+ ReferencePresentationContribution,
4682
+ ReferencePresentationUrlClaim,
4683
+ ReferencePresentationVariant,
4684
+ ReferencePresentationVariants,
4685
+ RegistrableDomain,
4686
+ ResolvedAuthGrantRequest,
4687
+ ResolvedEndpointGrantRequest,
4688
+ ResolvedKayAgentArtifactUriModule,
4689
+ ResolvedKayAgentConverter,
4690
+ ResolvedKayAgentEntityResolver,
4691
+ ResolvedKayAgentGitHostClaim,
4692
+ ResolvedKayAgentTool,
4693
+ ResolvedKayAgentUriModule,
4694
+ ResolvedKayAppDataSource,
4695
+ ResolvedKayAppMcpServer,
4696
+ ResolvedKayAppTool,
4697
+ ResolvedKayAppWhiteboardType,
4698
+ ResolvedKayUiApp,
4699
+ ResolvedKayUiAppEndpointCapability,
4700
+ ResolvedPluginAuthScheme,
4701
+ ResolvedPluginAuthService,
4702
+ ResolvedPluginContractV1,
4703
+ ResolvedPluginContributionId,
4704
+ ResolvedPluginGrants,
4705
+ ResolvedPluginProvisioningRequirement,
4706
+ ResolvedPluginWebSession,
4707
+ ResolvedReferencePresentation,
4708
+ ResolvedTargetGrantSet,
4709
+ ResourceContribution,
4710
+ ResultRendererContribution,
4711
+ RichViewContribution,
4712
+ SEMVER_PATTERN,
4713
+ SESSION_HEALTH_CLASSIFIER_IDS,
4714
+ SESSION_HEALTH_CUSTOM_TYPE,
4715
+ SESSION_HEALTH_ENTRY_NAME,
4716
+ SESSION_HEALTH_EXTENSION_ID,
4717
+ ScopeAlternative,
4718
+ ScopeNeed,
4719
+ ScopeRequirement,
4720
+ SdkSessionDoor,
4721
+ SearchProviderContribution,
4722
+ SessionCausedByEvent,
4723
+ SessionCreator,
4724
+ SessionDefiner,
4725
+ SessionIntent,
4726
+ SessionProvenanceRecord,
4727
+ SessionRelationship,
4728
+ SettingsPanelContribution,
4729
+ SignedInStatusCode,
4730
+ SkillContribution,
4731
+ StorageGrantRequest,
4732
+ TargetEntrypoint,
4733
+ TargetPermissionSet,
4734
+ TargetPermissionWiring,
4735
+ ThemeContribution,
4736
+ ToolContribution,
4737
+ UiAppCapabilities,
4738
+ UiAppContribution,
4739
+ UiCommandContribution,
4740
+ UiEventSubscriptionContribution,
4741
+ UiSlotContribution,
4742
+ UriFilesystemBacking,
4743
+ UriModuleContribution,
4744
+ UriScheme,
4745
+ WEB_SESSION_CSRF_SELECTOR_PATTERN,
4746
+ WebSessionCsrfSelector,
4747
+ WebSessionGrantRequest,
4748
+ WhiteboardCapabilityClaimV1,
4749
+ WhiteboardCapabilityOperation,
4750
+ WhiteboardCapabilityScope,
4751
+ WhiteboardDiagnostic,
4752
+ WhiteboardEntryEnvelope,
4753
+ WhiteboardEntryInfo,
4754
+ WhiteboardEntryInspection,
4755
+ WhiteboardEntrySnapshot,
4756
+ WhiteboardEntrySummary,
4757
+ WhiteboardTypeContribution,
4758
+ WhiteboardTypeInfo,
4759
+ WhiteboardTypeStorage,
4760
+ WhiteboardValue,
4761
+ captureArtifactProvenance,
4762
+ captureFailedContext,
4763
+ checkKayPluginHostCompatibility,
4764
+ compareClaims,
4765
+ compareSemver,
4766
+ configSectionKeysForManifest,
4767
+ dedupeClaims,
4768
+ finalizeArtifactProvenanceEnvelope,
4769
+ hasClaim,
4770
+ hostWithinDomain,
4771
+ isCompatible,
4772
+ isQualifiedAuthSchemeRef,
4773
+ isQualifiedAuthServiceRef,
4774
+ isSupportedSemverRange,
4775
+ kayUiEventSubscriptionsFromManifest,
4776
+ mintOperation,
4777
+ normalizeNetworkConnectHost,
4778
+ parseArtifactProvenanceCaptureContext,
4779
+ parseArtifactProvenanceEnvelope,
4780
+ parseKayPluginManifestV1,
4781
+ parseKayUiAppPackageV1,
4782
+ parseKayUiBundleIndexV1,
4783
+ parseResolvedPluginContractV1,
4784
+ parseSemver,
4785
+ precedingPersonMessages,
4786
+ refineWebSessionScopeConfined,
4787
+ resolveAdapterId,
4788
+ resolveGlobalContributionId,
4789
+ resolveKayPluginContractV1,
4790
+ resolvePluginEventType,
4791
+ resolvePluginNamespace,
4792
+ resolveSessionAncestry,
4793
+ retiredCompatibilityKeysIn,
4794
+ retiredPermissionAsksIn,
4795
+ safeResolveKayPluginContractV1,
4796
+ sameClaim,
4797
+ satisfiesSemverRange,
4798
+ sessionHealthEntrySchema,
4799
+ sessionHealthPWellDecile,
4800
+ turnPacket,
4801
+ urlHostWithinDomain,
4802
+ validateKayUiEventSubscription,
4803
+ webSessionPathWithinPrefix,
4804
+ whiteboardCapabilityClaim
4805
+ };