@yaklabs-ai/plugin-sdk 0.53.0 → 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/README.md +24 -22
- package/THIRD-PARTY-INVENTORY.json +1 -1
- package/dist/disclosure.d.ts +5 -1
- package/dist/disclosure.js +25 -0
- package/dist/endpoints.d.ts +29 -0
- package/dist/endpoints.js +28 -0
- package/dist/fs.d.ts +26 -0
- package/dist/fs.js +49 -2
- package/dist/kay-agent-testing.d.ts +25 -1
- package/dist/kay-agent.d.ts +44 -1
- package/dist/kay-agent.js +45 -17
- package/dist/kay-app-session-summary.d.ts +21 -0
- package/dist/kay-app.d.ts +234 -1
- package/dist/kay-app.js +88 -52
- package/dist/text-diff.d.ts +106 -0
- package/dist/text-diff.js +223 -0
- package/dist/transcript.d.ts +118 -0
- package/dist/transcript.js +266 -0
- package/package.json +5 -1
|
@@ -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
|
+
};
|
package/dist/transcript.d.ts
CHANGED
|
@@ -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. */
|
package/dist/transcript.js
CHANGED
|
@@ -133,6 +133,268 @@ function readProducedResourceDeclarations(details) {
|
|
|
133
133
|
return MALFORMED;
|
|
134
134
|
return { kind: "declared", resources: claimed };
|
|
135
135
|
}
|
|
136
|
+
|
|
137
|
+
// packages/plugin-protocol/dist/turn-packet.js
|
|
138
|
+
var RUN_STARTED = "session_run_started";
|
|
139
|
+
function textOf(entry) {
|
|
140
|
+
const content = entry.message?.content;
|
|
141
|
+
if (typeof content === "string")
|
|
142
|
+
return content;
|
|
143
|
+
if (!Array.isArray(content))
|
|
144
|
+
return "";
|
|
145
|
+
return content.map((part) => typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string" ? part.text : "").join("");
|
|
146
|
+
}
|
|
147
|
+
function turnMessageOrigin(entry) {
|
|
148
|
+
const m = entry.message;
|
|
149
|
+
if (m === void 0 || typeof m.clientIntentId !== "string")
|
|
150
|
+
return "unknown";
|
|
151
|
+
const details = m.details;
|
|
152
|
+
const deliveryId = typeof details === "object" && details !== null ? details.deliveryId : void 0;
|
|
153
|
+
return typeof deliveryId === "string" ? "human-verified" : "unknown";
|
|
154
|
+
}
|
|
155
|
+
function turnPacket(entries, settleEntryId, options = {}) {
|
|
156
|
+
const branch = branchEntryIds(entries);
|
|
157
|
+
const live = entries.filter((e) => branch.has(e.id));
|
|
158
|
+
const anchorIndex = live.findIndex((e) => e.id === settleEntryId);
|
|
159
|
+
const searchFrom = anchorIndex === -1 ? live.length - 1 : anchorIndex;
|
|
160
|
+
let runStart = -1;
|
|
161
|
+
for (let i = searchFrom; i >= 0; i -= 1) {
|
|
162
|
+
if (live[i]?.type === "custom" && live[i]?.customType === RUN_STARTED) {
|
|
163
|
+
runStart = i;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
let spanStart = runStart;
|
|
168
|
+
if (runStart === -1) {
|
|
169
|
+
spanStart = 0;
|
|
170
|
+
for (let i = searchFrom; i >= 0; i -= 1) {
|
|
171
|
+
if (live[i]?.type === "message" && live[i]?.message?.role === "user") {
|
|
172
|
+
spanStart = i;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const span = live.slice(spanStart, searchFrom + 1);
|
|
178
|
+
const messages = span.filter((e) => e.type === "message" && e.message?.role !== void 0);
|
|
179
|
+
const people = messages.filter((e) => e.message?.role === "user");
|
|
180
|
+
const assistants = messages.filter((e) => e.message?.role === "assistant");
|
|
181
|
+
const opener = people[0];
|
|
182
|
+
if (opener === void 0)
|
|
183
|
+
return void 0;
|
|
184
|
+
const toPacket = (entry) => ({
|
|
185
|
+
entryId: entry.id,
|
|
186
|
+
timestamp: entry.timestamp,
|
|
187
|
+
text: textOf(entry),
|
|
188
|
+
origin: turnMessageOrigin(entry)
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
personMessage: toPacket(opener),
|
|
192
|
+
personSteers: people.slice(1).map(toPacket),
|
|
193
|
+
assistantReplies: assistants.filter((e) => textOf(e).trim().length > 0).map(toPacket),
|
|
194
|
+
settleEntryId,
|
|
195
|
+
sessionTier: options.sessionTier ?? "unknown"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function precedingPersonMessages(entries, packet, count) {
|
|
199
|
+
if (count <= 0)
|
|
200
|
+
return [];
|
|
201
|
+
const branch = branchEntryIds(entries);
|
|
202
|
+
const live = entries.filter((e) => branch.has(e.id));
|
|
203
|
+
const openerIndex = live.findIndex((e) => e.id === packet.personMessage.entryId);
|
|
204
|
+
if (openerIndex === -1)
|
|
205
|
+
return [];
|
|
206
|
+
const found = [];
|
|
207
|
+
for (let i = openerIndex - 1; i >= 0 && found.length < count; i -= 1) {
|
|
208
|
+
const e = live[i];
|
|
209
|
+
if (e === void 0)
|
|
210
|
+
continue;
|
|
211
|
+
if (e.type !== "message" || e.message?.role !== "user")
|
|
212
|
+
continue;
|
|
213
|
+
if (turnMessageOrigin(e) !== "human-verified")
|
|
214
|
+
continue;
|
|
215
|
+
found.push({
|
|
216
|
+
entryId: e.id,
|
|
217
|
+
timestamp: e.timestamp,
|
|
218
|
+
text: textOf(e),
|
|
219
|
+
origin: "human-verified"
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return found.reverse();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// packages/plugin-protocol/dist/skill-suggestion-evidence.js
|
|
226
|
+
var UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
227
|
+
var UUID_ID = new RegExp(`^${UUID}$`, "i");
|
|
228
|
+
var ENTRY = "[a-z0-9_-]+";
|
|
229
|
+
var ENTRY_ID = new RegExp(`^${ENTRY}$`, "i");
|
|
230
|
+
var FRICTION_URI = /^friction:\/\/([a-z0-9]+(?:-[a-z0-9]+)*)$/;
|
|
231
|
+
var normalizeId = (value) => UUID_ID.test(value) ? value.toLowerCase() : value;
|
|
232
|
+
function sessionPattern(options) {
|
|
233
|
+
const known = (options.sessionIds ?? []).filter((id) => ENTRY_ID.test(id) && !UUID_ID.test(id)).map((id) => id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
234
|
+
return `(?:${[UUID, ...new Set(known)].join("|")})`;
|
|
235
|
+
}
|
|
236
|
+
function sessionRef(sessionId, entryId, raw) {
|
|
237
|
+
return entryId === void 0 ? { kind: "session", sessionId: normalizeId(sessionId), raw } : {
|
|
238
|
+
kind: "message",
|
|
239
|
+
sessionId: normalizeId(sessionId),
|
|
240
|
+
entryId: normalizeId(entryId),
|
|
241
|
+
raw
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function parseToken(raw, sessions) {
|
|
245
|
+
if (/^sessions?:\/\//i.test(raw)) {
|
|
246
|
+
const match = /^sessions?:\/\/([^/?#]+)([^?#]*)(?:\?([^#]*))?(?:#(.*))?$/i.exec(raw);
|
|
247
|
+
if (match === null)
|
|
248
|
+
return void 0;
|
|
249
|
+
const sessionId = match[1].replace(/\.jsonl$/i, "");
|
|
250
|
+
if (!new RegExp(`^${sessions}$`, "i").test(sessionId))
|
|
251
|
+
return void 0;
|
|
252
|
+
const path = match[2];
|
|
253
|
+
if (path !== "" && path !== "/" && !new RegExp(`^/${ENTRY}$`, "i").test(path))
|
|
254
|
+
return void 0;
|
|
255
|
+
const query = new URLSearchParams(match[3]);
|
|
256
|
+
const entryId = query.get("entry") ?? query.get("span") ?? query.get("from") ?? (path.length > 1 ? path.slice(1) : match[4]);
|
|
257
|
+
if (entryId !== void 0 && !ENTRY_ID.test(entryId))
|
|
258
|
+
return void 0;
|
|
259
|
+
return sessionRef(sessionId, entryId, raw);
|
|
260
|
+
}
|
|
261
|
+
if (/^friction:\/\//i.test(raw)) {
|
|
262
|
+
const reference = FRICTION_URI.exec(raw)?.[1];
|
|
263
|
+
return reference === void 0 || reference === "summary" ? void 0 : { kind: "friction", reference, raw };
|
|
264
|
+
}
|
|
265
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) {
|
|
266
|
+
return { kind: "uri", uri: raw, raw };
|
|
267
|
+
}
|
|
268
|
+
const message = new RegExp(`^(${sessions})(?:\\.jsonl)?(?:[/#](${ENTRY})(?:#[0-9]+)?)?$`, "i").exec(raw);
|
|
269
|
+
return message === null ? void 0 : sessionRef(message[1], message[2], raw);
|
|
270
|
+
}
|
|
271
|
+
function findSkillSuggestionCitations(text, options = {}) {
|
|
272
|
+
const sessions = sessionPattern(options);
|
|
273
|
+
const tokens = new RegExp(`(?<![\\w/.:#-])(?:[a-z][a-z0-9+.-]*://[^\\s<>\`"'|\\[\\]()]+|${sessions}(?:\\.jsonl)?(?:[/#]${ENTRY}(?:#[0-9]+)?)?(?![\\w/#-]))`, "gi");
|
|
274
|
+
const citations = [];
|
|
275
|
+
for (const match of text.matchAll(tokens)) {
|
|
276
|
+
const raw = match[0].replace(/[.,;:!?]+$/u, "");
|
|
277
|
+
if (new RegExp(`^${UUID}(?:#[0-9]+)?$`, "i").test(raw) && /\b(?:call|result)(?:\s*:\s*|\s+)$/i.test(text.slice(0, match.index)))
|
|
278
|
+
continue;
|
|
279
|
+
const ref = parseToken(raw, sessions);
|
|
280
|
+
if (ref !== void 0)
|
|
281
|
+
citations.push({
|
|
282
|
+
start: match.index,
|
|
283
|
+
end: match.index + raw.length,
|
|
284
|
+
ref
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
const sessionIds = new Set(citations.flatMap(({ ref }) => ref.kind === "message" || ref.kind === "session" && ref.raw.indexOf("://") >= 0 ? [ref.sessionId] : []));
|
|
288
|
+
if (sessionIds.size === 1) {
|
|
289
|
+
const sessionId = [...sessionIds][0];
|
|
290
|
+
const relative = new RegExp(`\\b(?:call|result)(?:\\s*:\\s*|\\s+)((?:${UUID}|[0-9a-f]{8,32}|[0-9]+)|${ENTRY}(?=#[0-9]+))(?:#[0-9]+)?(?![\\w/#-])`, "gi");
|
|
291
|
+
for (const match of text.matchAll(relative)) {
|
|
292
|
+
const start = match.index;
|
|
293
|
+
const end = start + match[0].length;
|
|
294
|
+
if (citations.some((item) => start < item.end && end > item.start))
|
|
295
|
+
continue;
|
|
296
|
+
citations.push({
|
|
297
|
+
start,
|
|
298
|
+
end,
|
|
299
|
+
ref: sessionRef(sessionId, match[1], match[0])
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return citations.sort((a, b) => a.start - b.start);
|
|
304
|
+
}
|
|
305
|
+
function skillSuggestionEvidenceKey(item) {
|
|
306
|
+
switch (item.kind) {
|
|
307
|
+
case "message":
|
|
308
|
+
return `message:${item.sessionId}/${item.entryId}`;
|
|
309
|
+
case "session":
|
|
310
|
+
return `session:${item.sessionId}`;
|
|
311
|
+
case "friction":
|
|
312
|
+
return `friction:${item.reference}`;
|
|
313
|
+
case "uri":
|
|
314
|
+
return `uri:${item.uri}`;
|
|
315
|
+
case "text":
|
|
316
|
+
return `text:${item.text.trim()}`;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function skillSuggestionEvidenceItems(evidence, options = {}) {
|
|
320
|
+
const items = [];
|
|
321
|
+
const keys = /* @__PURE__ */ new Set();
|
|
322
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
323
|
+
const add = (item) => {
|
|
324
|
+
const key = skillSuggestionEvidenceKey(item);
|
|
325
|
+
if (keys.has(key))
|
|
326
|
+
return;
|
|
327
|
+
keys.add(key);
|
|
328
|
+
items.push(item);
|
|
329
|
+
};
|
|
330
|
+
const text = (value) => {
|
|
331
|
+
const citations = findSkillSuggestionCitations(value, options);
|
|
332
|
+
if (citations.length === 0) {
|
|
333
|
+
if (value.trim() !== "")
|
|
334
|
+
add({ kind: "text", text: value });
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
let cursor = 0;
|
|
338
|
+
const rest = [];
|
|
339
|
+
for (const citation of citations) {
|
|
340
|
+
rest.push(value.slice(cursor, citation.start));
|
|
341
|
+
cursor = citation.end;
|
|
342
|
+
}
|
|
343
|
+
rest.push(value.slice(cursor));
|
|
344
|
+
const note = rest.join(" ").trim();
|
|
345
|
+
if (citations.every(({ ref }) => ref.kind === "uri") && /[\p{L}\p{N}]/u.test(note)) {
|
|
346
|
+
add({ kind: "text", text: value });
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
for (const citation of citations)
|
|
350
|
+
add(citation.ref);
|
|
351
|
+
if (/[\p{L}\p{N}]/u.test(note.replace(/\b(?:call|result|view|pointer|and)\b/gi, "")))
|
|
352
|
+
add({ kind: "text", text: note.replace(/^[\s|,;`]+|[\s|,;`]+$/g, "") });
|
|
353
|
+
};
|
|
354
|
+
const visit = (value, depth) => {
|
|
355
|
+
if (value === null || value === void 0)
|
|
356
|
+
return;
|
|
357
|
+
if (typeof value === "string")
|
|
358
|
+
return text(value);
|
|
359
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint")
|
|
360
|
+
return text(String(value));
|
|
361
|
+
if (typeof value !== "object" || visited.has(value))
|
|
362
|
+
return;
|
|
363
|
+
visited.add(value);
|
|
364
|
+
if (depth >= 32) {
|
|
365
|
+
add({ kind: "text", text: "[evidence nested too deeply]" });
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (Array.isArray(value)) {
|
|
369
|
+
for (const entry of value)
|
|
370
|
+
visit(entry, depth + 1);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const object = value;
|
|
374
|
+
const sessionId = object.sessionId;
|
|
375
|
+
const entryId = object.entryId ?? object.messageId;
|
|
376
|
+
if (typeof sessionId === "string" && ENTRY_ID.test(sessionId) && (entryId === void 0 || typeof entryId === "string" && ENTRY_ID.test(entryId))) {
|
|
377
|
+
add(sessionRef(sessionId, entryId, `sessions://${sessionId}${entryId === void 0 ? "" : `/${entryId}`}`));
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (object.kind === "text" && typeof object.text === "string") {
|
|
381
|
+
text(object.text);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (object.kind === "friction" && typeof object.reference === "string") {
|
|
385
|
+
text(`friction://${object.reference}`);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (object.kind === "uri" && typeof object.uri === "string") {
|
|
389
|
+
text(object.uri);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
for (const entry of Object.values(object))
|
|
393
|
+
visit(entry, depth + 1);
|
|
394
|
+
};
|
|
395
|
+
visit(evidence, 0);
|
|
396
|
+
return items;
|
|
397
|
+
}
|
|
136
398
|
export {
|
|
137
399
|
ATTACHMENTS_CUSTOM_TYPE,
|
|
138
400
|
NON_ACTIVITY_TOOL_NAMES,
|
|
@@ -145,7 +407,11 @@ export {
|
|
|
145
407
|
branchEntryIds,
|
|
146
408
|
customEntriesFromEntries,
|
|
147
409
|
matchesWorkBlock,
|
|
410
|
+
precedingPersonMessages,
|
|
148
411
|
readProducedResourceDeclarations,
|
|
412
|
+
skillSuggestionEvidenceItems,
|
|
413
|
+
turnMessageOrigin,
|
|
414
|
+
turnPacket,
|
|
149
415
|
turnSegments,
|
|
150
416
|
workBlockCoverage,
|
|
151
417
|
workBlockKey
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaklabs-ai/plugin-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0",
|
|
4
4
|
"description": "The Kay plugin SDK — types and helpers for authoring a Kay directory plugin.",
|
|
5
5
|
"author": "YakLabs",
|
|
6
6
|
"type": "module",
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
"types": "./dist/fs.d.ts",
|
|
34
34
|
"default": "./dist/fs.js"
|
|
35
35
|
},
|
|
36
|
+
"./text-diff": {
|
|
37
|
+
"types": "./dist/text-diff.d.ts",
|
|
38
|
+
"default": "./dist/text-diff.js"
|
|
39
|
+
},
|
|
36
40
|
"./kay-agent/testing": {
|
|
37
41
|
"types": "./dist/kay-agent-testing.d.ts",
|
|
38
42
|
"default": "./dist/kay-agent-testing.js"
|