@yaklabs-ai/plugin-sdk 0.44.0 → 0.46.0

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.
@@ -0,0 +1,565 @@
1
+ // packages/plugin-protocol/dist/artifact-provenance.js
2
+ import { z as z2 } from "zod";
3
+
4
+ // packages/plugin-protocol/dist/session-provenance.js
5
+ import { z } from "zod";
6
+ var NonEmptyString = z.string().min(1);
7
+ var SessionId = NonEmptyString;
8
+ var SessionRelationship = z.discriminatedUnion("kind", [
9
+ /** Spawn lineage: this session is a subagent child of `sessionId`. */
10
+ z.strictObject({ kind: z.literal("child-of"), sessionId: SessionId }),
11
+ /**
12
+ * This session was forked from a point in `sessionId`'s transcript - `/fork`, and every
13
+ * hindsight followup. `atEntryId` pins the fork point when one was given.
14
+ */
15
+ z.strictObject({
16
+ kind: z.literal("forked-from"),
17
+ sessionId: SessionId,
18
+ atEntryId: NonEmptyString.optional()
19
+ })
20
+ ]);
21
+ var SdkSessionDoor = z.enum([
22
+ "sessions.create",
23
+ "sessions.run",
24
+ "hindsight.runFollowup",
25
+ "agents.run"
26
+ ]);
27
+ var SessionCreator = z.discriminatedUnion("kind", [
28
+ /** A person, at the keyboard. `surface` names a scripted experience when one minted it. */
29
+ z.strictObject({
30
+ kind: z.literal("human"),
31
+ surface: NonEmptyString.optional()
32
+ }),
33
+ /** Another session's agent, through a tool call. */
34
+ z.strictObject({
35
+ kind: z.literal("agent"),
36
+ sessionId: SessionId,
37
+ tool: NonEmptyString,
38
+ /**
39
+ * The PARENT SESSION RUN that was driving when this session was minted
40
+ * (R2 provenance). Optional at parse so old rows stay readable; required
41
+ * of new agent producers. Absence on a new row is reported as a
42
+ * `legacy-run-id-missing` gap, never synthesized.
43
+ */
44
+ runId: NonEmptyString.optional(),
45
+ /**
46
+ * Correlation only; never part of identity. A missing or reused
47
+ * `toolCallId` must not block the exact parent-run link and never
48
+ * participates in mutation-result identity.
49
+ */
50
+ toolCallId: NonEmptyString.optional()
51
+ }),
52
+ /** A plugin, through one of its SDK doors. */
53
+ z.strictObject({
54
+ kind: z.literal("plugin"),
55
+ pluginId: NonEmptyString,
56
+ door: SdkSessionDoor,
57
+ callerSessionId: SessionId.optional()
58
+ }),
59
+ /** An automation binding that fired; the run id links its durable execution. */
60
+ z.strictObject({
61
+ kind: z.literal("trigger"),
62
+ automationId: NonEmptyString,
63
+ /**
64
+ * The AUTOMATION RUN that fired the binding (R2 provenance). Optional at
65
+ * parse so old rows stay readable; required of new automation producers.
66
+ * Absence on a new row is reported as a `legacy-run-id-missing` gap,
67
+ * never synthesized from the binding or a timestamp.
68
+ */
69
+ runId: NonEmptyString.optional(),
70
+ bindingName: NonEmptyString,
71
+ triggerKind: z.enum([
72
+ "cron",
73
+ "time",
74
+ "occurrence",
75
+ "uri-change",
76
+ "plugin-event",
77
+ "manual"
78
+ ])
79
+ }),
80
+ /** The daemon itself, for one of its own capabilities. */
81
+ z.strictObject({ kind: z.literal("daemon"), capability: NonEmptyString })
82
+ ]);
83
+ var SessionDefiner = z.discriminatedUnion("layer", [
84
+ z.strictObject({
85
+ layer: z.literal("plugin"),
86
+ pluginId: NonEmptyString,
87
+ capability: NonEmptyString,
88
+ /** See the type docblock: unpopulated by design until #5230's children land. */
89
+ version: NonEmptyString.optional()
90
+ }),
91
+ z.strictObject({ layer: z.literal("host"), capability: NonEmptyString })
92
+ ]);
93
+ var SessionIntent = z.enum(["interactive", "headless"]);
94
+ var SessionCausedByEventShape = z.strictObject({
95
+ /** The plugin's own name for the subscribe handler that made this run, e.g. `"work-record"`. */
96
+ handler: NonEmptyString,
97
+ /** Absent reads as `"session-event"` - see this type's own doc comment. */
98
+ kind: z.enum(["session-event", "backfill"]).optional(),
99
+ /** Required when `kind` is `"session-event"` or absent; must be omitted when `kind` is `"backfill"`. */
100
+ event: z.strictObject({
101
+ /** {@link KayAppSessionEventType} in `@bonsai/plugin-sdk`, mirrored: a closed, extensible union. */
102
+ type: z.enum([
103
+ "turn-settled",
104
+ "phase-shift",
105
+ "human-message",
106
+ "assistant-message",
107
+ "ui-action"
108
+ ]),
109
+ entryId: NonEmptyString,
110
+ /** The source run captured at the event boundary; absence remains unresolved. */
111
+ runId: NonEmptyString.optional(),
112
+ /** Exact prior persisted boundary for a projected human-message event; null means empty history. */
113
+ throughEntryId: NonEmptyString.nullable().optional()
114
+ }).optional(),
115
+ /**
116
+ * The plugin-supplied exact-match dedupe key, when it gave one. Absent when the handler chose
117
+ * not to dedupe this run (every call is admitted). Carried here (not just checked and
118
+ * discarded) so a later reader can explain WHY the host refused a same-keyed second run. A
119
+ * `"backfill"` cause always supplies one (`backfill:${sessionId}`) so a sweep can never
120
+ * double-run the same historical session, but that is a caller convention, not a schema rule.
121
+ */
122
+ dedupeKey: NonEmptyString.optional()
123
+ });
124
+ var SessionCausedByEvent = SessionCausedByEventShape.refine((value) => (value.kind ?? "session-event") === "backfill" || value.event !== void 0, {
125
+ message: 'event is required unless kind is "backfill"',
126
+ path: ["event"]
127
+ }).refine((value) => value.kind !== "backfill" || value.event === void 0, {
128
+ message: 'event must be omitted when kind is "backfill"',
129
+ path: ["event"]
130
+ });
131
+ var SessionProvenanceRecord = z.strictObject({
132
+ relationships: z.array(SessionRelationship),
133
+ createdBy: SessionCreator,
134
+ definedBy: SessionDefiner,
135
+ intent: SessionIntent,
136
+ /** See {@link SessionCausedByEvent}. Present only alongside `createdBy.door === "agents.run"`. */
137
+ causedByEvent: SessionCausedByEvent.optional()
138
+ });
139
+
140
+ // packages/plugin-protocol/dist/artifact-provenance.js
141
+ var ARTIFACT_PROVENANCE_VERSION = "kay.artifact-provenance.v1";
142
+ function hasControlCharacter(value) {
143
+ for (let index = 0; index < value.length; index += 1) {
144
+ const code = value.charCodeAt(index);
145
+ if (code <= 31 || code === 127)
146
+ return true;
147
+ }
148
+ return false;
149
+ }
150
+ function isProvenanceJson(input) {
151
+ const active = /* @__PURE__ */ new Set();
152
+ const stack = [{ value: input }];
153
+ while (stack.length > 0) {
154
+ const { value, leave } = stack.pop();
155
+ if (value === null || typeof value === "boolean")
156
+ continue;
157
+ if (typeof value === "string") {
158
+ if (hasControlCharacter(value))
159
+ return false;
160
+ continue;
161
+ }
162
+ if (typeof value === "number") {
163
+ if (!Number.isFinite(value) || Object.is(value, -0))
164
+ return false;
165
+ continue;
166
+ }
167
+ if (typeof value !== "object")
168
+ return false;
169
+ if (leave) {
170
+ active.delete(value);
171
+ continue;
172
+ }
173
+ if (active.has(value))
174
+ return false;
175
+ const array = Array.isArray(value);
176
+ const prototype = Object.getPrototypeOf(value);
177
+ if (!array && prototype !== Object.prototype && prototype !== null)
178
+ return false;
179
+ const descriptors = Object.getOwnPropertyDescriptors(value);
180
+ const keys = Reflect.ownKeys(descriptors).filter((key) => !array || key !== "length");
181
+ if (array && (keys.length !== value.length || keys.some((key, index) => key !== String(index))))
182
+ return false;
183
+ active.add(value);
184
+ stack.push({ value, leave: true });
185
+ for (const key of keys) {
186
+ if (typeof key !== "string" || hasControlCharacter(key))
187
+ return false;
188
+ const descriptor = descriptors[key];
189
+ if (!descriptor.enumerable || !("value" in descriptor))
190
+ return false;
191
+ stack.push({ value: descriptor.value });
192
+ }
193
+ }
194
+ return true;
195
+ }
196
+ var Text = z2.string().min(1).refine((value) => !hasControlCharacter(value), {
197
+ message: "Provenance text must not contain control characters"
198
+ });
199
+ var ArtifactProvenanceRevision = z2.strictObject({
200
+ storeId: Text,
201
+ artifactId: Text,
202
+ uri: Text.optional(),
203
+ /** The enclosing commit is resolved by the reader, avoiding a self-referential commit hash. */
204
+ revision: z2.union([
205
+ Text,
206
+ z2.strictObject({ kind: z2.literal("enclosing-commit") }),
207
+ z2.null()
208
+ ]),
209
+ operationId: Text.nullable(),
210
+ /** Optional when the store identifies revisions without content addressing (including tombstones). */
211
+ digest: z2.strictObject({ algorithm: Text, value: Text }).optional()
212
+ });
213
+ var ArtifactProvenanceActor = z2.discriminatedUnion("kind", [
214
+ z2.strictObject({
215
+ kind: z2.literal("session"),
216
+ sessionId: Text,
217
+ runId: Text.nullable()
218
+ }),
219
+ z2.strictObject({
220
+ kind: z2.literal("human"),
221
+ userId: Text.optional(),
222
+ surface: Text
223
+ }),
224
+ z2.strictObject({
225
+ kind: z2.literal("plugin"),
226
+ pluginId: Text,
227
+ capability: Text
228
+ }),
229
+ z2.strictObject({ kind: z2.literal("host"), capability: Text }),
230
+ z2.strictObject({ kind: z2.literal("import"), source: Text }),
231
+ z2.strictObject({ kind: z2.literal("unknown") })
232
+ ]);
233
+ var ArtifactProvenanceCause = z2.discriminatedUnion("kind", [
234
+ z2.strictObject({
235
+ kind: z2.literal("session"),
236
+ sessionId: Text,
237
+ runId: Text.nullable()
238
+ }),
239
+ z2.strictObject({
240
+ kind: z2.literal("session-event"),
241
+ sessionId: Text,
242
+ cause: SessionCausedByEvent
243
+ }),
244
+ z2.strictObject({
245
+ kind: z2.literal("automation"),
246
+ automationId: Text,
247
+ runId: Text.nullable()
248
+ }),
249
+ z2.strictObject({ kind: z2.literal("operation"), operationId: Text })
250
+ ]);
251
+ var ArtifactProvenanceOperation = z2.strictObject({
252
+ id: Text.nullable(),
253
+ at: z2.iso.datetime().nullable(),
254
+ name: Text,
255
+ actor: ArtifactProvenanceActor,
256
+ /** Null means no tool invocation; a known invocation with an unknown id records a gap. */
257
+ invocation: z2.strictObject({
258
+ tool: Text,
259
+ toolCallId: Text.nullable(),
260
+ origin: z2.enum(["model", "host"])
261
+ }).nullable(),
262
+ /** Known causes only; unavailable cause capture requires a gap, never a guessed creation cause. */
263
+ causes: z2.array(ArtifactProvenanceCause)
264
+ });
265
+ var ArtifactProvenanceReference = z2.discriminatedUnion("kind", [
266
+ z2.strictObject({
267
+ kind: z2.literal("session"),
268
+ sessionId: Text,
269
+ runId: Text.optional()
270
+ }),
271
+ z2.strictObject({
272
+ kind: z2.literal("artifact"),
273
+ revision: ArtifactProvenanceRevision
274
+ }),
275
+ z2.strictObject({
276
+ kind: z2.literal("resource"),
277
+ uri: Text,
278
+ revision: Text.nullable()
279
+ })
280
+ ]);
281
+ var ArtifactProvenanceGap = z2.strictObject({
282
+ code: z2.enum([
283
+ "unknown",
284
+ "capture-failed",
285
+ "legacy-derived",
286
+ "ambiguous-predecessor",
287
+ "unverified-index",
288
+ "missing-ancestor",
289
+ "cycle",
290
+ "truncated",
291
+ "metadata-write-failed",
292
+ "invalid-record"
293
+ ]),
294
+ /** JSON Pointer into this envelope; the empty pointer covers the whole capture. */
295
+ at: z2.string().regex(/^(?:\/(?:[^~/]|~[01])*)*$/).refine((value) => !hasControlCharacter(value)),
296
+ detail: Text.optional()
297
+ }).refine((gap) => gap.at !== "" || gap.code === "capture-failed", {
298
+ message: "Only a whole-capture failure may cover the entire envelope"
299
+ });
300
+ var ArtifactProvenanceSession = z2.strictObject({
301
+ sessionId: Text,
302
+ basis: z2.enum(["recorded", "legacy-derived", "unverified-index", "missing"]),
303
+ record: SessionProvenanceRecord.nullable()
304
+ });
305
+ var ArtifactProvenanceSource = z2.strictObject({
306
+ relationship: z2.enum([
307
+ "revision-of",
308
+ "copied-from",
309
+ "harvested-from",
310
+ "restored-from",
311
+ "accepted-from",
312
+ "imported-from"
313
+ ]),
314
+ revision: ArtifactProvenanceRevision
315
+ });
316
+ var ArtifactProvenanceMutation = z2.strictObject({
317
+ kind: z2.enum([
318
+ "create",
319
+ "update",
320
+ "delete",
321
+ "copy",
322
+ "harvest",
323
+ "restore",
324
+ "accept",
325
+ "import"
326
+ ]),
327
+ target: ArtifactProvenanceRevision,
328
+ /** Retained from the source on copies/acceptance; operation.actor is the persistence actor. */
329
+ producer: ArtifactProvenanceOperation,
330
+ sources: z2.array(ArtifactProvenanceSource)
331
+ });
332
+ var Envelope = z2.strictObject({
333
+ version: z2.literal(ARTIFACT_PROVENANCE_VERSION),
334
+ operation: ArtifactProvenanceOperation,
335
+ artifacts: z2.array(ArtifactProvenanceMutation).min(1),
336
+ sessions: z2.array(ArtifactProvenanceSession),
337
+ subjects: z2.array(ArtifactProvenanceReference),
338
+ evidence: z2.array(ArtifactProvenanceReference),
339
+ gaps: z2.array(ArtifactProvenanceGap)
340
+ });
341
+ var UNKNOWN = [
342
+ "unknown",
343
+ "capture-failed",
344
+ "metadata-write-failed",
345
+ "invalid-record"
346
+ ];
347
+ var MISSING = ["missing-ancestor", "truncated"];
348
+ function checkOperation(operation, path, gap, session) {
349
+ if (operation.id === null)
350
+ gap(`${path}/id`, UNKNOWN);
351
+ if (operation.at === null)
352
+ gap(`${path}/at`, UNKNOWN);
353
+ if (operation.actor.kind === "unknown")
354
+ gap(`${path}/actor`, UNKNOWN);
355
+ if (operation.actor.kind === "session") {
356
+ session(operation.actor.sessionId, `${path}/actor/sessionId`);
357
+ if (operation.actor.runId === null)
358
+ gap(`${path}/actor/runId`, UNKNOWN);
359
+ }
360
+ if (operation.invocation?.toolCallId === null)
361
+ gap(`${path}/invocation/toolCallId`, UNKNOWN);
362
+ operation.causes.forEach((cause, index) => {
363
+ const causePath = `${path}/causes/${index}`;
364
+ if (cause.kind === "session" || cause.kind === "session-event")
365
+ session(cause.sessionId, `${causePath}/sessionId`);
366
+ if (cause.kind === "session-event" && (cause.cause.kind ?? "session-event") !== "backfill" && cause.cause.event?.runId === void 0) {
367
+ gap(`${causePath}/cause/event/runId`, UNKNOWN);
368
+ }
369
+ if ("runId" in cause && cause.runId === null)
370
+ gap(`${causePath}/runId`, UNKNOWN);
371
+ });
372
+ }
373
+ function checkRevision(revision, path, gap, issue, currentTarget = false) {
374
+ if (revision.revision === null)
375
+ gap(`${path}/revision`, UNKNOWN);
376
+ else if (typeof revision.revision === "object") {
377
+ if (!currentTarget)
378
+ issue(path.slice(1).split("/"), "Only the current target may refer to its enclosing commit");
379
+ if (revision.digest === void 0)
380
+ issue(path.slice(1).split("/"), "An enclosing-commit target must bind the exact content digest before commit");
381
+ }
382
+ if (revision.operationId === null)
383
+ gap(`${path}/operationId`, UNKNOWN);
384
+ }
385
+ function checkGraph(envelope, gap, issue) {
386
+ const nodes = new Map(envelope.sessions.map((node, index) => [node.sessionId, index]));
387
+ const edges = /* @__PURE__ */ new Map();
388
+ envelope.sessions.forEach((node, index) => {
389
+ const path = `/sessions/${index}`;
390
+ if (nodes.get(node.sessionId) !== index)
391
+ issue(["sessions", index, "sessionId"], "Session snapshots must be deduplicated by sessionId");
392
+ if (node.basis === "missing") {
393
+ if (node.record !== null)
394
+ issue(["sessions", index, "record"], "A missing snapshot cannot carry a creation record");
395
+ gap(path, MISSING);
396
+ } else if (node.record === null) {
397
+ issue(["sessions", index, "record"], "A non-missing snapshot requires its creation record");
398
+ }
399
+ if (node.basis === "legacy-derived")
400
+ gap(path, ["legacy-derived"]);
401
+ if (node.basis === "unverified-index")
402
+ gap(path, ["unverified-index"]);
403
+ const record = node.record;
404
+ if (record === null)
405
+ return;
406
+ const outgoing = record.relationships.map((relationship, edgeIndex) => ({
407
+ id: relationship.sessionId,
408
+ path: `${path}/record/relationships/${edgeIndex}/sessionId`
409
+ }));
410
+ const creator = record.createdBy;
411
+ if (creator.kind === "agent")
412
+ outgoing.push({
413
+ id: creator.sessionId,
414
+ path: `${path}/record/createdBy/sessionId`
415
+ });
416
+ if (creator.kind === "plugin" && creator.callerSessionId !== void 0)
417
+ outgoing.push({
418
+ id: creator.callerSessionId,
419
+ path: `${path}/record/createdBy/callerSessionId`
420
+ });
421
+ if ((creator.kind === "agent" || creator.kind === "trigger") && creator.runId === void 0 && node.basis === "recorded")
422
+ gap(`${path}/record/createdBy/runId`, UNKNOWN);
423
+ const eventCause = record.causedByEvent;
424
+ if (eventCause !== void 0 && (eventCause.kind ?? "session-event") !== "backfill" && eventCause.event?.runId === void 0 && node.basis === "recorded") {
425
+ gap(`${path}/record/causedByEvent/event/runId`, UNKNOWN);
426
+ }
427
+ edges.set(node.sessionId, outgoing);
428
+ outgoing.forEach((edge) => {
429
+ if (!nodes.has(edge.id))
430
+ gap(edge.path, MISSING);
431
+ });
432
+ });
433
+ const state = /* @__PURE__ */ new Map();
434
+ for (const node of envelope.sessions) {
435
+ if (state.has(node.sessionId))
436
+ continue;
437
+ const stack = [{ id: node.sessionId, next: 0 }];
438
+ state.set(node.sessionId, "active");
439
+ while (stack.length > 0) {
440
+ const current = stack[stack.length - 1];
441
+ const edge = edges.get(current.id)?.[current.next++];
442
+ if (edge === void 0) {
443
+ state.set(current.id, "done");
444
+ stack.pop();
445
+ } else if (state.get(edge.id) === "active") {
446
+ gap(edge.path, ["cycle"]);
447
+ } else if (nodes.has(edge.id) && !state.has(edge.id)) {
448
+ state.set(edge.id, "active");
449
+ stack.push({ id: edge.id, next: 0 });
450
+ }
451
+ }
452
+ }
453
+ }
454
+ function validateEnvelope(envelope, context) {
455
+ const issue = (path, message) => context.addIssue({ code: "custom", path, message });
456
+ const gap = (path, codes) => {
457
+ if (!envelope.gaps.some((entry) => entry.code === "capture-failed" && entry.at === "" || codes.includes(entry.code) && (path === entry.at || path.startsWith(`${entry.at}/`)))) {
458
+ issue(path.slice(1).split("/"), `Unresolved provenance requires an explicit ${codes.join(" or ")} gap`);
459
+ }
460
+ };
461
+ const nodes = new Set(envelope.sessions.map((node) => node.sessionId));
462
+ const session = (id, path) => {
463
+ if (!nodes.has(id))
464
+ gap(path, MISSING);
465
+ };
466
+ if (envelope.operation.id === null)
467
+ issue(["operation", "id"], "The current mutation requires a fresh operation identity");
468
+ checkOperation(envelope.operation, "/operation", gap, session);
469
+ const targets = /* @__PURE__ */ new Set();
470
+ envelope.artifacts.forEach((artifact, index) => {
471
+ const path = `/artifacts/${index}`;
472
+ const key = JSON.stringify([
473
+ artifact.target.storeId,
474
+ artifact.target.artifactId
475
+ ]);
476
+ if (targets.has(key))
477
+ issue(["artifacts", index, "target"], "One operation records each target artifact once");
478
+ targets.add(key);
479
+ if (artifact.target.operationId !== envelope.operation.id)
480
+ issue(["artifacts", index, "target", "operationId"], "The target must name the current persistence operation");
481
+ checkRevision(artifact.target, `${path}/target`, gap, issue, true);
482
+ checkOperation(artifact.producer, `${path}/producer`, gap, session);
483
+ artifact.sources.forEach((source, sourceIndex) => checkRevision(source.revision, `${path}/sources/${sourceIndex}/revision`, gap, issue));
484
+ const requiredSource = {
485
+ create: void 0,
486
+ update: "revision-of",
487
+ delete: "revision-of",
488
+ copy: "copied-from",
489
+ harvest: "harvested-from",
490
+ restore: "restored-from",
491
+ accept: "accepted-from",
492
+ import: "imported-from"
493
+ }[artifact.kind];
494
+ if (requiredSource !== void 0 && !artifact.sources.some((source) => source.relationship === requiredSource))
495
+ gap(`${path}/sources`, UNKNOWN);
496
+ });
497
+ for (const role of ["subjects", "evidence"]) {
498
+ envelope[role].forEach((reference, index) => {
499
+ const path = `/${role}/${index}`;
500
+ if (reference.kind === "session")
501
+ session(reference.sessionId, `${path}/sessionId`);
502
+ if (reference.kind === "artifact")
503
+ checkRevision(reference.revision, `${path}/revision`, gap, issue);
504
+ if (reference.kind === "resource" && reference.revision === null)
505
+ gap(`${path}/revision`, UNKNOWN);
506
+ });
507
+ }
508
+ checkGraph(envelope, gap, issue);
509
+ }
510
+ var ArtifactProvenanceEnvelopeV1 = z2.unknown().refine(isProvenanceJson, {
511
+ message: "Provenance must be plain, acyclic, lossless JSON without control characters"
512
+ }).pipe(Envelope.superRefine(validateEnvelope));
513
+ function parseArtifactProvenanceEnvelope(input) {
514
+ return ArtifactProvenanceEnvelopeV1.parse(input);
515
+ }
516
+ var ArtifactProvenanceCaptureContextSchema = z2.strictObject({
517
+ operation: ArtifactProvenanceOperation,
518
+ sessions: z2.array(ArtifactProvenanceSession),
519
+ subjects: z2.array(ArtifactProvenanceReference),
520
+ evidence: z2.array(ArtifactProvenanceReference),
521
+ gaps: z2.array(ArtifactProvenanceGap)
522
+ });
523
+ function parseArtifactProvenanceCaptureContext(input) {
524
+ return ArtifactProvenanceCaptureContextSchema.parse(input);
525
+ }
526
+
527
+ // packages/plugin-protocol/dist/artifact-provenance-capture.js
528
+ function finalizeArtifactProvenanceEnvelope(capture, artifacts) {
529
+ const artifactGaps = [];
530
+ const blanket = capture.gaps.some((gap) => gap.code === "capture-failed" && gap.at === "");
531
+ if (!blanket) {
532
+ artifacts.forEach((artifact, index) => {
533
+ if (artifact.target.revision === null) {
534
+ artifactGaps.push({
535
+ code: "unknown",
536
+ at: `/artifacts/${index}/target/revision`
537
+ });
538
+ }
539
+ if (artifact.producer === capture.operation) {
540
+ for (const gap of capture.gaps) {
541
+ if (gap.at === "/operation" || gap.at.startsWith("/operation/")) {
542
+ artifactGaps.push({
543
+ ...gap,
544
+ at: `/artifacts/${index}/producer${gap.at.slice("/operation".length)}`
545
+ });
546
+ }
547
+ }
548
+ }
549
+ });
550
+ }
551
+ return parseArtifactProvenanceEnvelope({
552
+ version: ARTIFACT_PROVENANCE_VERSION,
553
+ operation: capture.operation,
554
+ artifacts,
555
+ sessions: capture.sessions,
556
+ subjects: capture.subjects,
557
+ evidence: capture.evidence,
558
+ gaps: [...capture.gaps, ...artifactGaps]
559
+ });
560
+ }
561
+ export {
562
+ finalizeArtifactProvenanceEnvelope,
563
+ parseArtifactProvenanceCaptureContext,
564
+ parseArtifactProvenanceEnvelope
565
+ };
@@ -543,11 +543,6 @@ function isKernelMessageRole(role) {
543
543
  return Object.hasOwn(KERNEL_MESSAGE_ROLES, role);
544
544
  }
545
545
 
546
- // packages/herd-transcript/dist/is-object.js
547
- function isObject(value) {
548
- return typeof value === "object" && value !== null && !Array.isArray(value);
549
- }
550
-
551
546
  // packages/herd-transcript/dist/run-failure-classifier.js
552
547
  var MAX_FAILURE_TEXT_CHARS = 2e3;
553
548
  function boundedFailureText(text) {
@@ -625,6 +620,13 @@ function parsePresentationAnchor(value) {
625
620
 
626
621
  // packages/herd-transcript/dist/tool-call-failure.js
627
622
  import { z as z5 } from "zod";
623
+
624
+ // packages/herd-transcript/dist/is-object.js
625
+ function isObject(value) {
626
+ return typeof value === "object" && value !== null && !Array.isArray(value);
627
+ }
628
+
629
+ // packages/herd-transcript/dist/tool-call-failure.js
628
630
  var TOOL_CALL_FAILURE_DETAILS_KEY = "toolCallFailure";
629
631
  var TOOL_CALL_FAILURE_DOMAIN = "tool-call";
630
632
  var TOOL_CALL_FAILURE_CATEGORIES = [
@@ -2430,8 +2430,20 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
2430
2430
  *
2431
2431
  * 0.44.0 adds `words.kinds` to a reference presentation (#15905): an ordered list of address patterns
2432
2432
  * and the kind each one names. Additive: a plugin that lists them declares ^0.44.0.
2433
+ *
2434
+ * 0.45.0 adds declared plugin cards and app.implementCard for their read/submit endpoints. It also
2435
+ * lets a plugin declare what the window hardcoded about it (#15906). A reference presentation gains
2436
+ * `accountLinks`, how a page opens under the account an address means, and a `local-app` open, for
2437
+ * an item an app on this machine shows; a plugin the person installed can't declare that open. A
2438
+ * `webSession` gains `signInFirst`. A URI module defines `lookupWebLink` and sets `looksUpWebLinks`
2439
+ * to find a page when someone opens the address, and a preview field marks which `part` of a message
2440
+ * it is. A claim's `brandIcon` is a file path and no longer inline markup, which only this repo's
2441
+ * Microsoft plugin used. Otherwise additive: a plugin using any of these declares ^0.45.0.
2442
+ *
2443
+ * 0.46.0 adds `@bonsai/plugin-sdk/artifact-provenance`, so a plugin shipped as files can finalize
2444
+ * and read the provenance the host captured. Additive: a plugin that imports it declares ^0.46.0.
2433
2445
  */
2434
- declare const KAY_PLUGIN_SDK_VERSION: "0.44.0";
2446
+ declare const KAY_PLUGIN_SDK_VERSION: "0.46.0";
2435
2447
 
2436
2448
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2437
2449
 
@@ -4701,6 +4713,7 @@ declare function refineWebSessionScopeConfined(webSession: {
4701
4713
  readonly probe: {
4702
4714
  readonly url: string;
4703
4715
  };
4716
+ readonly signInFirst?: string | undefined;
4704
4717
  }, ctx: z.RefinementCtx): void;
4705
4718
 
4706
4719
  declare const RegistrableDomain: z.ZodString;
@@ -9519,6 +9532,11 @@ declare type UriClaim = UriSchemeClaim | UriPatternClaim | UrlPatternClaim;
9519
9532
  * - `base64`: unpadded base64 of the UTF-8 text (`{base64(:id :calendar)}`).
9520
9533
  * - `slackTimestamp`: a Slack archive message token as the API's timestamp
9521
9534
  * (`p1783350000000100` becomes `1783350000.000100`).
9535
+ * - `beforeColon`: the part of a `<team>:<user>` id before the colon. It fills nothing for an id
9536
+ * with no colon or nothing before it.
9537
+ * - `beforeTilde`: the part of an id before its last `~`. It fills nothing with no `~`.
9538
+ * - `encoded`: every reserved character escaped, `:` and `@` too, for a whole URL carried as a
9539
+ * query value (`continue={encoded(:url)}`).
9522
9540
  *
9523
9541
  * A transform that cannot read its input fills nothing. A claim whose `normalizeTo` fills nothing
9524
9542
  * does not match; a `sourceUrl` that fills nothing paints no link.
@@ -9777,6 +9795,13 @@ declare interface UriModule {
9777
9795
  readonly uri: string;
9778
9796
  readonly payload: unknown;
9779
9797
  }, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
9798
+ /**
9799
+ * Find the web address of `uri` by asking the provider, for a resource whose address alone can't
9800
+ * name its page. Called only when someone opens the address, under the account it means, and never
9801
+ * to draw a reference. `undefined` means there's no page to open. A plugin module that defines
9802
+ * this sets `looksUpWebLinks` in its manifest.
9803
+ */
9804
+ lookupWebLink?(uri: string, ctx: UriResolveContext): Promise<string | undefined>;
9780
9805
  readonly getCachePolicy?: UriCachePolicyResolver;
9781
9806
  /**
9782
9807
  * Code matching, for the host's own modules whose addresses no claim can describe (a bare file
@@ -9898,6 +9923,11 @@ declare interface UriOperationDescription {
9898
9923
  readonly editable?: true;
9899
9924
  /** Longest edited value, in UTF-16 code units. Meaningful only with `editable`. */
9900
9925
  readonly maxLength?: number;
9926
+ /**
9927
+ * Which part of a message this field is, when the call sends one. A preview with a `to` and
9928
+ * a `text` is drawn as a message; without them it stays labelled rows.
9929
+ */
9930
+ readonly part?: "to" | "cc" | "bcc" | "subject" | "text";
9901
9931
  }[];
9902
9932
  };
9903
9933
  /** True only when every displayed fact comes from the input, independent of the executing account. */