veryfront 0.1.720 → 0.1.722

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.
@@ -1,763 +0,0 @@
1
- import { historicalToolSummaries } from "../../integrations/_tool_summaries.js";
2
- const MAX_FALLBACK_ITEMS = 5;
3
- const MAX_FALLBACK_STRING_LENGTH = 300;
4
- const MAX_OBJECT_ARRAY_ITEMS = 5;
5
- const MAX_EVIDENCE_TEXT_LENGTH = 20_000;
6
- const MAX_EVIDENCE_STRINGS = 12;
7
- const MAX_EVIDENCE_RECORDS = 25;
8
- const MAX_EVIDENCE_FIELDS_PER_RECORD = 12;
9
- const MAX_STRUCTURED_ASSERTION_LINES = 80;
10
- const RECORD_ID_PATTERN = /\b[A-Z][A-Z0-9]+-\d{4}-\d{3,}\b/g;
11
- const NEXT_RECORD_ID_PATTERN = /\b[A-Z][A-Z0-9]+-\d{4}-\d{3,}\b/;
12
- const GUARDED_FACT_ALIASES = {
13
- supplier: ["supplier", "vendor"],
14
- vendor: ["vendor", "supplier"],
15
- customer: ["customer", "client"],
16
- client: ["client", "customer"],
17
- owner: ["owner", "assignee", "assigned_to"],
18
- assignee: ["assignee", "owner", "assigned_to"],
19
- company: ["company", "organization", "account"],
20
- account: ["account", "company", "organization"],
21
- };
22
- const NON_EVIDENCE_TOOL_NAMES = new Set([
23
- "load_skill",
24
- "load_skill_reference",
25
- "execute_skill_script",
26
- ]);
27
- function isRecord(value) {
28
- return typeof value === "object" && value !== null && !Array.isArray(value);
29
- }
30
- function truncate(value, maxLength) {
31
- return value.length > maxLength ? `${value.slice(0, maxLength)}…` : value;
32
- }
33
- function normalizeWhitespace(value) {
34
- return value.replace(/\s+/g, " ").trim();
35
- }
36
- function stripMarkdownCell(value) {
37
- return normalizeWhitespace(value.replace(/^[-–—>→\s]+/, "").replace(/\*\*/g, ""));
38
- }
39
- function normalizeEvidenceField(value) {
40
- return normalizeWhitespace(value)
41
- .toLowerCase()
42
- .replace(/[^a-z0-9]+/g, "_")
43
- .replace(/^_+|_+$/g, "");
44
- }
45
- function normalizeEvidenceValue(value) {
46
- return stripMarkdownCell(value)
47
- .replace(/^["'`]+|["'`]+$/g, "")
48
- .replace(/[.!?;,]+$/g, "")
49
- .trim();
50
- }
51
- function getRecordIds(value) {
52
- return Array.from(new Set(value.match(RECORD_ID_PATTERN) ?? []));
53
- }
54
- function mergeEvidenceField(evidenceByRecordId, recordId, field, value) {
55
- const normalizedField = normalizeEvidenceField(field);
56
- const normalizedValue = normalizeEvidenceValue(value);
57
- if (!normalizedField || !normalizedValue || normalizedValue === recordId)
58
- return;
59
- if (getRecordIds(normalizedValue).includes(recordId))
60
- return;
61
- const fields = evidenceByRecordId.get(recordId) ?? {};
62
- if (Object.keys(fields).length >= MAX_EVIDENCE_FIELDS_PER_RECORD && !(normalizedField in fields)) {
63
- return;
64
- }
65
- fields[normalizedField] = truncate(normalizedValue, 160);
66
- evidenceByRecordId.set(recordId, fields);
67
- }
68
- function parseMarkdownTableRow(line) {
69
- const trimmed = line.trim();
70
- const withoutOuterPipes = trimmed.replace(/^\|/, "").replace(/\|$/, "");
71
- return withoutOuterPipes.split("|").map(stripMarkdownCell);
72
- }
73
- function isMarkdownTableSeparator(line) {
74
- const trimmed = line.trim();
75
- const withoutOuterPipes = trimmed.replace(/^\|/, "").replace(/\|$/, "");
76
- const cells = withoutOuterPipes.split("|").map((cell) => cell.trim());
77
- return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell.replace(/\s+/g, "")));
78
- }
79
- function collectStringLeaves(value, strings = []) {
80
- if (strings.length >= MAX_EVIDENCE_STRINGS)
81
- return strings;
82
- if (typeof value === "string") {
83
- if (value.trim())
84
- strings.push(value.slice(0, MAX_EVIDENCE_TEXT_LENGTH));
85
- return strings;
86
- }
87
- if (Array.isArray(value)) {
88
- for (const item of value)
89
- collectStringLeaves(item, strings);
90
- return strings;
91
- }
92
- if (isRecord(value)) {
93
- for (const entry of Object.values(value))
94
- collectStringLeaves(entry, strings);
95
- }
96
- return strings;
97
- }
98
- function extractEvidenceFromMarkdownTable(lines, startIndex, evidenceByRecordId) {
99
- const headers = parseMarkdownTableRow(lines[startIndex] ?? "");
100
- let cursor = startIndex + 2;
101
- const rows = [];
102
- while (cursor < lines.length && lines[cursor]?.includes("|")) {
103
- const row = parseMarkdownTableRow(lines[cursor] ?? "");
104
- if (row.length === headers.length)
105
- rows.push(row);
106
- cursor++;
107
- }
108
- const keyValueRows = rows.filter((row) => row.length >= 2);
109
- const isKeyValueTable = headers.length === 2 &&
110
- normalizeEvidenceField(headers[0] ?? "") === "field" &&
111
- normalizeEvidenceField(headers[1] ?? "") === "value";
112
- const primaryRecordId = keyValueRows
113
- .map((row) => getRecordIds(row[1] ?? "")[0])
114
- .find((recordId) => typeof recordId === "string");
115
- if (isKeyValueTable && primaryRecordId) {
116
- for (const row of keyValueRows) {
117
- mergeEvidenceField(evidenceByRecordId, primaryRecordId, row[0] ?? "", row[1] ?? "");
118
- }
119
- return cursor;
120
- }
121
- for (const row of rows) {
122
- const rowIds = row.flatMap(getRecordIds);
123
- for (const recordId of rowIds) {
124
- for (const [index, value] of row.entries()) {
125
- const field = headers[index] ?? "";
126
- if (!field || getRecordIds(value).includes(recordId))
127
- continue;
128
- mergeEvidenceField(evidenceByRecordId, recordId, field, value);
129
- }
130
- }
131
- }
132
- return cursor;
133
- }
134
- export function extractCurrentRunEvidence(result) {
135
- const evidenceByRecordId = new Map();
136
- for (const text of collectStringLeaves(result)) {
137
- const lines = text.split(/\r?\n/);
138
- for (let index = 0; index < lines.length; index++) {
139
- const line = lines[index] ?? "";
140
- if (!line.includes("|") || !lines[index + 1]?.includes("|"))
141
- continue;
142
- if (!isMarkdownTableSeparator(lines[index + 1] ?? ""))
143
- continue;
144
- index = extractEvidenceFromMarkdownTable(lines, index, evidenceByRecordId) - 1;
145
- }
146
- }
147
- return Array.from(evidenceByRecordId.entries())
148
- .slice(0, MAX_EVIDENCE_RECORDS)
149
- .map(([recordId, fields]) => ({ recordId, fields }));
150
- }
151
- function getEvidenceFieldsForRecord(state, recordId) {
152
- const fields = {};
153
- for (const bucket of Object.values(state)) {
154
- for (const call of Object.values(bucket.calls)) {
155
- for (const evidence of call.evidence ?? []) {
156
- if (evidence.recordId !== recordId)
157
- continue;
158
- Object.assign(fields, evidence.fields);
159
- }
160
- }
161
- }
162
- return fields;
163
- }
164
- function getExpectedEvidenceField(fields, field) {
165
- const aliases = GUARDED_FACT_ALIASES[field] ?? [field];
166
- for (const alias of aliases) {
167
- if (fields[alias])
168
- return { field: alias, value: fields[alias] };
169
- }
170
- return null;
171
- }
172
- function hasCompanyLikeShape(value) {
173
- if (getRecordIds(value).length > 0)
174
- return false;
175
- if (/[€$£¥]\s?\d|\d{4,}/.test(value))
176
- return false;
177
- if (/\b(invoice|record|item|payment|approval|escalation|matching)\b/i.test(value))
178
- return false;
179
- return /\b(GmbH|LLC|Ltd|Limited|Inc|Corp|Corporation|Company|Co\.?|AG|SA|BV|NV|PLC)\b/.test(value) ||
180
- /^[A-Z][\p{L}'&.-]+(?:\s+[A-Z][\p{L}'&.-]+)+$/u.test(value);
181
- }
182
- function extractAssertedFieldValues(text) {
183
- const assertions = [];
184
- for (const field of Object.keys(GUARDED_FACT_ALIASES)) {
185
- const aliases = GUARDED_FACT_ALIASES[field] ?? [field];
186
- for (const alias of aliases) {
187
- const pattern = new RegExp(`\\b${alias.replace(/_/g, "[ _-]")}\\b\\s*(?:is|:|=|named|called)?\\s+(.{2,100}?)(?=\\s+(?:for|with|matched|against|on|and|to|has|was|will|ready|blocked|created|released)\\b|[.,;\\n]|$)`, "giu");
188
- for (const match of text.matchAll(pattern)) {
189
- const value = normalizeEvidenceValue(match[1] ?? "");
190
- if (hasCompanyLikeShape(value))
191
- assertions.push({ field, value });
192
- }
193
- }
194
- }
195
- return assertions;
196
- }
197
- function valuesConflict(actual, expected) {
198
- const normalize = (value) => normalizeWhitespace(value).toLowerCase();
199
- const left = normalize(actual);
200
- const right = normalize(expected);
201
- return left !== right && !left.includes(right) && !right.includes(left);
202
- }
203
- function escapeRegExp(value) {
204
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
205
- }
206
- function getRecordTextWindows(text, recordId) {
207
- const windows = [];
208
- const pattern = new RegExp(escapeRegExp(recordId), "g");
209
- for (const match of text.matchAll(pattern)) {
210
- const index = match.index ?? 0;
211
- const before = text.slice(0, index);
212
- const after = text.slice(index + recordId.length);
213
- const previousRecordMatches = Array.from(before.matchAll(RECORD_ID_PATTERN));
214
- const previousRecordMatch = previousRecordMatches.at(-1);
215
- const previousRecordEnd = previousRecordMatch && typeof previousRecordMatch.index === "number"
216
- ? previousRecordMatch.index + previousRecordMatch[0].length
217
- : -1;
218
- const windowStart = Math.max(before.lastIndexOf("."), before.lastIndexOf("\n"), before.lastIndexOf(";"));
219
- const nextRecordMatch = after.match(NEXT_RECORD_ID_PATTERN);
220
- const nextRecordIndex = nextRecordMatch?.index;
221
- const windowEnd = typeof nextRecordIndex === "number"
222
- ? Math.max(0, nextRecordIndex)
223
- : Math.min(after.length, 300);
224
- windows.push(normalizeWhitespace(text.slice(Math.max(previousRecordEnd, windowStart >= 0 ? windowStart + 1 : Math.max(0, index - 160)), index + recordId.length + windowEnd)));
225
- }
226
- return windows;
227
- }
228
- function primitiveStructuredValueToString(value) {
229
- if (typeof value === "string")
230
- return value.trim() || null;
231
- if (typeof value === "number" && Number.isFinite(value))
232
- return String(value);
233
- if (typeof value === "boolean")
234
- return String(value);
235
- return null;
236
- }
237
- function collectStructuredAssertionLines(value, lines = [], path = []) {
238
- if (lines.length >= MAX_STRUCTURED_ASSERTION_LINES)
239
- return lines;
240
- const primitiveValue = primitiveStructuredValueToString(value);
241
- if (primitiveValue !== null && path.length > 0) {
242
- lines.push(`${path.join(".")}: ${truncate(primitiveValue, 300)}`);
243
- return lines;
244
- }
245
- if (Array.isArray(value)) {
246
- for (const item of value) {
247
- collectStructuredAssertionLines(item, lines, path);
248
- if (lines.length >= MAX_STRUCTURED_ASSERTION_LINES)
249
- break;
250
- }
251
- return lines;
252
- }
253
- if (!isRecord(value))
254
- return lines;
255
- for (const [key, entry] of Object.entries(value)) {
256
- collectStructuredAssertionLines(entry, lines, [...path, key]);
257
- if (lines.length >= MAX_STRUCTURED_ASSERTION_LINES)
258
- break;
259
- }
260
- return lines;
261
- }
262
- export function validateInvokeAgentInputAgainstCurrentRunEvidence(state, input) {
263
- if (!isRecord(input))
264
- return { ok: true };
265
- const text = normalizeWhitespace([
266
- input.prompt,
267
- input.description,
268
- input.input,
269
- ...collectStructuredAssertionLines(input.context),
270
- ]
271
- .filter((value) => typeof value === "string")
272
- .join("\n"));
273
- if (!text)
274
- return { ok: true };
275
- for (const recordId of getRecordIds(text)) {
276
- const evidenceFields = getEvidenceFieldsForRecord(state, recordId);
277
- for (const window of getRecordTextWindows(text, recordId)) {
278
- for (const assertion of extractAssertedFieldValues(window)) {
279
- const expected = getExpectedEvidenceField(evidenceFields, assertion.field);
280
- if (!expected || !valuesConflict(assertion.value, expected.value))
281
- continue;
282
- return {
283
- ok: false,
284
- error: `invoke_agent input conflicts with current run evidence: ${recordId} ${expected.field} is ` +
285
- `"${expected.value}", but the tool input says "${assertion.value}". ` +
286
- "Regenerate the tool call from recorded evidence, or delegate only the record id.",
287
- };
288
- }
289
- }
290
- }
291
- return { ok: true };
292
- }
293
- function normalizeForFingerprint(value) {
294
- if (Array.isArray(value))
295
- return value.map(normalizeForFingerprint);
296
- if (!isRecord(value))
297
- return value;
298
- const normalized = {};
299
- for (const key of Object.keys(value).sort()) {
300
- normalized[key] = normalizeForFingerprint(value[key]);
301
- }
302
- return normalized;
303
- }
304
- export function createToolInputFingerprint(input) {
305
- return JSON.stringify(normalizeForFingerprint(input ?? {}));
306
- }
307
- function compactContactValue(value) {
308
- if (typeof value === "string")
309
- return value;
310
- if (!isRecord(value))
311
- return null;
312
- const compact = {};
313
- const emailAddress = value.emailAddress;
314
- if (isRecord(emailAddress)) {
315
- if (typeof emailAddress.name === "string")
316
- compact.name = emailAddress.name;
317
- if (typeof emailAddress.address === "string")
318
- compact.address = emailAddress.address;
319
- }
320
- for (const field of ["login", "name", "address", "email", "id"]) {
321
- if (typeof value[field] === "string" || typeof value[field] === "number") {
322
- compact[field] = value[field];
323
- }
324
- }
325
- return Object.keys(compact).length > 0 ? compact : null;
326
- }
327
- function compactObjectValue(value, depth = 2) {
328
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
329
- return typeof value === "string" ? truncate(value, MAX_FALLBACK_STRING_LENGTH) : value;
330
- }
331
- if (Array.isArray(value)) {
332
- const compacted = value.slice(0, MAX_OBJECT_ARRAY_ITEMS)
333
- .map((item) => compactObjectValue(item, depth - 1))
334
- .filter((item) => item !== null);
335
- return compacted.length > 0 ? compacted : null;
336
- }
337
- if (!isRecord(value) || depth < 0)
338
- return null;
339
- const compact = {};
340
- for (const [key, entry] of Object.entries(value)) {
341
- const compacted = compactObjectValue(entry, depth - 1);
342
- if (compacted !== null)
343
- compact[key] = compacted;
344
- }
345
- return Object.keys(compact).length > 0 ? compact : null;
346
- }
347
- function compactField(field, value) {
348
- if (field.kind === "contact")
349
- return compactContactValue(value);
350
- if (field.kind === "contact-array") {
351
- if (!Array.isArray(value))
352
- return null;
353
- const contacts = value
354
- .map((item) => compactContactValue(item))
355
- .filter((item) => item !== null);
356
- return contacts.length > 0 ? contacts : null;
357
- }
358
- if (field.kind === "string-array") {
359
- if (!Array.isArray(value))
360
- return null;
361
- const strings = value.filter((item) => typeof item === "string");
362
- return strings.length > 0 ? strings : null;
363
- }
364
- if (field.kind === "object") {
365
- return compactObjectValue(value);
366
- }
367
- if (typeof value === "string") {
368
- return field.maxLength ? truncate(value, field.maxLength) : value;
369
- }
370
- if (typeof value === "number" || typeof value === "boolean")
371
- return value;
372
- if (Array.isArray(value)) {
373
- const strings = value.filter((item) => typeof item === "string");
374
- return strings.length > 0 ? strings : null;
375
- }
376
- return null;
377
- }
378
- function compactItem(value, fields) {
379
- if (!isRecord(value))
380
- return null;
381
- const compact = {};
382
- for (const field of fields) {
383
- const fieldValue = compactField(field, value[field.name]);
384
- if (fieldValue !== null)
385
- compact[field.name] = fieldValue;
386
- }
387
- return Object.keys(compact).length > 0 ? compact : null;
388
- }
389
- function getSummaryItems(result, contract) {
390
- if (Array.isArray(result))
391
- return result;
392
- if (!isRecord(result))
393
- return null;
394
- for (const key of contract.collectionKeys) {
395
- const value = result[key];
396
- if (Array.isArray(value))
397
- return value;
398
- }
399
- if (contract.singleItem)
400
- return [result];
401
- return null;
402
- }
403
- function summarizeWithContract(result, contract) {
404
- const sourceItems = getSummaryItems(result, contract);
405
- if (!sourceItems)
406
- return null;
407
- const items = sourceItems
408
- .map((item) => compactItem(item, contract.itemFields))
409
- .filter((item) => item !== null);
410
- const summary = {
411
- [`${contract.collectionName}Count`]: sourceItems.length,
412
- [contract.collectionName]: items,
413
- omitted: contract.omitted,
414
- };
415
- if (isRecord(result) && contract.outputFields) {
416
- for (const field of contract.outputFields) {
417
- const fieldValue = compactField(field, result[field.name]);
418
- if (fieldValue !== null)
419
- summary[field.name] = fieldValue;
420
- }
421
- }
422
- return {
423
- summary,
424
- status: sourceItems.length === 0 ? "empty" : "success",
425
- };
426
- }
427
- function summarizeFallbackRecord(value) {
428
- const summary = {};
429
- for (const [key, entry] of Object.entries(value)) {
430
- if (typeof entry === "string")
431
- summary[key] = truncate(entry, MAX_FALLBACK_STRING_LENGTH);
432
- else if (typeof entry === "number" || typeof entry === "boolean")
433
- summary[key] = entry;
434
- }
435
- for (const [key, entry] of Object.entries(value)) {
436
- if (Array.isArray(entry)) {
437
- summary[`${key}Count`] = entry.length;
438
- summary[key] = entry.slice(0, MAX_FALLBACK_ITEMS).map((item) => isRecord(item) ? summarizeFallbackRecord(item) : item);
439
- if (entry.length > MAX_FALLBACK_ITEMS) {
440
- summary.omitted = `Only first ${MAX_FALLBACK_ITEMS} ${key} included`;
441
- }
442
- break;
443
- }
444
- }
445
- return Object.keys(summary).length > 0 ? summary : { keys: Object.keys(value).slice(0, 20) };
446
- }
447
- function summarizeFallback(result) {
448
- if (Array.isArray(result)) {
449
- return {
450
- status: result.length === 0 ? "empty" : "success",
451
- summary: {
452
- itemsCount: result.length,
453
- items: result.slice(0, MAX_FALLBACK_ITEMS).map((item) => isRecord(item) ? summarizeFallbackRecord(item) : item),
454
- ...(result.length > MAX_FALLBACK_ITEMS
455
- ? { omitted: `Only first ${MAX_FALLBACK_ITEMS} items included` }
456
- : {}),
457
- },
458
- };
459
- }
460
- if (isRecord(result)) {
461
- if ("error" in result) {
462
- return { status: "error", summary: summarizeFallbackRecord(result) };
463
- }
464
- return { status: "success", summary: summarizeFallbackRecord(result) };
465
- }
466
- if (typeof result === "string") {
467
- return {
468
- status: result.length === 0 ? "empty" : "success",
469
- summary: truncate(result, MAX_FALLBACK_STRING_LENGTH),
470
- };
471
- }
472
- return { status: result == null ? "empty" : "success", summary: result };
473
- }
474
- export function summarizeToolResultForCurrentRunState(toolName, result) {
475
- if (isRecord(result) && "error" in result) {
476
- return { status: "error", summary: summarizeFallbackRecord(result) };
477
- }
478
- const contract = historicalToolSummaries[toolName];
479
- if (contract) {
480
- const contracted = summarizeWithContract(result, contract);
481
- if (contracted)
482
- return contracted;
483
- }
484
- return summarizeFallback(result);
485
- }
486
- export function createCurrentRunToolState() {
487
- return {};
488
- }
489
- export function recordCurrentRunToolResult(state, input) {
490
- const fingerprint = createToolInputFingerprint(input.input);
491
- const toolBucket = state[input.toolName] ?? { calls: {} };
492
- const existingCall = toolBucket.calls[fingerprint];
493
- const { summary, status } = summarizeToolResultForCurrentRunState(input.toolName, input.result);
494
- const evidence = NON_EVIDENCE_TOOL_NAMES.has(input.toolName)
495
- ? []
496
- : extractCurrentRunEvidence(input.result);
497
- toolBucket.calls[fingerprint] = {
498
- toolCallIds: existingCall?.toolCallIds.includes(input.toolCallId)
499
- ? existingCall.toolCallIds
500
- : [...(existingCall?.toolCallIds ?? []), input.toolCallId],
501
- input: input.input ?? {},
502
- status,
503
- summary,
504
- ...(evidence.length > 0 ? { evidence } : {}),
505
- updatedAt: (input.now ?? new Date()).toISOString(),
506
- };
507
- state[input.toolName] = toolBucket;
508
- }
509
- function parseJsonRecord(value) {
510
- try {
511
- const parsed = JSON.parse(value);
512
- return isRecord(parsed) ? parsed : null;
513
- }
514
- catch {
515
- return null;
516
- }
517
- }
518
- function getToolCallInput(part) {
519
- if (isRecord(part.args))
520
- return part.args;
521
- if (isRecord(part.input))
522
- return part.input;
523
- if (typeof part.args === "string")
524
- return parseJsonRecord(part.args) ?? part.args;
525
- if (typeof part.input === "string")
526
- return parseJsonRecord(part.input) ?? part.input;
527
- if (typeof part.inputText === "string")
528
- return parseJsonRecord(part.inputText) ?? part.inputText;
529
- return {};
530
- }
531
- function getToolResultValue(part) {
532
- if ("result" in part)
533
- return part.result;
534
- if ("output" in part)
535
- return part.output;
536
- return undefined;
537
- }
538
- function getToolCallIdentity(part) {
539
- if (!isRecord(part))
540
- return null;
541
- const toolCallId = typeof part.toolCallId === "string"
542
- ? part.toolCallId
543
- : typeof part.tool_call_id === "string"
544
- ? part.tool_call_id
545
- : typeof part.id === "string"
546
- ? part.id
547
- : null;
548
- const toolName = typeof part.toolName === "string"
549
- ? part.toolName
550
- : typeof part.tool_name === "string"
551
- ? part.tool_name
552
- : typeof part.name === "string"
553
- ? part.name
554
- : null;
555
- return toolCallId && toolName ? { toolCallId, toolName } : null;
556
- }
557
- function isToolCallPart(part) {
558
- if (!isRecord(part))
559
- return false;
560
- const type = typeof part.type === "string" ? part.type : "";
561
- if (type === "tool-result" || type === "tool_result")
562
- return false;
563
- if (type === "tool-call" || type === "tool_call" || type.startsWith("tool-")) {
564
- return getToolCallIdentity(part) !== null;
565
- }
566
- return false;
567
- }
568
- function isToolResultLikePart(part) {
569
- if (!isRecord(part))
570
- return false;
571
- const type = typeof part.type === "string" ? part.type : "";
572
- if (type !== "tool-result" && type !== "tool_result")
573
- return false;
574
- return getToolCallIdentity(part) !== null && ("result" in part || "output" in part);
575
- }
576
- export function hydrateCurrentRunToolStateFromMessages(state, messages, options) {
577
- const toolCallInputs = new Map();
578
- for (const message of messages) {
579
- for (const part of message.parts ?? []) {
580
- if (isToolCallPart(part)) {
581
- const identity = getToolCallIdentity(part);
582
- if (!identity)
583
- continue;
584
- toolCallInputs.set(identity.toolCallId, {
585
- toolName: identity.toolName,
586
- input: getToolCallInput(part),
587
- });
588
- continue;
589
- }
590
- if (!isToolResultLikePart(part))
591
- continue;
592
- const identity = getToolCallIdentity(part);
593
- if (!identity)
594
- continue;
595
- const call = toolCallInputs.get(identity.toolCallId);
596
- recordCurrentRunToolResult(state, {
597
- toolCallId: identity.toolCallId,
598
- toolName: identity.toolName,
599
- input: call?.input ?? {},
600
- result: getToolResultValue(part),
601
- now: options?.now,
602
- });
603
- }
604
- }
605
- }
606
- export function hasCurrentRunToolState(state) {
607
- return Object.keys(state).some((toolName) => Object.keys(state[toolName]?.calls ?? {}).length > 0);
608
- }
609
- function compactStringParameter(value) {
610
- if (typeof value === "string" && value.trim().length > 0) {
611
- return value.trim();
612
- }
613
- if (typeof value === "number" && Number.isFinite(value)) {
614
- return String(value);
615
- }
616
- return null;
617
- }
618
- function findRecordIdInStructuredContext(value) {
619
- if (typeof value === "string") {
620
- return getRecordIds(value)[0] ?? null;
621
- }
622
- if (typeof value === "number" && Number.isFinite(value)) {
623
- return null;
624
- }
625
- if (Array.isArray(value)) {
626
- for (const item of value) {
627
- const found = findRecordIdInStructuredContext(item);
628
- if (found)
629
- return found;
630
- }
631
- return null;
632
- }
633
- if (!isRecord(value)) {
634
- return null;
635
- }
636
- for (const key of ["record_id", "recordId", "entity_id", "entityId", "id"]) {
637
- const found = compactStringParameter(value[key]);
638
- if (found && getRecordIds(found).length > 0)
639
- return found;
640
- }
641
- for (const entry of Object.values(value)) {
642
- const found = findRecordIdInStructuredContext(entry);
643
- if (found)
644
- return found;
645
- }
646
- return null;
647
- }
648
- function getSemanticToolCall(input) {
649
- if (!isRecord(input.call.input))
650
- return null;
651
- switch (input.toolName) {
652
- case "load_skill": {
653
- const skillId = compactStringParameter(input.call.input.skillId) ??
654
- compactStringParameter(input.call.input.skill_id);
655
- return skillId ? { key: `skill:${skillId}`, parameters: { skillId } } : null;
656
- }
657
- case "invoke_agent": {
658
- const agentId = compactStringParameter(input.call.input.agent_id) ??
659
- compactStringParameter(input.call.input.agentId);
660
- if (!agentId)
661
- return null;
662
- const stepId = compactStringParameter(input.call.input.step_id) ??
663
- compactStringParameter(input.call.input.stepId);
664
- const idempotencyKey = compactStringParameter(input.call.input.idempotency_key) ??
665
- compactStringParameter(input.call.input.idempotencyKey);
666
- const recordId = compactStringParameter(input.call.input.record_id) ??
667
- compactStringParameter(input.call.input.recordId) ??
668
- compactStringParameter(input.call.input.invoice_id) ??
669
- compactStringParameter(input.call.input.invoiceId) ??
670
- compactStringParameter(input.call.input.entity_id) ??
671
- compactStringParameter(input.call.input.entityId) ??
672
- findRecordIdInStructuredContext(input.call.input.context);
673
- const keySuffix = recordId
674
- ? `:record:${recordId}`
675
- : stepId
676
- ? `:step:${stepId}`
677
- : idempotencyKey
678
- ? `:idempotency:${idempotencyKey}`
679
- : "";
680
- const parameters = { agent_id: agentId };
681
- if (recordId)
682
- parameters.record_id = recordId;
683
- if (stepId)
684
- parameters.step_id = stepId;
685
- if (idempotencyKey)
686
- parameters.idempotency_key = idempotencyKey;
687
- return { key: `agent:${agentId}${keySuffix}`, parameters };
688
- }
689
- case "studio_todo_write": {
690
- const taskId = compactStringParameter(input.call.input.taskId) ??
691
- compactStringParameter(input.call.input.task_id);
692
- return taskId ? { key: `todo:${taskId}`, parameters: { taskId } } : null;
693
- }
694
- default:
695
- return null;
696
- }
697
- }
698
- function mergeSemanticToolCall(existing, call, parameters) {
699
- return {
700
- status: call.status,
701
- callCount: (existing?.callCount ?? 0) + call.toolCallIds.length,
702
- parameters: existing?.parameters ?? parameters,
703
- summary: call.summary,
704
- };
705
- }
706
- function usesSemanticPromptKeys(toolName) {
707
- return toolName === "load_skill" ||
708
- toolName === "invoke_agent" ||
709
- toolName === "studio_todo_write";
710
- }
711
- function createPromptCallKey(input) {
712
- return usesSemanticPromptKeys(input.toolName) ? `call:${input.index + 1}` : input.fingerprint;
713
- }
714
- export function projectCurrentRunToolStateForPrompt(state) {
715
- const tools = {};
716
- const skills = {};
717
- const actions = {};
718
- for (const [toolName, bucket] of Object.entries(state)) {
719
- const calls = {};
720
- const semanticCalls = {};
721
- for (const [index, [fingerprint, call]] of Object.entries(bucket.calls).entries()) {
722
- calls[createPromptCallKey({ toolName, fingerprint, index })] = {
723
- status: call.status,
724
- summary: call.summary,
725
- };
726
- const semantic = getSemanticToolCall({ toolName, call });
727
- if (semantic) {
728
- const mergedSemanticCall = mergeSemanticToolCall(semanticCalls[semantic.key], call, semantic.parameters);
729
- semanticCalls[semantic.key] = mergedSemanticCall;
730
- if (toolName === "load_skill" && semantic.parameters.skillId) {
731
- skills[semantic.parameters.skillId] = {
732
- status: mergedSemanticCall.status,
733
- callCount: mergedSemanticCall.callCount,
734
- source: `tools.${toolName}.semanticCalls.${semantic.key}`,
735
- summary: mergedSemanticCall.summary,
736
- };
737
- }
738
- actions[`${toolName}:${semantic.key}`] = {
739
- status: call.status,
740
- source: `tools.${toolName}.semanticCalls.${semantic.key}`,
741
- summary: call.summary,
742
- };
743
- }
744
- }
745
- if (Object.keys(calls).length > 0) {
746
- tools[toolName] = {
747
- calls,
748
- ...(Object.keys(semanticCalls).length > 0 ? { semanticCalls } : {}),
749
- };
750
- }
751
- }
752
- return {
753
- tools,
754
- ...(Object.keys(skills).length > 0 ? { skills } : {}),
755
- ...(Object.keys(actions).length > 0 ? { actions } : {}),
756
- };
757
- }
758
- export function appendCurrentRunToolStateToSystemPrompt(systemPrompt, state) {
759
- if (!hasCurrentRunToolState(state))
760
- return systemPrompt;
761
- const promptState = projectCurrentRunToolStateForPrompt(state);
762
- return `${systemPrompt}\n\n<run_state current_run=\"true\">\n${JSON.stringify(promptState)}\n</run_state>`;
763
- }