osborn 0.8.13 → 0.8.15
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/claude-auth.js +6 -0
- package/dist/index.js +53 -6
- package/package.json +1 -1
package/dist/claude-auth.js
CHANGED
|
@@ -496,8 +496,14 @@ export async function ensureClaudeAuth(sendToFrontend) {
|
|
|
496
496
|
});
|
|
497
497
|
},
|
|
498
498
|
onComplete: () => {
|
|
499
|
+
// Include the captured token so the frontend can persist it to the
|
|
500
|
+
// host-persistent layer via the Sprites API. Without this, credentials
|
|
501
|
+
// written inside the service container's ephemeral overlay are lost on
|
|
502
|
+
// every warm→running transition (service re-registration creates a
|
|
503
|
+
// fresh container).
|
|
499
504
|
fanout('claude_auth_complete', {
|
|
500
505
|
message: 'Claude authenticated successfully. Starting voice session...',
|
|
506
|
+
token: process.env.CLAUDE_CODE_OAUTH_TOKEN || undefined,
|
|
501
507
|
});
|
|
502
508
|
},
|
|
503
509
|
onError: (message) => {
|
package/dist/index.js
CHANGED
|
@@ -818,7 +818,13 @@ async function main() {
|
|
|
818
818
|
proactivePromptHistory = [];
|
|
819
819
|
}
|
|
820
820
|
// Helper to send data to frontend (with size limit handling)
|
|
821
|
-
|
|
821
|
+
//
|
|
822
|
+
// WebRTC SCTP data channel max message size is ~256KB. Sending larger
|
|
823
|
+
// payloads corrupts the publisher transport, killing ALL subsequent sends.
|
|
824
|
+
// We enforce a soft limit (truncate text/content fields) and a hard limit
|
|
825
|
+
// (drop the message entirely with a warning) to prevent this.
|
|
826
|
+
const MAX_MESSAGE_SIZE = 60000; // soft limit — truncate text/content fields
|
|
827
|
+
const HARD_MAX_MESSAGE_SIZE = 200000; // hard limit — drop if still too large after truncation
|
|
822
828
|
async function sendToFrontend(data) {
|
|
823
829
|
if (!localParticipant) {
|
|
824
830
|
console.log('⚠️ sendToFrontend: no localParticipant!');
|
|
@@ -827,18 +833,36 @@ async function main() {
|
|
|
827
833
|
try {
|
|
828
834
|
const encoder = new TextEncoder();
|
|
829
835
|
let jsonData = JSON.stringify(data);
|
|
830
|
-
// If message is too large, truncate the text content
|
|
836
|
+
// If message is too large, truncate the text or content field
|
|
831
837
|
if (jsonData.length > MAX_MESSAGE_SIZE) {
|
|
832
838
|
const truncatedData = { ...data };
|
|
839
|
+
// Try truncating .text first (assistant_response, claude_output, etc.)
|
|
833
840
|
if (truncatedData.text && typeof truncatedData.text === 'string') {
|
|
834
841
|
const overhead = JSON.stringify({ ...truncatedData, text: '' }).length;
|
|
835
842
|
const maxTextLength = MAX_MESSAGE_SIZE - overhead - 100;
|
|
836
843
|
truncatedData.text = truncatedData.text.substring(0, maxTextLength) + '\n\n[Message truncated due to size limit]';
|
|
837
844
|
jsonData = JSON.stringify(truncatedData);
|
|
838
|
-
console.log(`⚠️ Message truncated from ${data.text?.length} to ${truncatedData.text.length} chars`);
|
|
845
|
+
console.log(`⚠️ Message truncated .text from ${data.text?.length} to ${truncatedData.text.length} chars`);
|
|
846
|
+
}
|
|
847
|
+
// Also try truncating .content (research_artifact_content, plan_file_content)
|
|
848
|
+
if (jsonData.length > MAX_MESSAGE_SIZE && truncatedData.content && typeof truncatedData.content === 'string') {
|
|
849
|
+
const overhead = JSON.stringify({ ...truncatedData, content: '' }).length;
|
|
850
|
+
const maxContentLength = MAX_MESSAGE_SIZE - overhead - 100;
|
|
851
|
+
truncatedData.content = truncatedData.content.substring(0, maxContentLength) + '\n\n[Content truncated due to size limit]';
|
|
852
|
+
truncatedData.truncated = true;
|
|
853
|
+
truncatedData.originalSize = Buffer.byteLength(data.content, 'utf-8');
|
|
854
|
+
jsonData = JSON.stringify(truncatedData);
|
|
855
|
+
console.log(`⚠️ Message truncated .content from ${data.content?.length} to ${truncatedData.content.length} chars`);
|
|
839
856
|
}
|
|
840
857
|
}
|
|
858
|
+
// Hard cap — if still too large after truncation, drop entirely.
|
|
859
|
+
// This prevents a 480KB base64 image or similar from killing the
|
|
860
|
+
// WebRTC publisher transport (which is unrecoverable without reconnect).
|
|
841
861
|
const payload = encoder.encode(jsonData);
|
|
862
|
+
if (payload.length > HARD_MAX_MESSAGE_SIZE) {
|
|
863
|
+
console.error(`❌ sendToFrontend: dropping message (${(payload.length / 1024).toFixed(0)}KB > ${(HARD_MAX_MESSAGE_SIZE / 1024).toFixed(0)}KB hard limit) — type: ${data.type}`);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
842
866
|
await localParticipant.publishData(payload, {
|
|
843
867
|
reliable: true,
|
|
844
868
|
topic: 'osborn-updates',
|
|
@@ -2677,13 +2701,36 @@ async function main() {
|
|
|
2677
2701
|
const fileName = filePath.split('/').pop() || '';
|
|
2678
2702
|
const ext = fileName.split('.').pop()?.toLowerCase() || '';
|
|
2679
2703
|
const isImage = ['png', 'jpg', 'jpeg', 'gif', 'webp'].includes(ext);
|
|
2704
|
+
// WebRTC SCTP data channel max message size is ~256KB. Sending
|
|
2705
|
+
// larger payloads corrupts the publisher transport, killing ALL
|
|
2706
|
+
// subsequent sends (publishData, streamBytes, publishTranscription)
|
|
2707
|
+
// with "could not establish publisher connection: timeout". This
|
|
2708
|
+
// was the root cause of the career-ops session bug: a 480KB
|
|
2709
|
+
// evaluation report blew through the limit on resume.
|
|
2710
|
+
const MAX_DATA_CHANNEL_BYTES = 200_000; // 200KB — safe margin under 256KB SCTP limit
|
|
2680
2711
|
if (isImage) {
|
|
2681
|
-
const
|
|
2682
|
-
|
|
2712
|
+
const stats = fs.statSync(filePath);
|
|
2713
|
+
const base64Size = Math.ceil(stats.size * 4 / 3); // base64 inflates ~33%
|
|
2714
|
+
if (base64Size > MAX_DATA_CHANNEL_BYTES) {
|
|
2715
|
+
console.log(`⚠️ Artifact too large for data channel: ${fileName} (${(base64Size / 1024).toFixed(0)}KB base64) — sending truncation notice`);
|
|
2716
|
+
await sendToFrontend({ type: 'research_artifact_content', filePath, content: '', fileName, isImage: false, truncated: true, originalSize: stats.size });
|
|
2717
|
+
}
|
|
2718
|
+
else {
|
|
2719
|
+
const base64 = fs.readFileSync(filePath, 'base64');
|
|
2720
|
+
await sendToFrontend({ type: 'research_artifact_content', filePath, content: base64, fileName, isImage: true, mimeType: `image/${ext}` });
|
|
2721
|
+
}
|
|
2683
2722
|
}
|
|
2684
2723
|
else {
|
|
2685
2724
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
2686
|
-
|
|
2725
|
+
if (Buffer.byteLength(content, 'utf-8') > MAX_DATA_CHANNEL_BYTES) {
|
|
2726
|
+
// Send a truncated preview + metadata so the frontend knows the file exists
|
|
2727
|
+
const truncated = content.substring(0, 50_000); // ~50KB text preview
|
|
2728
|
+
console.log(`⚠️ Artifact too large for data channel: ${fileName} (${(Buffer.byteLength(content, 'utf-8') / 1024).toFixed(0)}KB) — sending truncated preview`);
|
|
2729
|
+
await sendToFrontend({ type: 'research_artifact_content', filePath, content: truncated, fileName, isImage: false, truncated: true, originalSize: Buffer.byteLength(content, 'utf-8') });
|
|
2730
|
+
}
|
|
2731
|
+
else {
|
|
2732
|
+
await sendToFrontend({ type: 'research_artifact_content', filePath, content, fileName, isImage: false });
|
|
2733
|
+
}
|
|
2687
2734
|
}
|
|
2688
2735
|
}
|
|
2689
2736
|
catch (err) {
|