@yeaft/webchat-agent 1.0.263 → 1.0.265
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/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +50 -50
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/archive/tool-results.js +1 -1
- package/yeaft/llm/adapter.js +50 -0
- package/yeaft/llm/anthropic.js +6 -3
- package/yeaft/llm/openai-responses.js +7 -4
|
Binary file
|
package/package.json
CHANGED
|
@@ -77,7 +77,7 @@ export async function archiveOne({ root, scopeDir, message }) {
|
|
|
77
77
|
await fs.mkdir(dirname(path), { recursive: true });
|
|
78
78
|
await fs.writeFile(path, body, 'utf8');
|
|
79
79
|
const sizeStr = formatSize(body.length);
|
|
80
|
-
const preview = body.slice(0, STUB_PREVIEW_LEN).replace(/\s+/g, ' ');
|
|
80
|
+
const preview = body.slice(0, STUB_PREVIEW_LEN).toWellFormed().replace(/\s+/g, ' ');
|
|
81
81
|
const stub = {
|
|
82
82
|
role: 'tool',
|
|
83
83
|
toolCallId,
|
package/yeaft/llm/adapter.js
CHANGED
|
@@ -419,6 +419,56 @@ export function redactRawRequest(req) {
|
|
|
419
419
|
return { url: req.url, method: req.method, headers, body: req.body };
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
+
/**
|
|
423
|
+
* Return a wire-safe copy of a JSON-compatible value.
|
|
424
|
+
*
|
|
425
|
+
* JavaScript strings may contain lone UTF-16 surrogates. JSON.stringify keeps
|
|
426
|
+
* them as `\\ud800`-style escapes, but strict API servers reject them because
|
|
427
|
+
* they cannot represent Unicode scalar values in UTF-8. Replace only malformed
|
|
428
|
+
* code units with U+FFFD; valid surrogate pairs (including emoji) are kept.
|
|
429
|
+
*
|
|
430
|
+
* @param {any} value
|
|
431
|
+
* @returns {any}
|
|
432
|
+
*/
|
|
433
|
+
export function toWellFormedJson(value) {
|
|
434
|
+
// Let the native serializer retain its complete semantics first: toJSON,
|
|
435
|
+
// boxed primitives, non-finite numbers, array holes, and omitted values.
|
|
436
|
+
const serialized = JSON.stringify(value, (_key, child) => {
|
|
437
|
+
if (typeof child === 'string') return child.toWellFormed();
|
|
438
|
+
// JSON.stringify invokes the replacer before unboxing String objects.
|
|
439
|
+
// String#valueOf checks the real [[StringData]] internal slot across realms;
|
|
440
|
+
// unlike instanceof or Object#toString, it cannot be spoofed by a caller.
|
|
441
|
+
if (child && typeof child === 'object') {
|
|
442
|
+
try {
|
|
443
|
+
return String.prototype.valueOf.call(child).toWellFormed();
|
|
444
|
+
} catch (err) {
|
|
445
|
+
if (!(err instanceof TypeError)) throw err;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return child;
|
|
449
|
+
});
|
|
450
|
+
if (serialized === undefined) return undefined;
|
|
451
|
+
|
|
452
|
+
// The replacer cannot rename object keys. Rebuild only the already-serialized
|
|
453
|
+
// plain JSON tree so malformed keys are fixed too. Null-prototype containers
|
|
454
|
+
// keep an own "__proto__" key as data rather than invoking the legacy setter.
|
|
455
|
+
const normalizeKeys = child => {
|
|
456
|
+
if (Array.isArray(child)) return child.map(normalizeKeys);
|
|
457
|
+
if (!child || typeof child !== 'object') return child;
|
|
458
|
+
const out = Object.create(null);
|
|
459
|
+
for (const [key, nested] of Object.entries(child)) {
|
|
460
|
+
Object.defineProperty(out, key.toWellFormed(), {
|
|
461
|
+
value: normalizeKeys(nested),
|
|
462
|
+
enumerable: true,
|
|
463
|
+
configurable: true,
|
|
464
|
+
writable: true,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
return out;
|
|
468
|
+
};
|
|
469
|
+
return normalizeKeys(JSON.parse(serialized));
|
|
470
|
+
}
|
|
471
|
+
|
|
422
472
|
/**
|
|
423
473
|
* Snapshot a Fetch Response's headers into a plain object for the debug
|
|
424
474
|
* panel. Defensive against polyfilled / mocked Response shapes that don't
|
package/yeaft/llm/anthropic.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
redactRawRequest,
|
|
20
20
|
safeHeaders,
|
|
21
21
|
SseLineBuffer,
|
|
22
|
+
toWellFormedJson,
|
|
22
23
|
} from './adapter.js';
|
|
23
24
|
import {
|
|
24
25
|
normalizeEffort,
|
|
@@ -261,6 +262,7 @@ export class AnthropicAdapter extends LLMAdapter {
|
|
|
261
262
|
|
|
262
263
|
const translatedTools = this.#translateTools(tools);
|
|
263
264
|
if (translatedTools) body.tools = translatedTools;
|
|
265
|
+
const wireBody = toWellFormedJson(body);
|
|
264
266
|
|
|
265
267
|
const url = `${this.#baseUrl}/v1/messages`;
|
|
266
268
|
const headers = this.#headers();
|
|
@@ -268,7 +270,7 @@ export class AnthropicAdapter extends LLMAdapter {
|
|
|
268
270
|
// Expose the raw request (auth-redacted) for the debug panel. The body
|
|
269
271
|
// is captured verbatim — never truncated — so "copy request" matches
|
|
270
272
|
// exactly what we POST to the LLM.
|
|
271
|
-
const rawRequest = redactRawRequest({ url, method: 'POST', headers, body });
|
|
273
|
+
const rawRequest = redactRawRequest({ url, method: 'POST', headers, body: wireBody });
|
|
272
274
|
|
|
273
275
|
let response;
|
|
274
276
|
try {
|
|
@@ -276,7 +278,7 @@ export class AnthropicAdapter extends LLMAdapter {
|
|
|
276
278
|
response = await fetch(url, {
|
|
277
279
|
method: 'POST',
|
|
278
280
|
headers,
|
|
279
|
-
body: JSON.stringify(
|
|
281
|
+
body: JSON.stringify(wireBody),
|
|
280
282
|
signal,
|
|
281
283
|
});
|
|
282
284
|
} catch (err) {
|
|
@@ -544,6 +546,7 @@ export class AnthropicAdapter extends LLMAdapter {
|
|
|
544
546
|
if ((thinkingV1Enabled() || effortSource === 'user') && normEffort) {
|
|
545
547
|
applyAnthropicThinking(body, model, normEffort, effortContext);
|
|
546
548
|
}
|
|
549
|
+
const wireBody = toWellFormedJson(body);
|
|
547
550
|
|
|
548
551
|
let response;
|
|
549
552
|
try {
|
|
@@ -551,7 +554,7 @@ export class AnthropicAdapter extends LLMAdapter {
|
|
|
551
554
|
response = await fetch(`${this.#baseUrl}/v1/messages`, {
|
|
552
555
|
method: 'POST',
|
|
553
556
|
headers: this.#headers(),
|
|
554
|
-
body: JSON.stringify(
|
|
557
|
+
body: JSON.stringify(wireBody),
|
|
555
558
|
signal,
|
|
556
559
|
});
|
|
557
560
|
} catch (err) {
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
redactRawRequest,
|
|
39
39
|
safeHeaders,
|
|
40
40
|
SseLineBuffer,
|
|
41
|
+
toWellFormedJson,
|
|
41
42
|
} from './adapter.js';
|
|
42
43
|
import {
|
|
43
44
|
normalizeEffort,
|
|
@@ -187,7 +188,7 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
|
|
|
187
188
|
type: 'function_call',
|
|
188
189
|
call_id: tc.id,
|
|
189
190
|
name: tc.name,
|
|
190
|
-
arguments: JSON.stringify(tc.input ?? {}),
|
|
191
|
+
arguments: JSON.stringify(toWellFormedJson(tc.input ?? {})),
|
|
191
192
|
});
|
|
192
193
|
}
|
|
193
194
|
}
|
|
@@ -285,6 +286,7 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
|
|
|
285
286
|
}
|
|
286
287
|
|
|
287
288
|
if (extraBody) Object.assign(body, extraBody);
|
|
289
|
+
const wireBody = toWellFormedJson(body);
|
|
288
290
|
|
|
289
291
|
const url = `${this.#baseUrl}/responses`;
|
|
290
292
|
const headers = {
|
|
@@ -294,7 +296,7 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
|
|
|
294
296
|
|
|
295
297
|
// Expose the raw request (auth-redacted) for the debug panel. See
|
|
296
298
|
// `redactRawRequest` in adapter.js for the verbatim-design rationale.
|
|
297
|
-
const rawRequest = redactRawRequest({ url, method: 'POST', headers, body });
|
|
299
|
+
const rawRequest = redactRawRequest({ url, method: 'POST', headers, body: wireBody });
|
|
298
300
|
|
|
299
301
|
let response;
|
|
300
302
|
try {
|
|
@@ -302,7 +304,7 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
|
|
|
302
304
|
response = await fetch(url, {
|
|
303
305
|
method: 'POST',
|
|
304
306
|
headers,
|
|
305
|
-
body: JSON.stringify(
|
|
307
|
+
body: JSON.stringify(wireBody),
|
|
306
308
|
signal,
|
|
307
309
|
});
|
|
308
310
|
} catch (err) {
|
|
@@ -540,6 +542,7 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
|
|
|
540
542
|
}
|
|
541
543
|
|
|
542
544
|
if (extraBody) Object.assign(body, extraBody);
|
|
545
|
+
const wireBody = toWellFormedJson(body);
|
|
543
546
|
|
|
544
547
|
let response;
|
|
545
548
|
try {
|
|
@@ -550,7 +553,7 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
|
|
|
550
553
|
'Content-Type': 'application/json',
|
|
551
554
|
'Authorization': `Bearer ${this.#apiKey}`,
|
|
552
555
|
},
|
|
553
|
-
body: JSON.stringify(
|
|
556
|
+
body: JSON.stringify(wireBody),
|
|
554
557
|
signal,
|
|
555
558
|
});
|
|
556
559
|
} catch (err) {
|