@yaklabs-ai/plugin-sdk 0.45.0 → 0.47.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
+ };
@@ -6902,6 +6902,7 @@ declare const toolCallFailureCauseSchema: z.ZodObject<{
6902
6902
  errorName: z.ZodOptional<z.ZodString>;
6903
6903
  errorCode: z.ZodOptional<z.ZodString>;
6904
6904
  providerCode: z.ZodOptional<z.ZodString>;
6905
+ provider: z.ZodOptional<z.ZodString>;
6905
6906
  status: z.ZodOptional<z.ZodNumber>;
6906
6907
  editIndex: z.ZodOptional<z.ZodNumber>;
6907
6908
  occurrences: z.ZodOptional<z.ZodNumber>;
@@ -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 = [
@@ -728,6 +730,7 @@ var toolCallFailureDetailsSchema = z5.strictObject({
728
730
  errorName: z5.string().optional(),
729
731
  errorCode: z5.string().optional(),
730
732
  providerCode: z5.string().optional(),
733
+ provider: z5.string().optional(),
731
734
  status: z5.number().int().min(100).max(599).optional(),
732
735
  editIndex: count.optional(),
733
736
  occurrences: count.optional(),
@@ -2431,9 +2431,21 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
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
2433
  *
2434
- * 0.45.0 adds declared plugin cards and app.implementCard for their read/submit endpoints.
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.
2445
+ *
2446
+ * 0.47.0 adds card layouts and builders; successful card submits now require a summary.
2435
2447
  */
2436
- declare const KAY_PLUGIN_SDK_VERSION: "0.45.0";
2448
+ declare const KAY_PLUGIN_SDK_VERSION: "0.47.0";
2437
2449
 
2438
2450
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2439
2451
 
@@ -4703,6 +4715,7 @@ declare function refineWebSessionScopeConfined(webSession: {
4703
4715
  readonly probe: {
4704
4716
  readonly url: string;
4705
4717
  };
4718
+ readonly signInFirst?: string | undefined;
4706
4719
  }, ctx: z.RefinementCtx): void;
4707
4720
 
4708
4721
  declare const RegistrableDomain: z.ZodString;
@@ -7808,6 +7821,7 @@ declare const toolCallFailureCauseSchema: z.ZodObject<{
7808
7821
  errorName: z.ZodOptional<z.ZodString>;
7809
7822
  errorCode: z.ZodOptional<z.ZodString>;
7810
7823
  providerCode: z.ZodOptional<z.ZodString>;
7824
+ provider: z.ZodOptional<z.ZodString>;
7811
7825
  status: z.ZodOptional<z.ZodNumber>;
7812
7826
  editIndex: z.ZodOptional<z.ZodNumber>;
7813
7827
  occurrences: z.ZodOptional<z.ZodNumber>;
@@ -9521,6 +9535,11 @@ declare type UriClaim = UriSchemeClaim | UriPatternClaim | UrlPatternClaim;
9521
9535
  * - `base64`: unpadded base64 of the UTF-8 text (`{base64(:id :calendar)}`).
9522
9536
  * - `slackTimestamp`: a Slack archive message token as the API's timestamp
9523
9537
  * (`p1783350000000100` becomes `1783350000.000100`).
9538
+ * - `beforeColon`: the part of a `<team>:<user>` id before the colon. It fills nothing for an id
9539
+ * with no colon or nothing before it.
9540
+ * - `beforeTilde`: the part of an id before its last `~`. It fills nothing with no `~`.
9541
+ * - `encoded`: every reserved character escaped, `:` and `@` too, for a whole URL carried as a
9542
+ * query value (`continue={encoded(:url)}`).
9524
9543
  *
9525
9544
  * A transform that cannot read its input fills nothing. A claim whose `normalizeTo` fills nothing
9526
9545
  * does not match; a `sourceUrl` that fills nothing paints no link.
@@ -9779,6 +9798,13 @@ declare interface UriModule {
9779
9798
  readonly uri: string;
9780
9799
  readonly payload: unknown;
9781
9800
  }, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
9801
+ /**
9802
+ * Find the web address of `uri` by asking the provider, for a resource whose address alone can't
9803
+ * name its page. Called only when someone opens the address, under the account it means, and never
9804
+ * to draw a reference. `undefined` means there's no page to open. A plugin module that defines
9805
+ * this sets `looksUpWebLinks` in its manifest.
9806
+ */
9807
+ lookupWebLink?(uri: string, ctx: UriResolveContext): Promise<string | undefined>;
9782
9808
  readonly getCachePolicy?: UriCachePolicyResolver;
9783
9809
  /**
9784
9810
  * Code matching, for the host's own modules whose addresses no claim can describe (a bare file
@@ -9900,6 +9926,11 @@ declare interface UriOperationDescription {
9900
9926
  readonly editable?: true;
9901
9927
  /** Longest edited value, in UTF-16 code units. Meaningful only with `editable`. */
9902
9928
  readonly maxLength?: number;
9929
+ /**
9930
+ * Which part of a message this field is, when the call sends one. A preview with a `to` and
9931
+ * a `text` is drawn as a message; without them it stays labelled rows.
9932
+ */
9933
+ readonly part?: "to" | "cc" | "bcc" | "subject" | "text";
9903
9934
  }[];
9904
9935
  };
9905
9936
  /** True only when every displayed fact comes from the input, independent of the executing account. */
@@ -10505,9 +10536,6 @@ declare const WhiteboardTypeInfo: z.ZodObject<{
10505
10536
  displayName: z.ZodString;
10506
10537
  schemaVersion: z.ZodNumber;
10507
10538
  available: z.ZodBoolean;
10508
- presentation: z.ZodOptional<z.ZodObject<{
10509
- app: z.ZodString;
10510
- }, z.core.$strict>>;
10511
10539
  }, z.core.$strict>;
10512
10540
 
10513
10541
  declare type WhiteboardTypeInfo = Readonly<z.infer<typeof WhiteboardTypeInfo>>;