@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/LICENSE +21 -0
- package/README.md +58 -0
- package/THIRD-PARTY-INVENTORY.json +23 -0
- package/THIRD-PARTY-NOTICES.md +5 -0
- package/dist/index.d.ts +8339 -0
- package/dist/index.js +4805 -0
- package/package.json +37 -0
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
|
+
};
|