@yaklabs-ai/plugin-sdk 0.48.1 → 0.50.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.
package/dist/kay-app.js CHANGED
@@ -1,662 +1,693 @@
1
- // packages/plugin-protocol/dist/plugin-signal-limits.js
2
- var PLUGIN_EVENT_REFUSED_ERROR_NAME = "PluginEventRefusedError";
3
- var PLUGIN_SIGNAL_KINDS = [
4
- "message",
5
- "request",
6
- "mention",
7
- "invite",
8
- "status"
9
- ];
10
- var PLUGIN_SIGNAL_TITLE_MAX = 300;
11
- var PLUGIN_SIGNAL_SNIPPET_MAX = 500;
12
- var PLUGIN_SIGNAL_LABELS_MAX = 20;
13
- var PLUGIN_SIGNAL_LABEL_MAX = 64;
14
- var PLUGIN_SIGNAL_RELATED_KEYS = [
15
- "thread",
16
- "from",
17
- "to",
18
- "cc"
19
- ];
20
-
21
- // packages/plugin-protocol/dist/credential-text.js
22
- var CREDENTIAL_VALUE_PATTERNS = [
23
- // Anthropic / OpenAI-style secret keys (`sk-`, `sk-ant-`, `sk-proj-`).
24
- /^sk-[A-Za-z0-9_-]{16,}$/u,
25
- // GitHub classic, fine-grained, app, and OAuth tokens.
26
- /^gh[pousr]_[A-Za-z0-9]{16,}$/u,
27
- /^github_pat_[A-Za-z0-9_]{20,}$/u,
28
- // GitLab personal access tokens.
29
- /^glpat-[A-Za-z0-9_-]{16,}$/u,
30
- // Slack bot/user/app tokens.
31
- /^xox[baprse]-[A-Za-z0-9-]{10,}$/u,
32
- // AWS access key ids, long-lived and STS-issued.
33
- /^(?:AKIA|ASIA)[0-9A-Z]{16}$/u,
34
- // Google API keys, both published shapes.
35
- /^AIza[0-9A-Za-z_-]{35}$/u,
36
- /^AQ\.[A-Za-z0-9_-]{20,}$/u,
37
- // npm automation tokens.
38
- /^npm_[A-Za-z0-9]{30,}$/u,
39
- // SendGrid API keys (SG.<key id>.<secret>).
40
- /^SG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}$/u,
41
- // HashiCorp Vault service/batch/recovery tokens.
42
- /^hv[sbr]\.[A-Za-z0-9_-]{20,}$/u,
43
- // Shopify access tokens.
44
- /^shp(?:at|ss|ca|pa)_[a-fA-F0-9]{32}$/u,
45
- // Stripe live/test secret and restricted keys.
46
- /^(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}$/u,
47
- // JSON Web Tokens - header.payload.signature, header always starts `eyJ`.
48
- /^eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/u,
49
- // A whole Authorization header value carried as data.
50
- /^(?:Bearer|Basic) [A-Za-z0-9+/._~-]{16,}={0,2}$/iu,
51
- // PEM private key blocks, anywhere in the string.
52
- /-----BEGIN (?:[A-Z]+ )?PRIVATE KEY-----/u
53
- ];
54
- function isCredentialShapedText(value) {
55
- if (typeof value !== "string" || value.length === 0)
56
- return false;
57
- return CREDENTIAL_VALUE_PATTERNS.some((pattern) => pattern.test(value));
58
- }
59
-
60
- // packages/plugin-protocol/dist/json.js
61
- import { z } from "zod";
62
- var JsonValue = z.lazy(() => z.union([
63
- z.string(),
64
- z.number(),
65
- z.boolean(),
66
- z.null(),
67
- z.array(JsonValue),
68
- z.record(z.string(), JsonValue)
69
- ]));
70
- var JsonObject = z.record(z.string(), JsonValue);
71
- var JsonSchema = z.record(z.string(), JsonValue);
72
-
73
- // packages/plugin-sdk/dist/kay-app/card-layout.js
74
- function element(type, props) {
75
- return { root: "root", elements: { root: { type, props } } };
1
+ // packages/herd-uri/dist/errors.js
2
+ var UriError = class extends Error {
3
+ };
4
+ var CONFLICT_MESSAGES = {
5
+ changed: "This resource changed since this session last read it. Read it again before modifying it.",
6
+ unwitnessed: "This resource was not read in this session. Read it before modifying it.",
7
+ unknown: "This resource changed or was not read in this session. Read it again before modifying it."
8
+ };
9
+ var UriConflictError = class extends UriError {
10
+ _tag = "UriConflictError";
11
+ reason;
12
+ constructor(reason) {
13
+ super(CONFLICT_MESSAGES[reason ?? "unknown"]);
14
+ this.name = "UriConflictError";
15
+ this.reason = reason;
16
+ }
17
+ };
18
+ var UriValidationError = class extends UriError {
19
+ _tag = "UriValidationError";
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "UriValidationError";
23
+ }
24
+ };
25
+ function requireWriteContent(payload2, uri) {
26
+ if (payload2 === void 0) {
27
+ throw new UriValidationError(`Writing ${uri} needs \`content\` (or a \`source\` to copy from): pass the payload this address takes, which its help names.`);
28
+ }
29
+ if (payload2 instanceof Uint8Array) {
30
+ throw new UriValidationError(`${uri} stores text only, so it cannot take raw bytes. Copy the file to an address that accepts bytes.`);
31
+ }
32
+ return payload2;
76
33
  }
77
- function group(type, props, children) {
78
- const layout = element(type, props);
79
- layout.elements.root.children = children.map((child, index) => {
80
- const prefix = `${index}-`;
81
- for (const [key, value] of Object.entries(child.elements)) {
82
- layout.elements[prefix + key] = {
83
- ...value,
84
- ...value.children ? { children: value.children.map((id) => prefix + id) } : {}
85
- };
34
+ var UriProviderError = class extends UriError {
35
+ _tag = "UriProviderError";
36
+ moduleId;
37
+ operation;
38
+ uri;
39
+ status;
40
+ providerCode;
41
+ retryable;
42
+ /** Provider-directed delay before retrying, in milliseconds. */
43
+ retryAfterMs;
44
+ /** See {@link UriProviderErrorOptions.effect}. */
45
+ effect;
46
+ /** See {@link UriProviderErrorOptions.refusalMessage}. */
47
+ refusalMessage;
48
+ constructor(options) {
49
+ super(options.message);
50
+ this.name = "UriProviderError";
51
+ this.moduleId = options.moduleId;
52
+ this.operation = options.operation;
53
+ this.uri = options.uri;
54
+ if (options.status !== void 0)
55
+ this.status = options.status;
56
+ if (options.providerCode !== void 0)
57
+ this.providerCode = options.providerCode;
58
+ if (options.retryable !== void 0)
59
+ this.retryable = options.retryable;
60
+ if (options.retryAfterMs !== void 0) {
61
+ if (!Number.isInteger(options.retryAfterMs) || options.retryAfterMs < 0) {
62
+ throw new TypeError("retryAfterMs must be a nonnegative integer.");
63
+ }
64
+ this.retryAfterMs = options.retryAfterMs;
86
65
  }
87
- return prefix + child.root;
88
- });
89
- return layout;
90
- }
91
- function card(title, children, state) {
92
- return { ...group("Card", { title }, children), ...state ? { state } : {} };
93
- }
94
- function row(children) {
95
- return group("Row", {}, children);
96
- }
97
- function stack(children) {
98
- return group("Stack", {}, children);
99
- }
100
- function field(type, name, options) {
101
- const key = name.replaceAll("~", "~0").replaceAll("/", "~1");
102
- return element(type, { ...options, value: { $bindState: `/form/${key}` } });
103
- }
104
- function textField(name, options) {
105
- return field("Field", name, options);
106
- }
107
- function textArea(name, options) {
108
- return field("TextArea", name, options);
109
- }
110
- function select(name, options) {
111
- return field("Select", name, options);
112
- }
113
- function multiSelect(name, options) {
114
- return field("MultiSelect", name, options);
115
- }
116
- function switchField(name, options) {
117
- return field("Switch", name, options);
118
- }
119
- function submit(label) {
120
- return {
121
- root: "root",
122
- elements: {
123
- root: {
124
- type: "Button",
125
- props: { label },
126
- on: { press: { action: "submit" } }
66
+ if (options.effect !== void 0)
67
+ this.effect = options.effect;
68
+ if (options.refusalMessage !== void 0) {
69
+ if (options.effect !== "none") {
70
+ throw new TypeError('refusalMessage requires effect: "none".');
127
71
  }
72
+ this.refusalMessage = options.refusalMessage;
128
73
  }
129
- };
74
+ }
75
+ };
76
+ var UriNotFoundError = class extends UriError {
77
+ uri;
78
+ hint;
79
+ _tag = "UriNotFoundError";
80
+ constructor(uri, hint) {
81
+ super(hint !== void 0 ? `No resource found at '${uri}'. ${hint}` : `No resource found at '${uri}'.`);
82
+ this.uri = uri;
83
+ this.hint = hint;
84
+ this.name = "UriNotFoundError";
85
+ }
86
+ };
87
+ var UriUnsupportedOperationError = class extends UriError {
88
+ operation;
89
+ moduleId;
90
+ uri;
91
+ _tag = "UriUnsupportedOperationError";
92
+ constructor(operation, moduleId, uri, message) {
93
+ super(message ?? `URI module '${moduleId}' does not support '${operation}' for '${uri}'.`);
94
+ this.operation = operation;
95
+ this.moduleId = moduleId;
96
+ this.uri = uri;
97
+ this.name = "UriUnsupportedOperationError";
98
+ }
99
+ };
100
+
101
+ // packages/herd-uri/dist/resource-witness.js
102
+ import { createHash } from "node:crypto";
103
+ function contentDigest(content) {
104
+ const hash = createHash("sha256");
105
+ hash.update(typeof content === "string" ? new TextEncoder().encode(content) : content);
106
+ return `${DIGEST_PREFIX}${hash.digest("base64url")}`;
130
107
  }
108
+ var DIGEST_PREFIX = "sha256:";
131
109
 
132
- // packages/plugin-sdk/dist/signals.js
110
+ // packages/herd-uri/dist/operations.js
111
+ import { z } from "zod";
112
+ var URI_OPERATIONS = [
113
+ "help",
114
+ "read",
115
+ "write",
116
+ "edit",
117
+ "list",
118
+ "search",
119
+ "delete"
120
+ ];
121
+ var UriOperationSchema = z.enum(URI_OPERATIONS);
122
+
123
+ // packages/herd-uri/dist/help-schema.js
133
124
  import { z as z2 } from "zod";
134
- function createFallbackOnlySignals() {
135
- const noStore = { status: "unavailable", reason: "no-store" };
136
- return {
137
- observe: (input) => Promise.resolve(invalidObservation(input) ?? {
138
- status: "unavailable",
139
- reason: "no-system-one",
140
- detector: { ...input.detector, rubricDigest: "" },
141
- source: input.source,
142
- stored: false,
143
- deduplicated: false,
144
- inputDigest: "",
145
- recordedAtMs: Date.now()
146
- }),
147
- listDetectors: () => Promise.resolve(noStore),
148
- list: () => Promise.resolve(noStore)
149
- };
150
- }
151
- var SIGNAL_TIME_MAX_MS = 864e13;
152
- var MAX_CHOICE_LABELS = 64;
153
- var MAX_NAME = 64;
154
- var MAX_POINTER = 256;
155
- var MAX_QUESTIONS = 64;
156
- var DETECTOR_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
157
- var VERSION = /^[A-Za-z0-9]+(?:[._-][A-Za-z0-9]+)*$/u;
158
- var QUESTION_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
159
- var POINTER = /^[^\s\p{C}/?#]+$/u;
160
- var LABEL = /^[^\s,@\p{C}]+(?: [^\s,@\p{C}]+)*$/u;
161
- var RUN_ID = /^\P{Cc}+$/u;
162
- var POINTER_RULE = `1 to ${MAX_POINTER} characters with no whitespace, \`/\`, \`?\`, \`#\`, or control or invisible character`;
163
- var ObserveShape = z2.looseObject({
164
- detector: z2.looseObject({ id: z2.string(), version: z2.string() }),
165
- subject: z2.looseObject({ kind: z2.string(), key: z2.string().optional() }).optional(),
166
- after: z2.looseObject({ observationId: z2.unknown() }).optional(),
167
- reuse: z2.looseObject({ maxAgeMs: z2.unknown() }).optional(),
168
- source: z2.looseObject({
169
- sessionId: z2.string(),
170
- entryId: z2.string(),
171
- openerEntryId: z2.string().optional(),
172
- runId: z2.string().optional(),
173
- occurredAtMs: z2.unknown().optional()
174
- }),
175
- questions: z2.record(z2.string(), z2.unknown())
125
+ var UriContractJsonSchema = z2.record(z2.string(), z2.unknown());
126
+ var UriParameterSchema = z2.strictObject({
127
+ name: z2.string().min(1),
128
+ in: z2.enum(["uri", "query", "selector"]),
129
+ required: z2.boolean(),
130
+ description: z2.string().optional(),
131
+ schema: UriContractJsonSchema,
132
+ examples: z2.array(z2.unknown()).optional()
176
133
  });
177
- var ChoiceQuestion = z2.looseObject({
178
- type: z2.literal("choice"),
179
- criteria: z2.record(z2.string(), z2.unknown())
134
+ var UriPayloadContractSchema = z2.strictObject({
135
+ name: z2.string().min(1),
136
+ mediaType: z2.string().optional(),
137
+ description: z2.string().optional(),
138
+ schema: UriContractJsonSchema.optional(),
139
+ semanticConstraints: z2.array(z2.string().min(1)).min(1).optional(),
140
+ examples: z2.array(z2.unknown()).optional()
180
141
  });
181
- var fits = (value, grammar) => value.length <= MAX_NAME && grammar.test(value);
182
- var isWhole = (value, min) => typeof value === "number" && Number.isSafeInteger(value) && value >= min && value <= SIGNAL_TIME_MAX_MS;
183
- var quoted = (value) => JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}\u2026` : value).replace(/(?! )[\s\p{C}]/gu, (char) => `\\u${char.codePointAt(0).toString(16).padStart(4, "0")}`);
184
- function grammarProblem(input) {
185
- const shape = ObserveShape.safeParse(input);
186
- if (!shape.success) {
187
- const field2 = shape.error.issues[0]?.path.join(".") ?? "";
188
- return field2 === "" ? "`observe` takes an object with `detector`, `source`, `state`, and `questions`." : `\`${field2}\` is missing or has the wrong type.`;
189
- }
190
- const { detector, source, questions, subject, after, reuse } = shape.data;
191
- const { state } = input;
192
- if (typeof state !== "object" || state === null || Array.isArray(state)) {
193
- return "`state` must be a JSON object.";
194
- }
195
- if (!fits(detector.id, DETECTOR_ID)) {
196
- return "`detector.id` must be a lowercase slug of at most 64 characters: a-z and 0-9 in words joined by single hyphens.";
197
- }
198
- if (!fits(detector.version, VERSION)) {
199
- return "`detector.version` must be at most 64 characters: letters and digits in parts joined by single `.`, `-`, or `_`.";
200
- }
201
- for (const [field2, grammar, rule] of [
202
- ["sessionId", POINTER, POINTER_RULE],
203
- ["entryId", POINTER, POINTER_RULE],
204
- ["openerEntryId", POINTER, POINTER_RULE],
205
- [
206
- "runId",
207
- RUN_ID,
208
- `1 to ${MAX_POINTER} characters with no control character`
209
- ]
210
- ]) {
211
- const id = source[field2];
212
- if (id !== void 0 && !(id.length <= MAX_POINTER && grammar.test(id))) {
213
- return `\`source.${field2}\` must be ${rule}.`;
214
- }
215
- }
216
- const occurredAtMs = source.occurredAtMs;
217
- if (occurredAtMs !== void 0 && !(typeof occurredAtMs === "number" && Number.isSafeInteger(occurredAtMs) && occurredAtMs >= 0 && occurredAtMs <= SIGNAL_TIME_MAX_MS)) {
218
- return `\`source.occurredAtMs\` must be whole epoch milliseconds from 0 to ${SIGNAL_TIME_MAX_MS}.`;
219
- }
220
- if (subject !== void 0 && !fits(subject.kind, DETECTOR_ID)) {
221
- return "`subject.kind` must be a lowercase slug of at most 64 characters: a-z and 0-9 in words joined by single hyphens.";
142
+
143
+ // packages/herd-uri/dist/text-edit.js
144
+ import { z as z3 } from "zod";
145
+ var findReplaceArm = z3.strictObject({
146
+ find: z3.string().min(1),
147
+ replace: z3.string(),
148
+ all: z3.boolean().optional()
149
+ });
150
+ var oldNewEntry = z3.strictObject({
151
+ oldText: z3.string().min(1),
152
+ newText: z3.string()
153
+ });
154
+ var uriTextEditPayloadArms = z3.union([
155
+ findReplaceArm,
156
+ z3.strictObject({ edits: z3.array(oldNewEntry).min(1) }),
157
+ oldNewEntry,
158
+ z3.array(oldNewEntry).min(1)
159
+ ]);
160
+
161
+ // packages/herd-uri/dist/line-selector.js
162
+ var RAW_TOKEN = /^raw$/i;
163
+ function parseRangeListToken(token) {
164
+ if (token.length === 0)
165
+ return void 0;
166
+ const ranges = [];
167
+ for (const part of token.split(",")) {
168
+ const range = parseSingleRange(part);
169
+ if (range === void 0)
170
+ return void 0;
171
+ ranges.push(range);
222
172
  }
223
- if (subject?.key !== void 0 && !(subject.key.length <= MAX_POINTER && POINTER.test(subject.key))) {
224
- return `\`subject.key\` must be ${POINTER_RULE}.`;
173
+ return ranges;
174
+ }
175
+ function toInt(value) {
176
+ if (!/^\d+$/.test(value))
177
+ return void 0;
178
+ const n = Number.parseInt(value, 10);
179
+ return Number.isSafeInteger(n) && n >= 1 ? n : void 0;
180
+ }
181
+ function parseSingleRange(part) {
182
+ const plus = part.indexOf("+");
183
+ if (plus >= 0) {
184
+ const start2 = toInt(part.slice(0, plus));
185
+ const count = toInt(part.slice(plus + 1));
186
+ if (start2 === void 0 || count === void 0)
187
+ return void 0;
188
+ return { start: start2, end: start2 + count - 1 };
225
189
  }
226
- if (after !== void 0 && !isWhole(after.observationId, 1)) {
227
- return "`after.observationId` must be a positive whole number: the id of a stored signal.";
190
+ const dash = part.indexOf("-");
191
+ if (dash >= 0) {
192
+ const start2 = toInt(part.slice(0, dash));
193
+ const endRaw = part.slice(dash + 1);
194
+ if (start2 === void 0)
195
+ return void 0;
196
+ if (endRaw.length === 0)
197
+ return { start: start2, end: Number.POSITIVE_INFINITY };
198
+ const end = toInt(endRaw);
199
+ if (end === void 0 || end < start2)
200
+ return void 0;
201
+ return { start: start2, end };
228
202
  }
229
- if (reuse !== void 0) {
230
- if (!isWhole(reuse.maxAgeMs, 1)) {
231
- return `\`reuse.maxAgeMs\` must be whole milliseconds from 1 to ${SIGNAL_TIME_MAX_MS}.`;
232
- }
233
- if (subject?.key === void 0) {
234
- return "`reuse` needs `subject.key`: a stored answer is reused only for the same subject.";
203
+ const start = toInt(part);
204
+ if (start === void 0)
205
+ return void 0;
206
+ return { start, end: Number.POSITIVE_INFINITY };
207
+ }
208
+ function mergeRanges(ranges) {
209
+ const sorted = [...ranges].sort((a, b) => a.start - b.start);
210
+ const merged = [];
211
+ for (const range of sorted) {
212
+ const last = merged[merged.length - 1];
213
+ if (last !== void 0 && range.start <= last.end) {
214
+ if (range.end > last.end) {
215
+ merged[merged.length - 1] = { start: last.start, end: range.end };
216
+ }
217
+ continue;
235
218
  }
219
+ merged.push(range);
236
220
  }
237
- const names = Object.keys(questions);
238
- if (names.length === 0 || names.length > MAX_QUESTIONS) {
239
- return `\`questions\` must ask 1 to ${MAX_QUESTIONS} questions.`;
240
- }
241
- for (const name of names) {
242
- if (!fits(name, QUESTION_NAME)) {
243
- return `The question name ${quoted(name)} must be an identifier of at most 64 characters: letters, digits, and \`_\`, not starting with a digit.`;
244
- }
245
- const choice = ChoiceQuestion.safeParse(questions[name]);
246
- if (!choice.success)
221
+ return merged;
222
+ }
223
+ function parseLineSelector(input) {
224
+ let address = input;
225
+ let raw = false;
226
+ let ranges;
227
+ for (let i = 0; i < 2; i += 1) {
228
+ const colon = address.lastIndexOf(":");
229
+ if (colon < 0)
230
+ break;
231
+ const token = address.slice(colon + 1);
232
+ if (RAW_TOKEN.test(token)) {
233
+ if (raw)
234
+ break;
235
+ raw = true;
236
+ address = address.slice(0, colon);
247
237
  continue;
248
- const labels = Object.keys(choice.data.criteria);
249
- if (labels.length > MAX_CHOICE_LABELS) {
250
- return `The choice question \`${name}\` offers ${labels.length} labels; at most ${MAX_CHOICE_LABELS}.`;
251
- }
252
- const bad = labels.find((label) => !fits(label, LABEL));
253
- if (bad !== void 0) {
254
- return `The choice question \`${name}\` offers the label ${quoted(bad)}, which a \`where\` filter cannot name: a label is 1 to 64 characters with no \`,\`, \`@\`, or control or invisible character, and no whitespace but single spaces between words.`;
255
238
  }
239
+ const parsed = parseRangeListToken(token);
240
+ if (parsed === void 0)
241
+ break;
242
+ if (ranges !== void 0)
243
+ break;
244
+ ranges = parsed;
245
+ address = address.slice(0, colon);
256
246
  }
257
- return void 0;
258
- }
259
- var EchoText = z2.string().catch("");
260
- var Echo = z2.object({
261
- detector: z2.object({ id: EchoText, version: EchoText }).catch({ id: "", version: "" }),
262
- source: z2.object({ sessionId: EchoText, entryId: EchoText }).catch({ sessionId: "", entryId: "" })
263
- }).catch({
264
- detector: { id: "", version: "" },
265
- source: { sessionId: "", entryId: "" }
266
- });
267
- function invalidObservation(input) {
268
- const reason = grammarProblem(input);
269
- if (reason === void 0)
270
- return void 0;
271
- const { detector, source } = Echo.parse(input);
272
247
  return {
273
- status: "invalid",
274
- reason,
275
- detector: { ...detector, rubricDigest: "" },
276
- source,
277
- stored: false,
278
- deduplicated: false,
279
- inputDigest: "",
280
- recordedAtMs: Date.now()
248
+ address,
249
+ selector: { ranges: ranges ? mergeRanges(ranges) : [], raw }
281
250
  };
282
251
  }
283
252
 
284
- // packages/herd-transcript/dist/record-kinds.js
285
- var STORED_TRANSCRIPT_MESSAGE_KINDS = [
286
- "user",
287
- "assistant",
288
- "tool_result",
289
- "custom_message:*",
290
- "compaction",
291
- "branch_summary"
292
- ];
293
- var NAMED_KINDS = /* @__PURE__ */ new Set([
294
- "user",
295
- "assistant",
296
- "tool_result",
297
- "compaction",
298
- "branch_summary",
299
- "other",
300
- "unreadable"
301
- ]);
302
- var ROLE_KINDS = {
303
- user: "user",
304
- assistant: "assistant",
305
- toolResult: "tool_result"
306
- };
307
- function storedTranscriptKindOf(record) {
308
- if (!("entry" in record))
309
- return "unreadable";
310
- const entry = record.entry;
311
- switch (entry.type) {
312
- case "message": {
313
- const message = entry.message;
314
- if (message.role === "custom") {
315
- return `custom_message:${typeof message.customType === "string" ? message.customType : ""}`;
316
- }
317
- return (typeof message.role === "string" ? ROLE_KINDS[message.role] : void 0) ?? "other";
318
- }
319
- case "custom":
320
- return `custom:${entry.customType}`;
321
- case "compaction":
322
- return "compaction";
323
- case "branch_summary":
324
- return "branch_summary";
325
- default:
326
- return "other";
253
+ // packages/herd-uri/dist/schema/to-json-schema.js
254
+ import { z as z4 } from "zod";
255
+ function toJsonSchema(schema) {
256
+ if (schema instanceof z4.ZodType) {
257
+ const { $schema: _dialect, ...value } = z4.toJSONSchema(schema, {
258
+ // `io: "input"` describes what a caller may SEND, which is what a parameter and an
259
+ // `accepts` payload advertise: a field with a Zod default is optional on the wire even
260
+ // though the parsed value always carries it. Strictness still survives where the validator
261
+ // really enforces it - `z.strictObject` emits `additionalProperties: false`, while a plain
262
+ // `z.object` correctly does not, because it TOLERATES unknown keys and silently strips
263
+ // them. A `returns` payload derived from a Zod schema is described in the same input terms;
264
+ // no module does that today, and a faithful output projection would need its own seam.
265
+ io: "input",
266
+ // Inline every reused sub-schema rather than emitting `$ref`s into a `$defs` block. A
267
+ // contract schema is a FRAGMENT: it gets embedded in a payload contract, and callers
268
+ // legitimately lift one arm of a union out of it. A `$ref` only resolves relative to the
269
+ // root that carries `$defs`, so the moment the fragment moves, the reference dangles.
270
+ // Self-contained is worth the repetition at these sizes.
271
+ reused: "inline",
272
+ // Recursion (`z.lazy`) has no self-contained form: zod's default is to emit a
273
+ // `$defs` block with `$ref` pointers, which dangle the moment the fragment is
274
+ // embedded. Throw instead, so a contract that needs recursion has to disclose a
275
+ // non-recursive shape (see skill-module's metadata disclosure) rather than ship a
276
+ // reference nothing can resolve.
277
+ cycles: "throw"
278
+ });
279
+ return value;
327
280
  }
281
+ return structuredClone(schema);
328
282
  }
329
- function parseStoredTranscriptKindFilter(tokens) {
330
- const exact = /* @__PURE__ */ new Set();
331
- const families = /* @__PURE__ */ new Set();
332
- let all = false;
333
- for (const raw of tokens) {
334
- const token = raw.trim();
335
- if (token === "all") {
336
- all = true;
337
- } else if (token === "messages") {
338
- for (const kind of STORED_TRANSCRIPT_MESSAGE_KINDS)
339
- addToken(kind);
340
- } else if (!addToken(token)) {
341
- return { ok: false, invalid: raw };
342
- }
343
- }
283
+
284
+ // packages/herd-uri/dist/schema/helpers.js
285
+ function payload(name, schema, options = {}) {
344
286
  return {
345
- ok: true,
346
- filter: {
347
- includes: (kind) => all || exact.has(kind) || families.has(kind.slice(0, kind.indexOf(":") + 1))
348
- }
287
+ name,
288
+ ...options.mediaType === void 0 ? {} : { mediaType: options.mediaType },
289
+ ...options.description === void 0 ? {} : { description: options.description },
290
+ schema: toJsonSchema(schema),
291
+ ...options.semanticConstraints === void 0 ? {} : { semanticConstraints: options.semanticConstraints },
292
+ ...options.examples === void 0 ? {} : { examples: options.examples }
349
293
  };
350
- function addToken(token) {
351
- if (NAMED_KINDS.has(token)) {
352
- exact.add(token);
353
- return true;
354
- }
355
- const family = /^(custom|custom_message):(.+)$/.exec(token);
356
- if (family === null)
357
- return false;
358
- if (family[2] === "*")
359
- families.add(`${family[1]}:`);
360
- else
361
- exact.add(token);
362
- return true;
363
- }
364
294
  }
365
- var STORED_TRANSCRIPT_MESSAGE_FILTER = (() => {
366
- const parsed = parseStoredTranscriptKindFilter(STORED_TRANSCRIPT_MESSAGE_KINDS);
367
- if (!parsed.ok)
368
- throw new Error(`invalid default kinds: ${parsed.invalid}`);
369
- return parsed.filter;
370
- })();
371
295
 
372
- // packages/plugin-sdk/dist/kay-app/index.js
373
- import { pathToFileURL } from "node:url";
296
+ // packages/plugin-sdk/dist/uri.js
297
+ function createUnavailableUriCaller(message) {
298
+ const unavailable = async () => {
299
+ throw new Error(message);
300
+ };
301
+ return {
302
+ read: unavailable,
303
+ list: unavailable,
304
+ search: unavailable,
305
+ write: unavailable,
306
+ edit: unavailable,
307
+ delete: unavailable
308
+ };
309
+ }
310
+
311
+ // packages/plugin-sdk/dist/endpoints.js
312
+ function createUnavailablePluginEndpointCaller(message) {
313
+ return {
314
+ invokePlugin: async () => ({ ok: false, reason: "unavailable", message })
315
+ };
316
+ }
317
+
318
+ // packages/plugin-protocol/dist/plugin-signal-limits.js
319
+ var PLUGIN_EVENT_REFUSED_ERROR_NAME = "PluginEventRefusedError";
320
+ var PLUGIN_SIGNAL_KINDS = [
321
+ "message",
322
+ "request",
323
+ "mention",
324
+ "invite",
325
+ "status"
326
+ ];
327
+ var PLUGIN_SIGNAL_TITLE_MAX = 300;
328
+ var PLUGIN_SIGNAL_SNIPPET_MAX = 500;
329
+ var PLUGIN_SIGNAL_LABELS_MAX = 20;
330
+ var PLUGIN_SIGNAL_LABEL_MAX = 64;
331
+ var PLUGIN_SIGNAL_RELATED_KEYS = [
332
+ "thread",
333
+ "from",
334
+ "to",
335
+ "cc"
336
+ ];
337
+
338
+ // packages/plugin-protocol/dist/credential-text.js
339
+ var CREDENTIAL_VALUE_PATTERNS = [
340
+ // Anthropic / OpenAI-style secret keys (`sk-`, `sk-ant-`, `sk-proj-`).
341
+ /^sk-[A-Za-z0-9_-]{16,}$/u,
342
+ // GitHub classic, fine-grained, app, and OAuth tokens.
343
+ /^gh[pousr]_[A-Za-z0-9]{16,}$/u,
344
+ /^github_pat_[A-Za-z0-9_]{20,}$/u,
345
+ // GitLab personal access tokens.
346
+ /^glpat-[A-Za-z0-9_-]{16,}$/u,
347
+ // Slack bot/user/app tokens.
348
+ /^xox[baprse]-[A-Za-z0-9-]{10,}$/u,
349
+ // AWS access key ids, long-lived and STS-issued.
350
+ /^(?:AKIA|ASIA)[0-9A-Z]{16}$/u,
351
+ // Google API keys, both published shapes.
352
+ /^AIza[0-9A-Za-z_-]{35}$/u,
353
+ /^AQ\.[A-Za-z0-9_-]{20,}$/u,
354
+ // npm automation tokens.
355
+ /^npm_[A-Za-z0-9]{30,}$/u,
356
+ // SendGrid API keys (SG.<key id>.<secret>).
357
+ /^SG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}$/u,
358
+ // HashiCorp Vault service/batch/recovery tokens.
359
+ /^hv[sbr]\.[A-Za-z0-9_-]{20,}$/u,
360
+ // Shopify access tokens.
361
+ /^shp(?:at|ss|ca|pa)_[a-fA-F0-9]{32}$/u,
362
+ // Stripe live/test secret and restricted keys.
363
+ /^(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}$/u,
364
+ // JSON Web Tokens - header.payload.signature, header always starts `eyJ`.
365
+ /^eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/u,
366
+ // A whole Authorization header value carried as data.
367
+ /^(?:Bearer|Basic) [A-Za-z0-9+/._~-]{16,}={0,2}$/iu,
368
+ // PEM private key blocks, anywhere in the string.
369
+ /-----BEGIN (?:[A-Z]+ )?PRIVATE KEY-----/u
370
+ ];
371
+ function isCredentialShapedText(value) {
372
+ if (typeof value !== "string" || value.length === 0)
373
+ return false;
374
+ return CREDENTIAL_VALUE_PATTERNS.some((pattern) => pattern.test(value));
375
+ }
376
+
377
+ // packages/plugin-protocol/dist/json.js
378
+ import { z as z5 } from "zod";
379
+ var JsonValue = z5.lazy(() => z5.union([
380
+ z5.string(),
381
+ z5.number(),
382
+ z5.boolean(),
383
+ z5.null(),
384
+ z5.array(JsonValue),
385
+ z5.record(z5.string(), JsonValue)
386
+ ]));
387
+ var JsonObject = z5.record(z5.string(), JsonValue);
388
+ var JsonSchema = z5.record(z5.string(), JsonValue);
374
389
 
375
- // packages/herd-uri/dist/errors.js
376
- var UriError = class extends Error {
377
- };
378
- var CONFLICT_MESSAGES = {
379
- changed: "This resource changed since this session last read it. Read it again before modifying it.",
380
- unwitnessed: "This resource was not read in this session. Read it before modifying it.",
381
- unknown: "This resource changed or was not read in this session. Read it again before modifying it."
382
- };
383
- var UriConflictError = class extends UriError {
384
- _tag = "UriConflictError";
385
- reason;
386
- constructor(reason) {
387
- super(CONFLICT_MESSAGES[reason ?? "unknown"]);
388
- this.name = "UriConflictError";
389
- this.reason = reason;
390
- }
391
- };
392
- var UriValidationError = class extends UriError {
393
- _tag = "UriValidationError";
394
- constructor(message) {
395
- super(message);
396
- this.name = "UriValidationError";
397
- }
398
- };
399
- function requireWriteContent(payload2, uri) {
400
- if (payload2 === void 0) {
401
- throw new UriValidationError(`Writing ${uri} needs \`content\` (or a \`source\` to copy from): pass the payload this address takes, which its help names.`);
402
- }
403
- if (payload2 instanceof Uint8Array) {
404
- throw new UriValidationError(`${uri} stores text only, so it cannot take raw bytes. Copy the file to an address that accepts bytes.`);
405
- }
406
- return payload2;
390
+ // packages/plugin-sdk/dist/kay-app/card-layout.js
391
+ function element(type, props) {
392
+ return { root: "root", elements: { root: { type, props } } };
407
393
  }
408
- var UriProviderError = class extends UriError {
409
- _tag = "UriProviderError";
410
- moduleId;
411
- operation;
412
- uri;
413
- status;
414
- providerCode;
415
- retryable;
416
- /** Provider-directed delay before retrying, in milliseconds. */
417
- retryAfterMs;
418
- /** See {@link UriProviderErrorOptions.effect}. */
419
- effect;
420
- /** See {@link UriProviderErrorOptions.refusalMessage}. */
421
- refusalMessage;
422
- constructor(options) {
423
- super(options.message);
424
- this.name = "UriProviderError";
425
- this.moduleId = options.moduleId;
426
- this.operation = options.operation;
427
- this.uri = options.uri;
428
- if (options.status !== void 0)
429
- this.status = options.status;
430
- if (options.providerCode !== void 0)
431
- this.providerCode = options.providerCode;
432
- if (options.retryable !== void 0)
433
- this.retryable = options.retryable;
434
- if (options.retryAfterMs !== void 0) {
435
- if (!Number.isInteger(options.retryAfterMs) || options.retryAfterMs < 0) {
436
- throw new TypeError("retryAfterMs must be a nonnegative integer.");
437
- }
438
- this.retryAfterMs = options.retryAfterMs;
394
+ function group(type, props, children) {
395
+ const layout = element(type, props);
396
+ layout.elements.root.children = children.map((child, index) => {
397
+ const prefix = `${index}-`;
398
+ for (const [key, value] of Object.entries(child.elements)) {
399
+ layout.elements[prefix + key] = {
400
+ ...value,
401
+ ...value.children ? { children: value.children.map((id) => prefix + id) } : {}
402
+ };
439
403
  }
440
- if (options.effect !== void 0)
441
- this.effect = options.effect;
442
- if (options.refusalMessage !== void 0) {
443
- if (options.effect !== "none") {
444
- throw new TypeError('refusalMessage requires effect: "none".');
404
+ return prefix + child.root;
405
+ });
406
+ return layout;
407
+ }
408
+ function card(title, children, state) {
409
+ return { ...group("Card", { title }, children), ...state ? { state } : {} };
410
+ }
411
+ function row(children) {
412
+ return group("Row", {}, children);
413
+ }
414
+ function stack(children) {
415
+ return group("Stack", {}, children);
416
+ }
417
+ function field(type, name, options) {
418
+ const key = name.replaceAll("~", "~0").replaceAll("/", "~1");
419
+ return element(type, { ...options, value: { $bindState: `/form/${key}` } });
420
+ }
421
+ function textField(name, options) {
422
+ return field("Field", name, options);
423
+ }
424
+ function textArea(name, options) {
425
+ return field("TextArea", name, options);
426
+ }
427
+ function select(name, options) {
428
+ return field("Select", name, options);
429
+ }
430
+ function multiSelect(name, options) {
431
+ return field("MultiSelect", name, options);
432
+ }
433
+ function switchField(name, options) {
434
+ return field("Switch", name, options);
435
+ }
436
+ function submit(label) {
437
+ return {
438
+ root: "root",
439
+ elements: {
440
+ root: {
441
+ type: "Button",
442
+ props: { label },
443
+ on: { press: { action: "submit" } }
445
444
  }
446
- this.refusalMessage = options.refusalMessage;
447
445
  }
448
- }
449
- };
450
- var UriNotFoundError = class extends UriError {
451
- uri;
452
- hint;
453
- _tag = "UriNotFoundError";
454
- constructor(uri, hint) {
455
- super(hint !== void 0 ? `No resource found at '${uri}'. ${hint}` : `No resource found at '${uri}'.`);
456
- this.uri = uri;
457
- this.hint = hint;
458
- this.name = "UriNotFoundError";
459
- }
460
- };
461
- var UriUnsupportedOperationError = class extends UriError {
462
- operation;
463
- moduleId;
464
- uri;
465
- _tag = "UriUnsupportedOperationError";
466
- constructor(operation, moduleId, uri, message) {
467
- super(message ?? `URI module '${moduleId}' does not support '${operation}' for '${uri}'.`);
468
- this.operation = operation;
469
- this.moduleId = moduleId;
470
- this.uri = uri;
471
- this.name = "UriUnsupportedOperationError";
472
- }
473
- };
474
-
475
- // packages/herd-uri/dist/operations.js
476
- import { z as z3 } from "zod";
477
- var URI_OPERATIONS = [
478
- "help",
479
- "read",
480
- "write",
481
- "edit",
482
- "list",
483
- "search",
484
- "delete"
485
- ];
486
- var UriOperationSchema = z3.enum(URI_OPERATIONS);
487
-
488
- // packages/herd-uri/dist/help-schema.js
489
- import { z as z4 } from "zod";
490
- var UriContractJsonSchema = z4.record(z4.string(), z4.unknown());
491
- var UriParameterSchema = z4.strictObject({
492
- name: z4.string().min(1),
493
- in: z4.enum(["uri", "query", "selector"]),
494
- required: z4.boolean(),
495
- description: z4.string().optional(),
496
- schema: UriContractJsonSchema,
497
- examples: z4.array(z4.unknown()).optional()
498
- });
499
- var UriPayloadContractSchema = z4.strictObject({
500
- name: z4.string().min(1),
501
- mediaType: z4.string().optional(),
502
- description: z4.string().optional(),
503
- schema: UriContractJsonSchema.optional(),
504
- semanticConstraints: z4.array(z4.string().min(1)).min(1).optional(),
505
- examples: z4.array(z4.unknown()).optional()
506
- });
446
+ };
447
+ }
507
448
 
508
- // packages/herd-uri/dist/text-edit.js
509
- import { z as z5 } from "zod";
510
- var findReplaceArm = z5.strictObject({
511
- find: z5.string().min(1),
512
- replace: z5.string(),
513
- all: z5.boolean().optional()
449
+ // packages/plugin-sdk/dist/signals.js
450
+ import { z as z6 } from "zod";
451
+ function createFallbackOnlySignals() {
452
+ const noStore = { status: "unavailable", reason: "no-store" };
453
+ return {
454
+ observe: (input) => Promise.resolve(invalidObservation(input) ?? {
455
+ status: "unavailable",
456
+ reason: "no-system-one",
457
+ detector: { ...input.detector, rubricDigest: "" },
458
+ source: input.source,
459
+ stored: false,
460
+ deduplicated: false,
461
+ inputDigest: "",
462
+ recordedAtMs: Date.now()
463
+ }),
464
+ listDetectors: () => Promise.resolve(noStore),
465
+ list: () => Promise.resolve(noStore)
466
+ };
467
+ }
468
+ var SIGNAL_TIME_MAX_MS = 864e13;
469
+ var MAX_CHOICE_LABELS = 64;
470
+ var MAX_NAME = 64;
471
+ var MAX_POINTER = 256;
472
+ var MAX_QUESTIONS = 64;
473
+ var DETECTOR_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
474
+ var VERSION = /^[A-Za-z0-9]+(?:[._-][A-Za-z0-9]+)*$/u;
475
+ var QUESTION_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
476
+ var POINTER = /^[^\s\p{C}/?#]+$/u;
477
+ var LABEL = /^[^\s,@\p{C}]+(?: [^\s,@\p{C}]+)*$/u;
478
+ var RUN_ID = /^\P{Cc}+$/u;
479
+ var POINTER_RULE = `1 to ${MAX_POINTER} characters with no whitespace, \`/\`, \`?\`, \`#\`, or control or invisible character`;
480
+ var ObserveShape = z6.looseObject({
481
+ detector: z6.looseObject({ id: z6.string(), version: z6.string() }),
482
+ subject: z6.looseObject({ kind: z6.string(), key: z6.string().optional() }).optional(),
483
+ after: z6.looseObject({ observationId: z6.unknown() }).optional(),
484
+ reuse: z6.looseObject({ maxAgeMs: z6.unknown() }).optional(),
485
+ source: z6.looseObject({
486
+ sessionId: z6.string(),
487
+ entryId: z6.string(),
488
+ openerEntryId: z6.string().optional(),
489
+ runId: z6.string().optional(),
490
+ occurredAtMs: z6.unknown().optional()
491
+ }),
492
+ questions: z6.record(z6.string(), z6.unknown())
514
493
  });
515
- var oldNewEntry = z5.strictObject({
516
- oldText: z5.string().min(1),
517
- newText: z5.string()
494
+ var ChoiceQuestion = z6.looseObject({
495
+ type: z6.literal("choice"),
496
+ criteria: z6.record(z6.string(), z6.unknown())
518
497
  });
519
- var uriTextEditPayloadArms = z5.union([
520
- findReplaceArm,
521
- z5.strictObject({ edits: z5.array(oldNewEntry).min(1) }),
522
- oldNewEntry,
523
- z5.array(oldNewEntry).min(1)
524
- ]);
525
-
526
- // packages/herd-uri/dist/line-selector.js
527
- var RAW_TOKEN = /^raw$/i;
528
- function parseRangeListToken(token) {
529
- if (token.length === 0)
530
- return void 0;
531
- const ranges = [];
532
- for (const part of token.split(",")) {
533
- const range = parseSingleRange(part);
534
- if (range === void 0)
535
- return void 0;
536
- ranges.push(range);
498
+ var fits = (value, grammar) => value.length <= MAX_NAME && grammar.test(value);
499
+ var isWhole = (value, min) => typeof value === "number" && Number.isSafeInteger(value) && value >= min && value <= SIGNAL_TIME_MAX_MS;
500
+ var quoted = (value) => JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}\u2026` : value).replace(/(?! )[\s\p{C}]/gu, (char) => `\\u${char.codePointAt(0).toString(16).padStart(4, "0")}`);
501
+ function grammarProblem(input) {
502
+ const shape = ObserveShape.safeParse(input);
503
+ if (!shape.success) {
504
+ const field2 = shape.error.issues[0]?.path.join(".") ?? "";
505
+ return field2 === "" ? "`observe` takes an object with `detector`, `source`, `state`, and `questions`." : `\`${field2}\` is missing or has the wrong type.`;
506
+ }
507
+ const { detector, source, questions, subject, after, reuse } = shape.data;
508
+ const { state } = input;
509
+ if (typeof state !== "object" || state === null || Array.isArray(state)) {
510
+ return "`state` must be a JSON object.";
511
+ }
512
+ if (!fits(detector.id, DETECTOR_ID)) {
513
+ return "`detector.id` must be a lowercase slug of at most 64 characters: a-z and 0-9 in words joined by single hyphens.";
514
+ }
515
+ if (!fits(detector.version, VERSION)) {
516
+ return "`detector.version` must be at most 64 characters: letters and digits in parts joined by single `.`, `-`, or `_`.";
517
+ }
518
+ for (const [field2, grammar, rule] of [
519
+ ["sessionId", POINTER, POINTER_RULE],
520
+ ["entryId", POINTER, POINTER_RULE],
521
+ ["openerEntryId", POINTER, POINTER_RULE],
522
+ [
523
+ "runId",
524
+ RUN_ID,
525
+ `1 to ${MAX_POINTER} characters with no control character`
526
+ ]
527
+ ]) {
528
+ const id = source[field2];
529
+ if (id !== void 0 && !(id.length <= MAX_POINTER && grammar.test(id))) {
530
+ return `\`source.${field2}\` must be ${rule}.`;
531
+ }
537
532
  }
538
- return ranges;
539
- }
540
- function toInt(value) {
541
- if (!/^\d+$/.test(value))
542
- return void 0;
543
- const n = Number.parseInt(value, 10);
544
- return Number.isSafeInteger(n) && n >= 1 ? n : void 0;
545
- }
546
- function parseSingleRange(part) {
547
- const plus = part.indexOf("+");
548
- if (plus >= 0) {
549
- const start2 = toInt(part.slice(0, plus));
550
- const count = toInt(part.slice(plus + 1));
551
- if (start2 === void 0 || count === void 0)
552
- return void 0;
553
- return { start: start2, end: start2 + count - 1 };
533
+ const occurredAtMs = source.occurredAtMs;
534
+ if (occurredAtMs !== void 0 && !(typeof occurredAtMs === "number" && Number.isSafeInteger(occurredAtMs) && occurredAtMs >= 0 && occurredAtMs <= SIGNAL_TIME_MAX_MS)) {
535
+ return `\`source.occurredAtMs\` must be whole epoch milliseconds from 0 to ${SIGNAL_TIME_MAX_MS}.`;
554
536
  }
555
- const dash = part.indexOf("-");
556
- if (dash >= 0) {
557
- const start2 = toInt(part.slice(0, dash));
558
- const endRaw = part.slice(dash + 1);
559
- if (start2 === void 0)
560
- return void 0;
561
- if (endRaw.length === 0)
562
- return { start: start2, end: Number.POSITIVE_INFINITY };
563
- const end = toInt(endRaw);
564
- if (end === void 0 || end < start2)
565
- return void 0;
566
- return { start: start2, end };
537
+ if (subject !== void 0 && !fits(subject.kind, DETECTOR_ID)) {
538
+ return "`subject.kind` must be a lowercase slug of at most 64 characters: a-z and 0-9 in words joined by single hyphens.";
567
539
  }
568
- const start = toInt(part);
569
- if (start === void 0)
570
- return void 0;
571
- return { start, end: Number.POSITIVE_INFINITY };
572
- }
573
- function mergeRanges(ranges) {
574
- const sorted = [...ranges].sort((a, b) => a.start - b.start);
575
- const merged = [];
576
- for (const range of sorted) {
577
- const last = merged[merged.length - 1];
578
- if (last !== void 0 && range.start <= last.end) {
579
- if (range.end > last.end) {
580
- merged[merged.length - 1] = { start: last.start, end: range.end };
581
- }
582
- continue;
540
+ if (subject?.key !== void 0 && !(subject.key.length <= MAX_POINTER && POINTER.test(subject.key))) {
541
+ return `\`subject.key\` must be ${POINTER_RULE}.`;
542
+ }
543
+ if (after !== void 0 && !isWhole(after.observationId, 1)) {
544
+ return "`after.observationId` must be a positive whole number: the id of a stored signal.";
545
+ }
546
+ if (reuse !== void 0) {
547
+ if (!isWhole(reuse.maxAgeMs, 1)) {
548
+ return `\`reuse.maxAgeMs\` must be whole milliseconds from 1 to ${SIGNAL_TIME_MAX_MS}.`;
549
+ }
550
+ if (subject?.key === void 0) {
551
+ return "`reuse` needs `subject.key`: a stored answer is reused only for the same subject.";
583
552
  }
584
- merged.push(range);
585
553
  }
586
- return merged;
587
- }
588
- function parseLineSelector(input) {
589
- let address = input;
590
- let raw = false;
591
- let ranges;
592
- for (let i = 0; i < 2; i += 1) {
593
- const colon = address.lastIndexOf(":");
594
- if (colon < 0)
595
- break;
596
- const token = address.slice(colon + 1);
597
- if (RAW_TOKEN.test(token)) {
598
- if (raw)
599
- break;
600
- raw = true;
601
- address = address.slice(0, colon);
554
+ const names = Object.keys(questions);
555
+ if (names.length === 0 || names.length > MAX_QUESTIONS) {
556
+ return `\`questions\` must ask 1 to ${MAX_QUESTIONS} questions.`;
557
+ }
558
+ for (const name of names) {
559
+ if (!fits(name, QUESTION_NAME)) {
560
+ return `The question name ${quoted(name)} must be an identifier of at most 64 characters: letters, digits, and \`_\`, not starting with a digit.`;
561
+ }
562
+ const choice = ChoiceQuestion.safeParse(questions[name]);
563
+ if (!choice.success)
602
564
  continue;
565
+ const labels = Object.keys(choice.data.criteria);
566
+ if (labels.length > MAX_CHOICE_LABELS) {
567
+ return `The choice question \`${name}\` offers ${labels.length} labels; at most ${MAX_CHOICE_LABELS}.`;
568
+ }
569
+ const bad = labels.find((label) => !fits(label, LABEL));
570
+ if (bad !== void 0) {
571
+ return `The choice question \`${name}\` offers the label ${quoted(bad)}, which a \`where\` filter cannot name: a label is 1 to 64 characters with no \`,\`, \`@\`, or control or invisible character, and no whitespace but single spaces between words.`;
603
572
  }
604
- const parsed = parseRangeListToken(token);
605
- if (parsed === void 0)
606
- break;
607
- if (ranges !== void 0)
608
- break;
609
- ranges = parsed;
610
- address = address.slice(0, colon);
611
573
  }
574
+ return void 0;
575
+ }
576
+ var EchoText = z6.string().catch("");
577
+ var Echo = z6.object({
578
+ detector: z6.object({ id: EchoText, version: EchoText }).catch({ id: "", version: "" }),
579
+ source: z6.object({ sessionId: EchoText, entryId: EchoText }).catch({ sessionId: "", entryId: "" })
580
+ }).catch({
581
+ detector: { id: "", version: "" },
582
+ source: { sessionId: "", entryId: "" }
583
+ });
584
+ function invalidObservation(input) {
585
+ const reason = grammarProblem(input);
586
+ if (reason === void 0)
587
+ return void 0;
588
+ const { detector, source } = Echo.parse(input);
612
589
  return {
613
- address,
614
- selector: { ranges: ranges ? mergeRanges(ranges) : [], raw }
590
+ status: "invalid",
591
+ reason,
592
+ detector: { ...detector, rubricDigest: "" },
593
+ source,
594
+ stored: false,
595
+ deduplicated: false,
596
+ inputDigest: "",
597
+ recordedAtMs: Date.now()
615
598
  };
616
599
  }
617
600
 
618
- // packages/herd-uri/dist/schema/to-json-schema.js
619
- import { z as z6 } from "zod";
620
- function toJsonSchema(schema) {
621
- if (schema instanceof z6.ZodType) {
622
- const { $schema: _dialect, ...value } = z6.toJSONSchema(schema, {
623
- // `io: "input"` describes what a caller may SEND, which is what a parameter and an
624
- // `accepts` payload advertise: a field with a Zod default is optional on the wire even
625
- // though the parsed value always carries it. Strictness still survives where the validator
626
- // really enforces it - `z.strictObject` emits `additionalProperties: false`, while a plain
627
- // `z.object` correctly does not, because it TOLERATES unknown keys and silently strips
628
- // them. A `returns` payload derived from a Zod schema is described in the same input terms;
629
- // no module does that today, and a faithful output projection would need its own seam.
630
- io: "input",
631
- // Inline every reused sub-schema rather than emitting `$ref`s into a `$defs` block. A
632
- // contract schema is a FRAGMENT: it gets embedded in a payload contract, and callers
633
- // legitimately lift one arm of a union out of it. A `$ref` only resolves relative to the
634
- // root that carries `$defs`, so the moment the fragment moves, the reference dangles.
635
- // Self-contained is worth the repetition at these sizes.
636
- reused: "inline",
637
- // Recursion (`z.lazy`) has no self-contained form: zod's default is to emit a
638
- // `$defs` block with `$ref` pointers, which dangle the moment the fragment is
639
- // embedded. Throw instead, so a contract that needs recursion has to disclose a
640
- // non-recursive shape (see skill-module's metadata disclosure) rather than ship a
641
- // reference nothing can resolve.
642
- cycles: "throw"
643
- });
644
- return value;
601
+ // packages/herd-transcript/dist/record-kinds.js
602
+ var STORED_TRANSCRIPT_MESSAGE_KINDS = [
603
+ "user",
604
+ "assistant",
605
+ "tool_result",
606
+ "custom_message:*",
607
+ "compaction",
608
+ "branch_summary"
609
+ ];
610
+ var NAMED_KINDS = /* @__PURE__ */ new Set([
611
+ "user",
612
+ "assistant",
613
+ "tool_result",
614
+ "compaction",
615
+ "branch_summary",
616
+ "other",
617
+ "unreadable"
618
+ ]);
619
+ var ROLE_KINDS = {
620
+ user: "user",
621
+ assistant: "assistant",
622
+ toolResult: "tool_result"
623
+ };
624
+ function storedTranscriptKindOf(record) {
625
+ if (!("entry" in record))
626
+ return "unreadable";
627
+ const entry = record.entry;
628
+ switch (entry.type) {
629
+ case "message": {
630
+ const message = entry.message;
631
+ if (message.role === "custom") {
632
+ return `custom_message:${typeof message.customType === "string" ? message.customType : ""}`;
633
+ }
634
+ return (typeof message.role === "string" ? ROLE_KINDS[message.role] : void 0) ?? "other";
635
+ }
636
+ case "custom":
637
+ return `custom:${entry.customType}`;
638
+ case "compaction":
639
+ return "compaction";
640
+ case "branch_summary":
641
+ return "branch_summary";
642
+ default:
643
+ return "other";
645
644
  }
646
- return structuredClone(schema);
647
645
  }
648
-
649
- // packages/herd-uri/dist/schema/helpers.js
650
- function payload(name, schema, options = {}) {
646
+ function parseStoredTranscriptKindFilter(tokens) {
647
+ const exact = /* @__PURE__ */ new Set();
648
+ const families = /* @__PURE__ */ new Set();
649
+ let all = false;
650
+ for (const raw of tokens) {
651
+ const token = raw.trim();
652
+ if (token === "all") {
653
+ all = true;
654
+ } else if (token === "messages") {
655
+ for (const kind of STORED_TRANSCRIPT_MESSAGE_KINDS)
656
+ addToken(kind);
657
+ } else if (!addToken(token)) {
658
+ return { ok: false, invalid: raw };
659
+ }
660
+ }
651
661
  return {
652
- name,
653
- ...options.mediaType === void 0 ? {} : { mediaType: options.mediaType },
654
- ...options.description === void 0 ? {} : { description: options.description },
655
- schema: toJsonSchema(schema),
656
- ...options.semanticConstraints === void 0 ? {} : { semanticConstraints: options.semanticConstraints },
657
- ...options.examples === void 0 ? {} : { examples: options.examples }
662
+ ok: true,
663
+ filter: {
664
+ includes: (kind) => all || exact.has(kind) || families.has(kind.slice(0, kind.indexOf(":") + 1))
665
+ }
658
666
  };
667
+ function addToken(token) {
668
+ if (NAMED_KINDS.has(token)) {
669
+ exact.add(token);
670
+ return true;
671
+ }
672
+ const family = /^(custom|custom_message):(.+)$/.exec(token);
673
+ if (family === null)
674
+ return false;
675
+ if (family[2] === "*")
676
+ families.add(`${family[1]}:`);
677
+ else
678
+ exact.add(token);
679
+ return true;
680
+ }
659
681
  }
682
+ var STORED_TRANSCRIPT_MESSAGE_FILTER = (() => {
683
+ const parsed = parseStoredTranscriptKindFilter(STORED_TRANSCRIPT_MESSAGE_KINDS);
684
+ if (!parsed.ok)
685
+ throw new Error(`invalid default kinds: ${parsed.invalid}`);
686
+ return parsed.filter;
687
+ })();
688
+
689
+ // packages/plugin-sdk/dist/kay-app/index.js
690
+ import { pathToFileURL } from "node:url";
660
691
 
661
692
  // packages/plugin-sdk/dist/kay-app/auth.js
662
693
  var KayAppCredentialRejectedError = class extends Error {
@@ -1078,12 +1109,15 @@ export {
1078
1109
  UriUnsupportedOperationError,
1079
1110
  UriValidationError,
1080
1111
  card,
1112
+ contentDigest,
1081
1113
  createConsoleKayPluginLog,
1082
1114
  createFallbackOnlySignals,
1083
1115
  createKayPluginLog,
1116
+ createUnavailablePluginEndpointCaller,
1084
1117
  createUnavailableSealing,
1085
1118
  createUnavailableSkills,
1086
1119
  createUnavailableTools,
1120
+ createUnavailableUriCaller,
1087
1121
  createUnavailableUsage,
1088
1122
  declaredAppToolDefinition,
1089
1123
  definePoller,