@yaklabs-ai/plugin-sdk 0.52.1 → 0.54.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
@@ -308,6 +308,31 @@ function createUnavailableUriCaller(message) {
308
308
  };
309
309
  }
310
310
 
311
+ // packages/plugin-protocol/dist/project-directories.js
312
+ import { z as z5 } from "zod";
313
+ var PluginProjectDirectories = z5.strictObject({
314
+ /** An endpoint this plugin's app target declares; called with a {@link ProjectDirectoryRequest}. */
315
+ endpoint: z5.string().min(1),
316
+ /** A scheme this plugin's URI modules serve; a project is addressed as `<scheme>://<id>`. */
317
+ scheme: z5.string().min(1)
318
+ });
319
+ var ProjectDirectoryRequest = z5.strictObject({
320
+ /** The project's stable id. */
321
+ id: z5.string().min(1)
322
+ });
323
+ var ProjectDirectory = z5.strictObject({
324
+ id: z5.string().min(1),
325
+ /** Absolute path of the project's folder. */
326
+ path: z5.string().min(1),
327
+ /** Absolute path a new session in this project starts in. */
328
+ defaultCwd: z5.string().min(1),
329
+ /** Ancestor project ids, root first. */
330
+ ancestorIds: z5.array(z5.string().min(1))
331
+ });
332
+ var ProjectDirectoryResponse = z5.strictObject({
333
+ project: ProjectDirectory.nullable()
334
+ });
335
+
311
336
  // packages/plugin-sdk/dist/endpoints.js
312
337
  function createUnavailablePluginEndpointCaller(message) {
313
338
  return {
@@ -374,41 +399,48 @@ function isCredentialShapedText(value) {
374
399
  return CREDENTIAL_VALUE_PATTERNS.some((pattern) => pattern.test(value));
375
400
  }
376
401
 
402
+ // packages/plugin-protocol/dist/resource-invocation-cursor.js
403
+ import { z as z6 } from "zod";
404
+ var CorpusResourceInvocationCursor = z6.strictObject({
405
+ at: z6.iso.datetime({ offset: true }),
406
+ eventId: z6.string().min(1)
407
+ });
408
+
377
409
  // 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)
410
+ import { z as z7 } from "zod";
411
+ var JsonValue = z7.lazy(() => z7.union([
412
+ z7.string(),
413
+ z7.number(),
414
+ z7.boolean(),
415
+ z7.null(),
416
+ z7.array(JsonValue),
417
+ z7.record(z7.string(), JsonValue)
386
418
  ]));
387
- var JsonObject = z5.record(z5.string(), JsonValue);
388
- var JsonSchema = z5.record(z5.string(), JsonValue);
419
+ var JsonObject = z7.record(z7.string(), JsonValue);
420
+ var JsonSchema = z7.record(z7.string(), JsonValue);
389
421
 
390
422
  // packages/plugin-protocol/dist/shell-commands.js
391
- import { z as z6 } from "zod";
392
- var PluginShellCommand = z6.strictObject({
393
- name: z6.string().regex(/^[a-z][a-z0-9-]*$/),
394
- aliases: z6.array(z6.string().regex(/^[a-z][a-z0-9-]*$/)).optional(),
395
- endpoint: z6.string().min(1),
396
- usage: z6.string().min(1)
423
+ import { z as z8 } from "zod";
424
+ var PluginShellCommand = z8.strictObject({
425
+ name: z8.string().regex(/^[a-z][a-z0-9-]*$/),
426
+ aliases: z8.array(z8.string().regex(/^[a-z][a-z0-9-]*$/)).optional(),
427
+ endpoint: z8.string().min(1),
428
+ usage: z8.string().min(1)
397
429
  });
398
- var ShellCommandOutcome = z6.strictObject({
399
- exitCode: z6.number().int(),
430
+ var ShellCommandOutcome = z8.strictObject({
431
+ exitCode: z8.number().int(),
400
432
  envelope: JsonValue,
401
433
  result: JsonValue.optional()
402
434
  });
403
- var ShellCommandRequest = z6.strictObject({
404
- argv: z6.array(z6.string()),
405
- cwd: z6.string(),
406
- outcomes: z6.array(ShellCommandOutcome).max(3)
435
+ var ShellCommandRequest = z8.strictObject({
436
+ argv: z8.array(z8.string()),
437
+ cwd: z8.string(),
438
+ outcomes: z8.array(ShellCommandOutcome).max(3)
407
439
  });
408
- var ShellCommandStep = z6.discriminatedUnion("kind", [
409
- z6.strictObject({
410
- kind: z6.literal("invoke"),
411
- toolName: z6.enum([
440
+ var ShellCommandStep = z8.discriminatedUnion("kind", [
441
+ z8.strictObject({
442
+ kind: z8.literal("invoke"),
443
+ toolName: z8.enum([
412
444
  "read",
413
445
  "list",
414
446
  "search",
@@ -417,13 +449,13 @@ var ShellCommandStep = z6.discriminatedUnion("kind", [
417
449
  "delete",
418
450
  "help"
419
451
  ]),
420
- input: z6.record(z6.string(), JsonValue),
421
- flags: z6.record(z6.string(), z6.string())
452
+ input: z8.record(z8.string(), JsonValue),
453
+ flags: z8.record(z8.string(), z8.string())
422
454
  }),
423
- z6.strictObject({
424
- kind: z6.literal("finish"),
425
- exitCode: z6.number().int(),
426
- stdout: z6.string()
455
+ z8.strictObject({
456
+ kind: z8.literal("finish"),
457
+ exitCode: z8.number().int(),
458
+ stdout: z8.string()
427
459
  })
428
460
  ]);
429
461
 
@@ -487,7 +519,7 @@ function submit(label) {
487
519
  }
488
520
 
489
521
  // packages/plugin-sdk/dist/signals.js
490
- import { z as z7 } from "zod";
522
+ import { z as z9 } from "zod";
491
523
  function createFallbackOnlySignals() {
492
524
  const noStore = { status: "unavailable", reason: "no-store" };
493
525
  return {
@@ -517,23 +549,23 @@ var POINTER = /^[^\s\p{C}/?#]+$/u;
517
549
  var LABEL = /^[^\s,@\p{C}]+(?: [^\s,@\p{C}]+)*$/u;
518
550
  var RUN_ID = /^\P{Cc}+$/u;
519
551
  var POINTER_RULE = `1 to ${MAX_POINTER} characters with no whitespace, \`/\`, \`?\`, \`#\`, or control or invisible character`;
520
- var ObserveShape = z7.looseObject({
521
- detector: z7.looseObject({ id: z7.string(), version: z7.string() }),
522
- subject: z7.looseObject({ kind: z7.string(), key: z7.string().optional() }).optional(),
523
- after: z7.looseObject({ observationId: z7.unknown() }).optional(),
524
- reuse: z7.looseObject({ maxAgeMs: z7.unknown() }).optional(),
525
- source: z7.looseObject({
526
- sessionId: z7.string(),
527
- entryId: z7.string(),
528
- openerEntryId: z7.string().optional(),
529
- runId: z7.string().optional(),
530
- occurredAtMs: z7.unknown().optional()
552
+ var ObserveShape = z9.looseObject({
553
+ detector: z9.looseObject({ id: z9.string(), version: z9.string() }),
554
+ subject: z9.looseObject({ kind: z9.string(), key: z9.string().optional() }).optional(),
555
+ after: z9.looseObject({ observationId: z9.unknown() }).optional(),
556
+ reuse: z9.looseObject({ maxAgeMs: z9.unknown() }).optional(),
557
+ source: z9.looseObject({
558
+ sessionId: z9.string(),
559
+ entryId: z9.string(),
560
+ openerEntryId: z9.string().optional(),
561
+ runId: z9.string().optional(),
562
+ occurredAtMs: z9.unknown().optional()
531
563
  }),
532
- questions: z7.record(z7.string(), z7.unknown())
564
+ questions: z9.record(z9.string(), z9.unknown())
533
565
  });
534
- var ChoiceQuestion = z7.looseObject({
535
- type: z7.literal("choice"),
536
- criteria: z7.record(z7.string(), z7.unknown())
566
+ var ChoiceQuestion = z9.looseObject({
567
+ type: z9.literal("choice"),
568
+ criteria: z9.record(z9.string(), z9.unknown())
537
569
  });
538
570
  var fits = (value, grammar) => value.length <= MAX_NAME && grammar.test(value);
539
571
  var isWhole = (value, min) => typeof value === "number" && Number.isSafeInteger(value) && value >= min && value <= SIGNAL_TIME_MAX_MS;
@@ -613,10 +645,10 @@ function grammarProblem(input) {
613
645
  }
614
646
  return void 0;
615
647
  }
616
- var EchoText = z7.string().catch("");
617
- var Echo = z7.object({
618
- detector: z7.object({ id: EchoText, version: EchoText }).catch({ id: "", version: "" }),
619
- source: z7.object({ sessionId: EchoText, entryId: EchoText }).catch({ sessionId: "", entryId: "" })
648
+ var EchoText = z9.string().catch("");
649
+ var Echo = z9.object({
650
+ detector: z9.object({ id: EchoText, version: EchoText }).catch({ id: "", version: "" }),
651
+ source: z9.object({ sessionId: EchoText, entryId: EchoText }).catch({ sessionId: "", entryId: "" })
620
652
  }).catch({
621
653
  detector: { id: "", version: "" },
622
654
  source: { sessionId: "", entryId: "" }
@@ -1121,6 +1153,7 @@ var KAY_APP_DATA_CHANGE_MAX_BYTES = 256 * 1024;
1121
1153
  var KAY_APP_DATA_READ_REQUEST_MAX_BYTES = 64 * 1024;
1122
1154
  var KAY_APP_DATA_READ_RESULT_MAX_BYTES = 16 * 1024 * 1024;
1123
1155
  export {
1156
+ CorpusResourceInvocationCursor,
1124
1157
  JsonSchema,
1125
1158
  JsonValue,
1126
1159
  KAY_APP_AUTOMATION_CONDITION_KINDS,
@@ -1140,6 +1173,9 @@ export {
1140
1173
  PLUGIN_SIGNAL_RELATED_KEYS,
1141
1174
  PLUGIN_SIGNAL_SNIPPET_MAX,
1142
1175
  PLUGIN_SIGNAL_TITLE_MAX,
1176
+ ProjectDirectory,
1177
+ ProjectDirectoryRequest,
1178
+ ProjectDirectoryResponse,
1143
1179
  SIGNAL_TIME_MAX_MS,
1144
1180
  STORED_TRANSCRIPT_MESSAGE_FILTER,
1145
1181
  STORED_TRANSCRIPT_MESSAGE_KINDS,
@@ -0,0 +1,106 @@
1
+ /** What changed between the texts the card showed and the texts the person sent. */
2
+ export declare function diffDraft(before: DraftTexts, after: DraftTexts): DraftChange;
3
+
4
+ declare interface DiffSegment {
5
+ readonly kind: DiffSegmentKind;
6
+ readonly text: string;
7
+ }
8
+
9
+ declare type DiffSegmentKind = "same" | "removed" | "added";
10
+
11
+ /** Compare two texts word by word. Callers normalize first; this compares what it is given. */
12
+ export declare function diffWords(before: string, after: string): WordDiff;
13
+
14
+ export declare interface DraftChange {
15
+ /** The normalized subject or body differ. A change in whitespace alone counts. */
16
+ readonly edited: boolean;
17
+ readonly subjectChanged: boolean;
18
+ readonly bodyChanged: boolean;
19
+ /** Words across subject and body; zero for an edit that only moved whitespace. */
20
+ readonly wordsAdded: number;
21
+ readonly wordsRemoved: number;
22
+ /**
23
+ * Words across subject and body of the draft as proposed and of the message as sent, counted the
24
+ * way `wordsRemoved` and `wordsAdded` are. `(beforeWords - wordsRemoved) / beforeWords` is the
25
+ * share of the proposed draft that was kept.
26
+ */
27
+ readonly beforeWords: number;
28
+ readonly afterWords: number;
29
+ readonly beforeChars: number;
30
+ readonly afterChars: number;
31
+ /**
32
+ * Either part's changed span was too long to align, so it counts as one removed block and one
33
+ * added block: `wordsRemoved` is then an upper bound and the share kept a lower bound.
34
+ */
35
+ readonly coarse: boolean;
36
+ readonly subjectDiff: WordDiff | undefined;
37
+ readonly bodyDiff: WordDiff | undefined;
38
+ }
39
+
40
+ export declare interface DraftTexts {
41
+ readonly subject: string | undefined;
42
+ readonly body: string;
43
+ }
44
+
45
+ /** Both ends of a text too long to show whole, and how much lay between them. */
46
+ export declare interface HeadAndTail {
47
+ readonly head: string;
48
+ readonly tail: string;
49
+ /** UTF-16 units left out between `head` and `tail`; `0` when the text fit and `head` is all of it. */
50
+ readonly omitted: number;
51
+ }
52
+
53
+ /**
54
+ * The first `headMax` and the last `tailMax` UTF-16 units of `text`, each cut on a character
55
+ * boundary. Text that fits in `headMax + tailMax` comes back whole as `head`, with nothing omitted.
56
+ *
57
+ * For text whose ASK sits at the end (a reply that closes on a question) as well as its framing at
58
+ * the start: a head-only cut keeps the framing and loses the ask.
59
+ */
60
+ export declare function headAndTailChars(text: string, headMax: number, tailMax: number): HeadAndTail;
61
+
62
+ /** At most `max` UTF-16 units from the start of `text`, never ending inside a surrogate pair. */
63
+ export declare function headChars(text: string, max: number): string;
64
+
65
+ /** The most cells the alignment table may hold (about 2,000 tokens a side, 16 MB of Uint32). */
66
+ export declare const MAX_LCS_CELLS = 4000000;
67
+
68
+ /**
69
+ * Line endings, trailing whitespace on each line and trailing blank lines are not edits: a text
70
+ * area and a saved file disagree on them without the person touching a word.
71
+ */
72
+ export declare function normalizeDraftBody(text: string): string;
73
+
74
+ /** A subject is one line: any whitespace run is one space and the ends are trimmed. */
75
+ export declare function normalizeDraftSubject(subject: string | undefined): string | undefined;
76
+
77
+ /**
78
+ * The diff as one marked text: `[-removed-]` and `{+added+}` inside the unchanged words around
79
+ * them, with a long unchanged stretch collapsed to its edges and an ellipsis. The unmarked parts
80
+ * are the original wording, so the result reads as the final text with the edits shown.
81
+ */
82
+ export declare function renderWordDiff(diff: WordDiff, options?: {
83
+ readonly contextWords?: number;
84
+ readonly maxChars?: number;
85
+ }): {
86
+ readonly text: string;
87
+ readonly truncated: boolean;
88
+ };
89
+
90
+ /** The end index of a head that does not end inside a surrogate pair. */
91
+ export declare function safeHeadEnd(text: string, end: number): number;
92
+
93
+ /** The start index of a tail that does not begin inside a surrogate pair. */
94
+ export declare function safeTailStart(text: string, start: number): number;
95
+
96
+ export declare interface WordDiff {
97
+ /** Non-whitespace words present before and gone after. */
98
+ readonly wordsRemoved: number;
99
+ /** Non-whitespace words present after and not before. */
100
+ readonly wordsAdded: number;
101
+ readonly segments: readonly DiffSegment[];
102
+ /** The middle was too large to align, so it is one removed block and one added block. */
103
+ readonly coarse: boolean;
104
+ }
105
+
106
+ export { }
@@ -0,0 +1,223 @@
1
+ // packages/plugin-sdk/dist/text-clip.js
2
+ function safeTailStart(text, start) {
3
+ const code = text.charCodeAt(start);
4
+ return code >= 56320 && code <= 57343 ? start + 1 : start;
5
+ }
6
+ function safeHeadEnd(text, end) {
7
+ const code = text.charCodeAt(end - 1);
8
+ return end > 0 && code >= 55296 && code <= 56319 ? end - 1 : end;
9
+ }
10
+ function headChars(text, max) {
11
+ return text.length <= max ? text : text.slice(0, safeHeadEnd(text, max));
12
+ }
13
+ function headAndTailChars(text, headMax, tailMax) {
14
+ if (text.length <= headMax + tailMax) {
15
+ return { head: text, tail: "", omitted: 0 };
16
+ }
17
+ const headEnd = safeHeadEnd(text, headMax);
18
+ const tailStart = safeTailStart(text, text.length - tailMax);
19
+ return {
20
+ head: text.slice(0, headEnd),
21
+ tail: text.slice(tailStart),
22
+ omitted: tailStart - headEnd
23
+ };
24
+ }
25
+
26
+ // packages/plugin-sdk/dist/text-diff.js
27
+ var MAX_LCS_CELLS = 4e6;
28
+ var isSpaceOrTab = (code) => code === 32 || code === 9;
29
+ var isNewline = (code) => code === 10;
30
+ function trimEndWhile(text, drop) {
31
+ let end = text.length;
32
+ while (end > 0 && drop(text.charCodeAt(end - 1)))
33
+ end -= 1;
34
+ return text.slice(0, end);
35
+ }
36
+ function normalizeDraftBody(text) {
37
+ const lines = text.replace(/\r\n?/g, "\n").split("\n").map((line) => trimEndWhile(line, isSpaceOrTab));
38
+ return trimEndWhile(lines.join("\n"), isNewline);
39
+ }
40
+ function normalizeDraftSubject(subject) {
41
+ return subject === void 0 ? void 0 : subject.replace(/\s+/g, " ").trim();
42
+ }
43
+ function tokensOf(text) {
44
+ return text.match(/\s+|\S+/g) ?? [];
45
+ }
46
+ function wordCount(text) {
47
+ return text.split(/\s+/).filter((word) => word !== "").length;
48
+ }
49
+ function alignTokens(a, b) {
50
+ const rows = a.length;
51
+ const cols = b.length;
52
+ if (rows === 0 || cols === 0)
53
+ return [];
54
+ if ((rows + 1) * (cols + 1) > MAX_LCS_CELLS)
55
+ return void 0;
56
+ const width = cols + 1;
57
+ const table = new Uint32Array((rows + 1) * width);
58
+ for (let row2 = rows - 1; row2 >= 0; row2 -= 1) {
59
+ for (let col2 = cols - 1; col2 >= 0; col2 -= 1) {
60
+ table[row2 * width + col2] = a[row2] === b[col2] ? table[(row2 + 1) * width + col2 + 1] + 1 : Math.max(table[(row2 + 1) * width + col2], table[row2 * width + col2 + 1]);
61
+ }
62
+ }
63
+ const pairs = [];
64
+ let row = 0;
65
+ let col = 0;
66
+ while (row < rows && col < cols) {
67
+ if (a[row] === b[col]) {
68
+ pairs.push([row, col]);
69
+ row += 1;
70
+ col += 1;
71
+ } else if (table[(row + 1) * width + col] >= table[row * width + col + 1]) {
72
+ row += 1;
73
+ } else {
74
+ col += 1;
75
+ }
76
+ }
77
+ return pairs;
78
+ }
79
+ function pushSegment(segments, kind, text) {
80
+ if (text === "")
81
+ return;
82
+ const last = segments.at(-1);
83
+ if (last?.kind === kind) {
84
+ segments[segments.length - 1] = { kind, text: last.text + text };
85
+ } else {
86
+ segments.push({ kind, text });
87
+ }
88
+ }
89
+ function diffWords(before, after) {
90
+ const a = tokensOf(before);
91
+ const b = tokensOf(after);
92
+ let start = 0;
93
+ while (start < a.length && start < b.length && a[start] === b[start]) {
94
+ start += 1;
95
+ }
96
+ let endA = a.length;
97
+ let endB = b.length;
98
+ while (endA > start && endB > start && a[endA - 1] === b[endB - 1]) {
99
+ endA -= 1;
100
+ endB -= 1;
101
+ }
102
+ const middleA = a.slice(start, endA);
103
+ const middleB = b.slice(start, endB);
104
+ const pairs = alignTokens(middleA, middleB);
105
+ const segments = [];
106
+ pushSegment(segments, "same", a.slice(0, start).join(""));
107
+ if (pairs === void 0) {
108
+ pushSegment(segments, "removed", middleA.join(""));
109
+ pushSegment(segments, "added", middleB.join(""));
110
+ } else {
111
+ let row = 0;
112
+ let col = 0;
113
+ for (const [matchedRow, matchedCol] of pairs) {
114
+ pushSegment(segments, "removed", middleA.slice(row, matchedRow).join(""));
115
+ pushSegment(segments, "added", middleB.slice(col, matchedCol).join(""));
116
+ pushSegment(segments, "same", middleA[matchedRow]);
117
+ row = matchedRow + 1;
118
+ col = matchedCol + 1;
119
+ }
120
+ pushSegment(segments, "removed", middleA.slice(row).join(""));
121
+ pushSegment(segments, "added", middleB.slice(col).join(""));
122
+ }
123
+ pushSegment(segments, "same", a.slice(endA).join(""));
124
+ let wordsRemoved = 0;
125
+ let wordsAdded = 0;
126
+ for (const segment of segments) {
127
+ if (segment.kind === "removed")
128
+ wordsRemoved += wordCount(segment.text);
129
+ if (segment.kind === "added")
130
+ wordsAdded += wordCount(segment.text);
131
+ }
132
+ return { wordsRemoved, wordsAdded, segments, coarse: pairs === void 0 };
133
+ }
134
+ function diffDraft(before, after) {
135
+ const beforeBody = normalizeDraftBody(before.body);
136
+ const afterBody = normalizeDraftBody(after.body);
137
+ const beforeSubject = normalizeDraftSubject(before.subject);
138
+ const afterSubject = normalizeDraftSubject(after.subject);
139
+ const subjectChanged = (beforeSubject ?? "") !== (afterSubject ?? "");
140
+ const bodyChanged = beforeBody !== afterBody;
141
+ const subjectDiff = subjectChanged ? diffWords(beforeSubject ?? "", afterSubject ?? "") : void 0;
142
+ const bodyDiff = bodyChanged ? diffWords(beforeBody, afterBody) : void 0;
143
+ return {
144
+ edited: subjectChanged || bodyChanged,
145
+ subjectChanged,
146
+ bodyChanged,
147
+ wordsAdded: (subjectDiff?.wordsAdded ?? 0) + (bodyDiff?.wordsAdded ?? 0),
148
+ wordsRemoved: (subjectDiff?.wordsRemoved ?? 0) + (bodyDiff?.wordsRemoved ?? 0),
149
+ beforeWords: wordCount(beforeSubject ?? "") + wordCount(beforeBody),
150
+ afterWords: wordCount(afterSubject ?? "") + wordCount(afterBody),
151
+ beforeChars: (beforeSubject ?? "").length + beforeBody.length,
152
+ afterChars: (afterSubject ?? "").length + afterBody.length,
153
+ coarse: subjectDiff?.coarse === true || bodyDiff?.coarse === true,
154
+ subjectDiff,
155
+ bodyDiff
156
+ };
157
+ }
158
+ function visible(text) {
159
+ if (text.trim() === "") {
160
+ return text.replace(/\n/g, "\u23CE").replace(/\t/g, "\u21E5").replace(/ /g, "\u2423");
161
+ }
162
+ return text.replace(/\n/g, "\u23CE\n");
163
+ }
164
+ function wordSpans(text) {
165
+ return [...text.matchAll(/\S+/g)].map((match) => ({
166
+ start: match.index ?? 0,
167
+ end: (match.index ?? 0) + match[0].length
168
+ }));
169
+ }
170
+ function contextOf(text, words, where) {
171
+ const spans = wordSpans(text);
172
+ if (spans.length <= words)
173
+ return { text, cut: false };
174
+ return where === "head" ? { text: text.slice(0, spans[words - 1].end), cut: true } : { text: text.slice(spans[spans.length - words].start), cut: true };
175
+ }
176
+ function renderWordDiff(diff, options = {}) {
177
+ const contextWords = options.contextWords ?? 8;
178
+ const maxChars = options.maxChars ?? 3e3;
179
+ if (diff.segments.every((segment) => segment.kind === "same")) {
180
+ return {
181
+ text: "(no word changed; only whitespace differs)",
182
+ truncated: false
183
+ };
184
+ }
185
+ const lastIndex = diff.segments.length - 1;
186
+ const parts = diff.segments.map((segment, index) => {
187
+ if (segment.kind === "removed")
188
+ return `[-${visible(segment.text)}-]`;
189
+ if (segment.kind === "added")
190
+ return `{+${visible(segment.text)}+}`;
191
+ const words = wordSpans(segment.text).length;
192
+ if (index === 0) {
193
+ const tail2 = contextOf(segment.text, contextWords, "tail");
194
+ return tail2.cut ? `\u2026 ${tail2.text}` : tail2.text;
195
+ }
196
+ if (index === lastIndex) {
197
+ const head2 = contextOf(segment.text, contextWords, "head");
198
+ return head2.cut ? `${head2.text} \u2026` : head2.text;
199
+ }
200
+ if (words <= contextWords * 2)
201
+ return segment.text;
202
+ const head = contextOf(segment.text, contextWords, "head").text;
203
+ const tail = contextOf(segment.text, contextWords, "tail").text;
204
+ return `${head} \u2026 ${tail}`;
205
+ });
206
+ const rendered = parts.join("");
207
+ return rendered.length <= maxChars ? { text: rendered, truncated: false } : {
208
+ text: `${headChars(rendered, maxChars)}\u2026`,
209
+ truncated: true
210
+ };
211
+ }
212
+ export {
213
+ MAX_LCS_CELLS,
214
+ diffDraft,
215
+ diffWords,
216
+ headAndTailChars,
217
+ headChars,
218
+ normalizeDraftBody,
219
+ normalizeDraftSubject,
220
+ renderWordDiff,
221
+ safeHeadEnd,
222
+ safeTailStart
223
+ };
@@ -45,6 +45,17 @@ export declare function matchesWorkBlock(coverage: WorkBlockCoverage, toolResult
45
45
  */
46
46
  export declare const NON_ACTIVITY_TOOL_NAMES: readonly string[];
47
47
 
48
+ /**
49
+ * The person's messages that precede a packet's opener on the same branch: the last `count`
50
+ * human-verified `role: "user"` messages before it, oldest first. Openers and steers of earlier
51
+ * turns both count; a message the transcript cannot attribute to a person does not.
52
+ *
53
+ * This is the rolling context a detector reads when it must interpret the opener in light of
54
+ * what the person said before (a rule amended in one message that was set in an earlier one).
55
+ * Pure, like {@link turnPacket}: the caller passes the same entries it built the packet from.
56
+ */
57
+ export declare function precedingPersonMessages(entries: readonly TurnPacketEntry[], packet: TurnPacket, count: number): readonly TurnPacketMessage[];
58
+
48
59
  /**
49
60
  * What a run did to a URI. The words match the automation run wire's artifact actions. When one
50
61
  * run both creates and updates a URI, the reader reports `created`.
@@ -87,6 +98,36 @@ export declare const RUN_STARTED_CUSTOM_TYPE = "session_run_started";
87
98
  */
88
99
  export declare const RUN_TERMINAL_CUSTOM_TYPE = "session_run_state";
89
100
 
101
+ export declare type SkillSuggestionEvidenceItem = SkillSuggestionEvidenceRef | {
102
+ readonly kind: "text";
103
+ readonly text: string;
104
+ };
105
+
106
+ export declare function skillSuggestionEvidenceItems(evidence: unknown, options?: SkillSuggestionEvidenceOptions): readonly SkillSuggestionEvidenceItem[];
107
+
108
+ export declare interface SkillSuggestionEvidenceOptions {
109
+ readonly sessionIds?: readonly string[];
110
+ }
111
+
112
+ export declare type SkillSuggestionEvidenceRef = {
113
+ readonly kind: "message";
114
+ readonly sessionId: string;
115
+ readonly entryId: string;
116
+ readonly raw: string;
117
+ } | {
118
+ readonly kind: "session";
119
+ readonly sessionId: string;
120
+ readonly raw: string;
121
+ } | {
122
+ readonly kind: "friction";
123
+ readonly reference: string;
124
+ readonly raw: string;
125
+ } | {
126
+ readonly kind: "uri";
127
+ readonly uri: string;
128
+ readonly raw: string;
129
+ };
130
+
90
131
  /** The fields the customType selector needs off a session entry. */
91
132
  export declare interface TranscriptEntryLike {
92
133
  readonly id: string;
@@ -128,6 +169,83 @@ export declare interface TurnBoundaryEntry {
128
169
  readonly message?: unknown;
129
170
  }
130
171
 
172
+ /**
173
+ * What the transcript can say about who wrote a message.
174
+ *
175
+ * Two values, not three: an earlier draft also declared `session-root` ("the session is a
176
+ * person-started root"), but nothing ever derived it, so the union advertised an answer this
177
+ * helper cannot give. A consumer needing session-level provenance reads the session's own
178
+ * stamps instead of a message's.
179
+ */
180
+ export declare type TurnMessageOrigin =
181
+ /** The message carries both `clientIntentId` and `details.deliveryId`: a person typed it. */
182
+ "human-verified"
183
+ /** Neither the session nor the message establishes human authorship. */
184
+ | "unknown";
185
+
186
+ /**
187
+ * Who wrote one message, read from what the transcript carries: `clientIntentId` AND
188
+ * `details.deliveryId` mean a person typed it. The one definition the turn packet and every other
189
+ * reader of a message's authorship share.
190
+ */
191
+ export declare function turnMessageOrigin(entry: Pick<TurnPacketEntry, "message">): TurnMessageOrigin;
192
+
193
+ export declare interface TurnPacket {
194
+ /** The message that opened the turn. */
195
+ readonly personMessage: TurnPacketMessage;
196
+ /** Human steering messages that landed inside the run, oldest first. */
197
+ readonly personSteers: readonly TurnPacketMessage[];
198
+ /** The assistant's prose replies in order. Tool calls and results are excluded. */
199
+ readonly assistantReplies: readonly TurnPacketMessage[];
200
+ /** The settle anchor the caller supplied, so a consumer can re-derive the same packet. */
201
+ readonly settleEntryId: string;
202
+ /** The session's provenance tier, as the caller established it from the log's own stamps. */
203
+ readonly sessionTier: "root" | "spawned" | "unknown";
204
+ }
205
+
206
+ /**
207
+ * Shape the turn that settled at `settleEntryId` out of the raw entries on its branch.
208
+ *
209
+ * The walk is branch-aware: entries the active-branch walk excludes (rewound or abandoned
210
+ * forks) are never included. The settle anchor is folded onto the run it follows when it is a
211
+ * trailing bookkeeping entry, which is what a settle event points at. If the anchor names an
212
+ * entry the branch walk cannot reach, the packet is built from the branch's last message
213
+ * instead, and the anchor is preserved on the result for a consumer to notice the mismatch.
214
+ */
215
+ export declare function turnPacket(entries: readonly TurnPacketEntry[], settleEntryId: string, options?: {
216
+ readonly sessionTier?: "root" | "spawned" | "unknown";
217
+ }): TurnPacket | undefined;
218
+
219
+ /** The fields the packet reads off one raw entry. Deliberately narrower than any repo type. */
220
+ export declare interface TurnPacketEntry {
221
+ readonly id: string;
222
+ readonly parentId?: string | null;
223
+ readonly type: string;
224
+ readonly customType?: string;
225
+ /** Epoch milliseconds (pi's `EntryBase.timestamp`). */
226
+ readonly timestamp: number;
227
+ /**
228
+ * Typed as loosely as pi's own message union is wide: `content` is a string or a parts
229
+ * array depending on the role, and `details` is opaque JSON that the daemon stamps a
230
+ * `deliveryId` into. The packet reads both through guards rather than asking every caller
231
+ * to narrow first.
232
+ */
233
+ readonly message?: {
234
+ readonly role?: string;
235
+ readonly content?: unknown;
236
+ readonly clientIntentId?: unknown;
237
+ readonly details?: unknown;
238
+ };
239
+ }
240
+
241
+ export declare interface TurnPacketMessage {
242
+ readonly entryId: string;
243
+ /** Epoch milliseconds, as the transcript entry carries it. */
244
+ readonly timestamp: number;
245
+ readonly text: string;
246
+ readonly origin: TurnMessageOrigin;
247
+ }
248
+
131
249
  /** One turn's bounds as {@link turnSegments} reports them, both inclusive. */
132
250
  export declare interface TurnSegment {
133
251
  /** The user message entry that opened the turn. A steer never opens one. */