claude-threads 1.6.2 → 1.7.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.
@@ -15849,7 +15849,7 @@ var require_validation3 = __commonJS((exports, module) => {
15849
15849
 
15850
15850
  // node_modules/ws/lib/receiver.js
15851
15851
  var require_receiver = __commonJS((exports, module) => {
15852
- var { Writable: Writable2 } = __require("stream");
15852
+ var { Writable } = __require("stream");
15853
15853
  var PerMessageDeflate = require_permessage_deflate();
15854
15854
  var {
15855
15855
  BINARY_TYPES,
@@ -15868,7 +15868,7 @@ var require_receiver = __commonJS((exports, module) => {
15868
15868
  var INFLATING = 5;
15869
15869
  var DEFER_EVENT = 6;
15870
15870
 
15871
- class Receiver extends Writable2 {
15871
+ class Receiver extends Writable {
15872
15872
  constructor(options2 = {}) {
15873
15873
  super();
15874
15874
  this._allowSynchronousEvents = options2.allowSynchronousEvents !== undefined ? options2.allowSynchronousEvents : true;
@@ -16906,7 +16906,7 @@ var require_websocket = __commonJS((exports, module) => {
16906
16906
  var net = __require("net");
16907
16907
  var tls = __require("tls");
16908
16908
  var { randomBytes, createHash } = __require("crypto");
16909
- var { Duplex, Readable: Readable2 } = __require("stream");
16909
+ var { Duplex, Readable } = __require("stream");
16910
16910
  var { URL: URL2 } = __require("url");
16911
16911
  var PerMessageDeflate = require_permessage_deflate();
16912
16912
  var Receiver = require_receiver();
@@ -52407,661 +52407,28 @@ function createMessageManagerEvents() {
52407
52407
 
52408
52408
  // src/operations/streaming/handler.ts
52409
52409
  var import_yauzl = __toESM(require_yauzl(), 1);
52410
- import { createGunzip } from "zlib";
52411
- import { pipeline as pipeline2 } from "stream/promises";
52412
- import { Readable, Writable } from "stream";
52413
52410
  var log2 = createLogger("streaming");
52414
52411
  var MAX_PDF_SIZE = 32 * 1024 * 1024;
52415
52412
  var MAX_TEXT_FILE_SIZE = 1 * 1024 * 1024;
52416
52413
  var MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024;
52417
52414
  var MAX_ZIP_SIZE = 50 * 1024 * 1024;
52418
52415
  var MAX_GZIP_SIZE = 50 * 1024 * 1024;
52419
- var MAX_ZIP_FILES = 20;
52420
- var SUPPORTED_IMAGE_TYPES = [
52421
- "image/jpeg",
52422
- "image/png",
52423
- "image/gif",
52424
- "image/webp"
52425
- ];
52426
- var SUPPORTED_TEXT_TYPES = [
52427
- "text/plain",
52428
- "text/markdown",
52429
- "text/csv",
52430
- "text/xml",
52431
- "text/yaml",
52432
- "text/x-yaml",
52433
- "application/json",
52434
- "application/xml",
52435
- "application/x-yaml",
52436
- "application/yaml"
52437
- ];
52438
- var TEXT_FILE_EXTENSIONS = [
52439
- ".txt",
52440
- ".md",
52441
- ".markdown",
52442
- ".json",
52443
- ".csv",
52444
- ".xml",
52445
- ".yaml",
52446
- ".yml",
52447
- ".js",
52448
- ".ts",
52449
- ".jsx",
52450
- ".tsx",
52451
- ".py",
52452
- ".rb",
52453
- ".go",
52454
- ".rs",
52455
- ".java",
52456
- ".c",
52457
- ".cpp",
52458
- ".h",
52459
- ".hpp",
52460
- ".cs",
52461
- ".php",
52462
- ".swift",
52463
- ".kt",
52464
- ".scala",
52465
- ".sh",
52466
- ".bash",
52467
- ".zsh",
52468
- ".fish",
52469
- ".ps1",
52470
- ".bat",
52471
- ".cmd",
52472
- ".html",
52473
- ".htm",
52474
- ".css",
52475
- ".scss",
52476
- ".sass",
52477
- ".less",
52478
- ".sql",
52479
- ".graphql",
52480
- ".gql",
52481
- ".toml",
52482
- ".ini",
52483
- ".cfg",
52484
- ".conf",
52485
- ".env",
52486
- ".properties",
52487
- ".dockerfile",
52488
- ".gitignore",
52489
- ".gitattributes",
52490
- ".editorconfig"
52491
- ];
52492
- function isImageFile(file2) {
52493
- return file2.mimeType.startsWith("image/") && SUPPORTED_IMAGE_TYPES.includes(file2.mimeType);
52494
- }
52495
- function isPdfFile(file2) {
52496
- return file2.mimeType === "application/pdf" || file2.name.toLowerCase().endsWith(".pdf");
52497
- }
52498
- function isTextFile(file2) {
52499
- if (SUPPORTED_TEXT_TYPES.includes(file2.mimeType)) {
52500
- return true;
52501
- }
52502
- const lowerName = file2.name.toLowerCase();
52503
- return TEXT_FILE_EXTENSIONS.some((ext) => lowerName.endsWith(ext));
52504
- }
52505
- function isGzipFile(file2) {
52506
- return file2.mimeType === "application/gzip" || file2.mimeType === "application/x-gzip" || file2.name.toLowerCase().endsWith(".gz");
52507
- }
52508
- function isZipFile(file2) {
52509
- return file2.mimeType === "application/zip" || file2.mimeType === "application/x-zip-compressed" || file2.name.toLowerCase().endsWith(".zip");
52510
- }
52511
- function categorizeFile(file2) {
52512
- if (isImageFile(file2))
52513
- return "image";
52514
- if (isPdfFile(file2))
52515
- return "pdf";
52516
- if (isZipFile(file2))
52517
- return "zip";
52518
- if (isGzipFile(file2))
52519
- return "gzip";
52520
- if (isTextFile(file2))
52521
- return "text";
52522
- return "unsupported";
52523
- }
52524
- async function processImageFile(file2, platform, debug = false) {
52525
- try {
52526
- if (!platform.downloadFile) {
52527
- return {
52528
- skipped: {
52529
- name: file2.name,
52530
- reason: "Platform does not support file downloads"
52531
- }
52532
- };
52533
- }
52534
- const buffer = await platform.downloadFile(file2.id);
52535
- const base644 = buffer.toString("base64");
52536
- if (debug) {
52537
- log2.debug(`Attached image: ${file2.name} (${file2.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
52538
- }
52539
- return {
52540
- block: {
52541
- type: "image",
52542
- source: {
52543
- type: "base64",
52544
- media_type: file2.mimeType,
52545
- data: base644
52546
- }
52547
- }
52548
- };
52549
- } catch (err) {
52550
- log2.error(`Failed to download image ${file2.name}: ${err}`);
52551
- return {
52552
- skipped: {
52553
- name: file2.name,
52554
- reason: `Download failed: ${err instanceof Error ? err.message : String(err)}`
52555
- }
52556
- };
52557
- }
52558
- }
52559
- async function processPdfFile(file2, platform, debug = false) {
52560
- try {
52561
- if (!platform.downloadFile) {
52562
- return {
52563
- skipped: {
52564
- name: file2.name,
52565
- reason: "Platform does not support file downloads"
52566
- }
52567
- };
52568
- }
52569
- const buffer = await platform.downloadFile(file2.id);
52570
- if (buffer.length > MAX_PDF_SIZE) {
52571
- return {
52572
- skipped: {
52573
- name: file2.name,
52574
- reason: `PDF exceeds ${Math.round(MAX_PDF_SIZE / 1024 / 1024)}MB limit (${Math.round(buffer.length / 1024 / 1024)}MB)`,
52575
- suggestion: "Try splitting the PDF into smaller parts"
52576
- }
52577
- };
52578
- }
52579
- const base644 = buffer.toString("base64");
52580
- if (debug) {
52581
- log2.debug(`Attached PDF: ${file2.name} (${Math.round(buffer.length / 1024)}KB)`);
52582
- }
52583
- return {
52584
- block: {
52585
- type: "document",
52586
- source: {
52587
- type: "base64",
52588
- media_type: "application/pdf",
52589
- data: base644
52590
- },
52591
- title: file2.name
52592
- }
52593
- };
52594
- } catch (err) {
52595
- log2.error(`Failed to process PDF ${file2.name}: ${err}`);
52596
- return {
52597
- skipped: {
52598
- name: file2.name,
52599
- reason: `Processing failed: ${err instanceof Error ? err.message : String(err)}`
52600
- }
52601
- };
52602
- }
52603
- }
52604
- async function processTextFile(file2, platform, debug = false) {
52605
- try {
52606
- if (!platform.downloadFile) {
52607
- return {
52608
- skipped: {
52609
- name: file2.name,
52610
- reason: "Platform does not support file downloads"
52611
- }
52612
- };
52613
- }
52614
- const buffer = await platform.downloadFile(file2.id);
52615
- if (buffer.length > MAX_TEXT_FILE_SIZE) {
52616
- return {
52617
- skipped: {
52618
- name: file2.name,
52619
- reason: `File exceeds ${Math.round(MAX_TEXT_FILE_SIZE / 1024)}KB limit (${Math.round(buffer.length / 1024)}KB)`,
52620
- suggestion: "Try splitting the file or extracting relevant portions"
52621
- }
52622
- };
52623
- }
52624
- const content = buffer.toString("utf-8");
52625
- if (debug) {
52626
- log2.debug(`Attached text file: ${file2.name} (${Math.round(buffer.length / 1024)}KB)`);
52627
- }
52628
- const wrappedContent = formatTextFileContent(file2.name, content);
52629
- return {
52630
- block: {
52631
- type: "text",
52632
- text: wrappedContent
52633
- }
52634
- };
52635
- } catch (err) {
52636
- log2.error(`Failed to process text file ${file2.name}: ${err}`);
52637
- return {
52638
- skipped: {
52639
- name: file2.name,
52640
- reason: `Processing failed: ${err instanceof Error ? err.message : String(err)}`
52641
- }
52642
- };
52643
- }
52644
- }
52645
- function formatTextFileContent(filename, content) {
52646
- return `\uD83D\uDCC4 **${filename}**:
52647
- \`\`\`
52648
- ${content}
52649
- \`\`\``;
52650
- }
52651
- async function decompressGzipStream(compressedBuffer) {
52652
- const chunks = [];
52653
- let totalSize = 0;
52654
- const gunzip = createGunzip();
52655
- const source = Readable.from(compressedBuffer);
52656
- const collector = new Writable({
52657
- write(chunk, _encoding, callback) {
52658
- totalSize += chunk.length;
52659
- if (totalSize > MAX_DECOMPRESSED_SIZE) {
52660
- callback(new Error(`Decompressed size exceeds ${Math.round(MAX_DECOMPRESSED_SIZE / 1024 / 1024)}MB limit`));
52661
- return;
52662
- }
52663
- chunks.push(chunk);
52664
- callback();
52665
- }
52666
- });
52667
- await pipeline2(source, gunzip, collector);
52668
- return Buffer.concat(chunks);
52669
- }
52670
- async function processGzipFile(file2, platform, debug = false) {
52671
- try {
52672
- if (!platform.downloadFile) {
52673
- return {
52674
- skipped: {
52675
- name: file2.name,
52676
- reason: "Platform does not support file downloads"
52677
- }
52678
- };
52679
- }
52680
- if (file2.size && file2.size > MAX_GZIP_SIZE) {
52681
- return {
52682
- skipped: {
52683
- name: file2.name,
52684
- reason: `Gzip file exceeds ${Math.round(MAX_GZIP_SIZE / 1024 / 1024)}MB limit (${Math.round(file2.size / 1024 / 1024)}MB)`,
52685
- suggestion: "Try compressing a smaller file or splitting the content"
52686
- }
52687
- };
52688
- }
52689
- let compressedBuffer;
52690
- try {
52691
- compressedBuffer = await platform.downloadFile(file2.id);
52692
- } catch (err) {
52693
- const errorMessage = err instanceof Error ? err.message : String(err);
52694
- log2.error(`Failed to download gzip file ${file2.name}: ${errorMessage}`);
52695
- return {
52696
- skipped: {
52697
- name: file2.name,
52698
- reason: `Download failed: ${errorMessage}`,
52699
- suggestion: "Check if the file is still available and try again"
52700
- }
52701
- };
52702
- }
52703
- if (file2.size && compressedBuffer.length !== file2.size) {
52704
- log2.warn(`Downloaded size mismatch for ${file2.name}: expected ${file2.size}, got ${compressedBuffer.length}`);
52705
- }
52706
- let decompressedBuffer;
52707
- try {
52708
- decompressedBuffer = await decompressGzipStream(compressedBuffer);
52709
- } catch (err) {
52710
- const errorMessage = err instanceof Error ? err.message : String(err);
52711
- let reason;
52712
- let suggestion;
52713
- if (errorMessage.includes("incorrect header check")) {
52714
- reason = "Invalid gzip file: the file header is corrupted or this is not a gzip file";
52715
- suggestion = "Verify the file is a valid gzip archive";
52716
- } else if (errorMessage.includes("unexpected end of file")) {
52717
- reason = "Incomplete gzip file: the file appears to be truncated";
52718
- suggestion = "Re-download the file or check if the upload completed";
52719
- } else if (errorMessage.includes("invalid stored block lengths")) {
52720
- reason = "Corrupted gzip file: the compressed data is damaged";
52721
- suggestion = "Try re-compressing the original file";
52722
- } else if (errorMessage.includes("exceeds") && errorMessage.includes("limit")) {
52723
- reason = errorMessage;
52724
- suggestion = "Try extracting only the relevant portions of the file";
52725
- } else {
52726
- reason = `Decompression failed: ${errorMessage}`;
52727
- suggestion = "Verify the file is a valid gzip archive";
52728
- }
52729
- return {
52730
- skipped: {
52731
- name: file2.name,
52732
- reason,
52733
- suggestion
52734
- }
52735
- };
52736
- }
52737
- const innerFilename = file2.name.toLowerCase().endsWith(".gz") ? file2.name.slice(0, -3) : file2.name;
52738
- const contentType = detectDecompressedContentType(decompressedBuffer, innerFilename);
52739
- if (debug) {
52740
- log2.debug(`Decompressed ${file2.name}: ${Math.round(decompressedBuffer.length / 1024)}KB, detected type: ${contentType}`);
52741
- }
52742
- if (contentType === "pdf") {
52743
- const base644 = decompressedBuffer.toString("base64");
52744
- return {
52745
- block: {
52746
- type: "document",
52747
- source: {
52748
- type: "base64",
52749
- media_type: "application/pdf",
52750
- data: base644
52751
- },
52752
- title: innerFilename
52753
- }
52754
- };
52755
- } else if (contentType === "text") {
52756
- const content = decompressedBuffer.toString("utf-8");
52757
- const wrappedContent = formatTextFileContent(innerFilename, content);
52758
- return {
52759
- block: {
52760
- type: "text",
52761
- text: wrappedContent
52762
- }
52763
- };
52764
- } else {
52765
- return {
52766
- skipped: {
52767
- name: file2.name,
52768
- reason: "Decompressed content type not supported",
52769
- suggestion: "Only text-based files and PDFs are supported after decompression"
52770
- }
52771
- };
52772
- }
52773
- } catch (err) {
52774
- const errorMessage = err instanceof Error ? err.message : String(err);
52775
- log2.error(`Failed to process gzip file ${file2.name}: ${errorMessage}`);
52776
- return {
52777
- skipped: {
52778
- name: file2.name,
52779
- reason: `Processing failed: ${errorMessage}`,
52780
- suggestion: "An unexpected error occurred. Please try again or contact support if the issue persists"
52781
- }
52782
- };
52783
- }
52784
- }
52785
- async function extractZipEntry(zipfile, entry) {
52786
- return new Promise((resolve2, reject) => {
52787
- zipfile.openReadStream(entry, (err, readStream) => {
52788
- if (err) {
52789
- reject(err);
52790
- return;
52791
- }
52792
- if (!readStream) {
52793
- reject(new Error("No read stream"));
52794
- return;
52795
- }
52796
- const chunks = [];
52797
- readStream.on("data", (chunk) => chunks.push(chunk));
52798
- readStream.on("end", () => resolve2(Buffer.concat(chunks)));
52799
- readStream.on("error", reject);
52800
- });
52801
- });
52802
- }
52803
- async function processZipFile(file2, platform, debug = false) {
52804
- const blocks = [];
52805
- const skipped = [];
52806
- try {
52807
- if (!platform.downloadFile) {
52808
- return {
52809
- blocks: [],
52810
- skipped: [{
52811
- name: file2.name,
52812
- reason: "Platform does not support file downloads"
52813
- }]
52814
- };
52815
- }
52816
- if (file2.size && file2.size > MAX_ZIP_SIZE) {
52817
- return {
52818
- blocks: [],
52819
- skipped: [{
52820
- name: file2.name,
52821
- reason: `Zip file exceeds ${Math.round(MAX_ZIP_SIZE / 1024 / 1024)}MB limit (${Math.round(file2.size / 1024 / 1024)}MB)`
52822
- }]
52823
- };
52824
- }
52825
- const zipBuffer = await platform.downloadFile(file2.id);
52826
- if (debug) {
52827
- log2.debug(`Processing zip file ${file2.name}: ${Math.round(zipBuffer.length / 1024)}KB`);
52828
- }
52829
- const zipfile = await new Promise((resolve2, reject) => {
52830
- import_yauzl.default.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zf) => {
52831
- if (err)
52832
- reject(err);
52833
- else if (!zf)
52834
- reject(new Error("Failed to open zip file"));
52835
- else
52836
- resolve2(zf);
52837
- });
52838
- });
52839
- const entries = [];
52840
- await new Promise((resolve2, reject) => {
52841
- zipfile.on("entry", (entry) => {
52842
- if (!entry.fileName.endsWith("/")) {
52843
- entries.push(entry);
52844
- }
52845
- zipfile.readEntry();
52846
- });
52847
- zipfile.on("end", resolve2);
52848
- zipfile.on("error", reject);
52849
- zipfile.readEntry();
52850
- });
52851
- if (entries.length > MAX_ZIP_FILES) {
52852
- zipfile.close();
52853
- return {
52854
- blocks: [],
52855
- skipped: [{
52856
- name: file2.name,
52857
- reason: `Zip contains too many files (${entries.length}). Maximum is ${MAX_ZIP_FILES} files.`,
52858
- suggestion: "Extract and upload the most relevant files individually"
52859
- }]
52860
- };
52861
- }
52862
- if (entries.length === 0) {
52863
- zipfile.close();
52864
- return {
52865
- blocks: [],
52866
- skipped: [{
52867
- name: file2.name,
52868
- reason: "Zip archive is empty"
52869
- }]
52870
- };
52871
- }
52872
- const zipfile2 = await new Promise((resolve2, reject) => {
52873
- import_yauzl.default.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zf) => {
52874
- if (err)
52875
- reject(err);
52876
- else if (!zf)
52877
- reject(new Error("Failed to open zip file"));
52878
- else
52879
- resolve2(zf);
52880
- });
52881
- });
52882
- let processedCount = 0;
52883
- await new Promise((resolve2, reject) => {
52884
- zipfile2.on("entry", async (entry) => {
52885
- try {
52886
- if (entry.fileName.endsWith("/")) {
52887
- zipfile2.readEntry();
52888
- return;
52889
- }
52890
- if (entry.uncompressedSize > MAX_DECOMPRESSED_SIZE) {
52891
- skipped.push({
52892
- name: entry.fileName,
52893
- reason: `File exceeds ${Math.round(MAX_DECOMPRESSED_SIZE / 1024 / 1024)}MB decompressed size limit`
52894
- });
52895
- zipfile2.readEntry();
52896
- return;
52897
- }
52898
- const buffer = await extractZipEntry(zipfile2, entry);
52899
- const contentType = detectDecompressedContentType(buffer, entry.fileName);
52900
- if (debug) {
52901
- log2.debug(`Extracted ${entry.fileName}: ${Math.round(buffer.length / 1024)}KB, type: ${contentType}`);
52902
- }
52903
- if (contentType === "pdf") {
52904
- const base644 = buffer.toString("base64");
52905
- blocks.push({
52906
- type: "document",
52907
- source: {
52908
- type: "base64",
52909
- media_type: "application/pdf",
52910
- data: base644
52911
- }
52912
- });
52913
- processedCount++;
52914
- } else if (contentType === "text") {
52915
- const content = buffer.toString("utf-8");
52916
- const wrappedContent = formatTextFileContent(entry.fileName, content);
52917
- blocks.push({
52918
- type: "text",
52919
- text: wrappedContent
52920
- });
52921
- processedCount++;
52922
- } else {
52923
- skipped.push({
52924
- name: entry.fileName,
52925
- reason: "Unsupported file type inside zip",
52926
- suggestion: "Only text-based files and PDFs are supported"
52927
- });
52928
- }
52929
- zipfile2.readEntry();
52930
- } catch (err) {
52931
- skipped.push({
52932
- name: entry.fileName,
52933
- reason: `Failed to extract: ${err instanceof Error ? err.message : String(err)}`
52934
- });
52935
- zipfile2.readEntry();
52936
- }
52937
- });
52938
- zipfile2.on("end", resolve2);
52939
- zipfile2.on("error", reject);
52940
- zipfile2.readEntry();
52941
- });
52942
- zipfile2.close();
52943
- if (debug) {
52944
- log2.debug(`Zip ${file2.name}: processed ${processedCount} files, skipped ${skipped.length}`);
52945
- }
52946
- return { blocks, skipped };
52947
- } catch (err) {
52948
- log2.error(`Failed to process zip file ${file2.name}: ${err}`);
52949
- return {
52950
- blocks: [],
52951
- skipped: [{
52952
- name: file2.name,
52953
- reason: `Failed to process zip: ${err instanceof Error ? err.message : String(err)}`
52954
- }]
52955
- };
52956
- }
52957
- }
52958
- function detectDecompressedContentType(buffer, filename) {
52959
- if (buffer.length >= 5 && buffer.toString("ascii", 0, 5) === "%PDF-") {
52960
- return "pdf";
52961
- }
52962
- const lowerFilename = filename.toLowerCase();
52963
- if (lowerFilename.endsWith(".pdf")) {
52964
- return "pdf";
52965
- }
52966
- if (TEXT_FILE_EXTENSIONS.some((ext) => lowerFilename.endsWith(ext))) {
52967
- return "text";
52968
- }
52969
- if (buffer.length > 0) {
52970
- const firstChar = String.fromCharCode(buffer[0]);
52971
- if (firstChar === "{" || firstChar === "[") {
52972
- return "text";
52973
- }
52974
- }
52975
- try {
52976
- const text = buffer.toString("utf-8");
52977
- const printableRatio = countPrintableChars(text) / text.length;
52978
- if (printableRatio > 0.9) {
52979
- return "text";
52980
- }
52981
- } catch {}
52982
- return "unknown";
52416
+ async function postSkippedFilesFeedback(platform, threadId, skipped) {
52417
+ if (skipped.length === 0)
52418
+ return;
52419
+ await platform.createPost(formatSkippedFilesFeedback(skipped), threadId);
52983
52420
  }
52984
- function countPrintableChars(text) {
52985
- let count = 0;
52986
- for (let i = 0;i < text.length; i++) {
52987
- const code = text.charCodeAt(i);
52988
- if (code >= 32 && code <= 126 || code === 9 || code === 10 || code === 13) {
52989
- count++;
52421
+ function formatSkippedFilesFeedback(skippedFiles) {
52422
+ const lines = ["⚠️ **Some files could not be processed:**"];
52423
+ for (const file2 of skippedFiles) {
52424
+ let line = `- **${file2.name}**: ${file2.reason}`;
52425
+ if (file2.suggestion) {
52426
+ line += ` _(${file2.suggestion})_`;
52990
52427
  }
52428
+ lines.push(line);
52991
52429
  }
52992
- return count;
52993
- }
52994
- function getUnsupportedFileSuggestion(file2) {
52995
- const ext = file2.name.toLowerCase().split(".").pop();
52996
- const mime = file2.mimeType.toLowerCase();
52997
- if (ext === "doc" || ext === "docx" || mime.includes("msword") || mime.includes("wordprocessingml")) {
52998
- return "Convert to PDF for best results";
52999
- }
53000
- if (ext === "xls" || ext === "xlsx" || mime.includes("spreadsheet")) {
53001
- return "Export as CSV for text-based analysis";
53002
- }
53003
- if (ext === "ppt" || ext === "pptx" || mime.includes("presentation")) {
53004
- return "Convert to PDF for best results";
53005
- }
53006
- if (ext === "tar" || ext === "rar" || ext === "7z") {
53007
- return "Extract files and upload them individually, or use .zip format";
53008
- }
53009
- if (ext === "exe" || ext === "dll" || ext === "so" || ext === "dylib") {
53010
- return "Binary files are not supported";
53011
- }
53012
- return;
53013
- }
53014
- async function processFiles(platform, files, debug = false) {
53015
- const blocks = [];
53016
- const skipped = [];
53017
- if (!files || files.length === 0) {
53018
- return { blocks, skipped };
53019
- }
53020
- for (const file2 of files) {
53021
- const category = categorizeFile(file2);
53022
- if (category === "zip") {
53023
- const zipResult = await processZipFile(file2, platform, debug);
53024
- blocks.push(...zipResult.blocks);
53025
- for (const s of zipResult.skipped) {
53026
- skipped.push(s);
53027
- log2.warn(`Skipped file ${s.name}: ${s.reason}`);
53028
- }
53029
- continue;
53030
- }
53031
- let result;
53032
- switch (category) {
53033
- case "image":
53034
- result = await processImageFile(file2, platform, debug);
53035
- break;
53036
- case "pdf":
53037
- result = await processPdfFile(file2, platform, debug);
53038
- break;
53039
- case "text":
53040
- result = await processTextFile(file2, platform, debug);
53041
- break;
53042
- case "gzip":
53043
- result = await processGzipFile(file2, platform, debug);
53044
- break;
53045
- case "unsupported":
53046
- default:
53047
- result = {
53048
- skipped: {
53049
- name: file2.name,
53050
- reason: `Unsupported file type: ${file2.mimeType}`,
53051
- suggestion: getUnsupportedFileSuggestion(file2)
53052
- }
53053
- };
53054
- break;
53055
- }
53056
- if (result.block) {
53057
- blocks.push(result.block);
53058
- }
53059
- if (result.skipped) {
53060
- skipped.push(result.skipped);
53061
- log2.warn(`Skipped file ${result.skipped.name}: ${result.skipped.reason}`);
53062
- }
53063
- }
53064
- return { blocks, skipped };
52430
+ return lines.join(`
52431
+ `);
53065
52432
  }
53066
52433
 
53067
52434
  // src/operations/message-manager.ts
@@ -53495,20 +52862,15 @@ class MessageManager {
53495
52862
  }
53496
52863
  this.session.threadLogger?.logUserMessage(username || this.session.startedBy, message, displayName, files && files.length > 0);
53497
52864
  await this.prepareForUserMessage();
53498
- let skippedFiles = [];
53499
- if (files && files.length > 0) {
53500
- const fileResult = await processFiles(this.platform, files);
53501
- skippedFiles = fileResult.skipped;
53502
- }
53503
52865
  let content = message;
52866
+ let skippedFiles = [];
53504
52867
  if (this.buildMessageContentCallback) {
53505
- content = await this.buildMessageContentCallback(message, this.platform, files);
52868
+ const built = await this.buildMessageContentCallback(message, this.platform, files);
52869
+ content = built.content;
52870
+ skippedFiles = built.skipped;
53506
52871
  }
53507
52872
  this.session.claude.sendMessage(content);
53508
- if (skippedFiles.length > 0) {
53509
- const feedback = this.formatSkippedFilesFeedback(skippedFiles);
53510
- await this.platform.createPost(feedback, this.threadId);
53511
- }
52873
+ await postSkippedFilesFeedback(this.platform, this.threadId, skippedFiles);
53512
52874
  this.session.lastActivityAt = new Date;
53513
52875
  this.session.isProcessing = true;
53514
52876
  this.emitSessionUpdateCallback?.({ status: "active", isTyping: true });
@@ -53516,18 +52878,6 @@ class MessageManager {
53516
52878
  logger.debug("User message sent to Claude");
53517
52879
  return true;
53518
52880
  }
53519
- formatSkippedFilesFeedback(skippedFiles) {
53520
- const lines = ["⚠️ **Some files could not be processed:**"];
53521
- for (const file2 of skippedFiles) {
53522
- let line = `- **${file2.name}**: ${file2.reason}`;
53523
- if (file2.suggestion) {
53524
- line += ` _(${file2.suggestion})_`;
53525
- }
53526
- lines.push(line);
53527
- }
53528
- return lines.join(`
53529
- `);
53530
- }
53531
52881
  getSession() {
53532
52882
  return this.session;
53533
52883
  }
@@ -54150,6 +53500,64 @@ import { fileURLToPath as fileURLToPath3 } from "url";
54150
53500
  import { existsSync as existsSync4, readFileSync as readFileSync3, watchFile, unwatchFile, unlinkSync, statSync, readdirSync } from "fs";
54151
53501
  import { tmpdir } from "os";
54152
53502
  import { join as join3 } from "path";
53503
+
53504
+ // src/claude/rate-limit-detector.ts
53505
+ var RATE_LIMIT_PHRASES = [
53506
+ /usage limit reached/i,
53507
+ /rate[_\s-]?limit[_\s-]?error/i,
53508
+ /you have hit the rate limit/i,
53509
+ /quota (has been )?exceeded/i,
53510
+ /\b429\b.*(rate|limit|quota)/i
53511
+ ];
53512
+ var DEFAULT_COOLDOWN_MS = 60 * 60 * 1000;
53513
+ function detectRateLimit(text, now = Date.now()) {
53514
+ if (!text)
53515
+ return { detected: false };
53516
+ let matched;
53517
+ for (const phrase of RATE_LIMIT_PHRASES) {
53518
+ const m = text.match(phrase);
53519
+ if (m) {
53520
+ matched = m[0];
53521
+ break;
53522
+ }
53523
+ }
53524
+ if (!matched)
53525
+ return { detected: false };
53526
+ const resetAtEpochMs = extractResetAt(text, now);
53527
+ return { detected: true, matched, resetAtEpochMs };
53528
+ }
53529
+ function extractResetAt(text, now) {
53530
+ const relative = text.match(/(?:retry[_\s-]?after|resets?\s+in)\s+(\d+)\s*(second|minute|hour|day)s?/i);
53531
+ if (relative) {
53532
+ const value = parseInt(relative[1], 10);
53533
+ const unit = relative[2].toLowerCase();
53534
+ const unitMs = {
53535
+ second: 1000,
53536
+ minute: 60000,
53537
+ hour: 3600000,
53538
+ day: 86400000
53539
+ };
53540
+ return now + value * unitMs[unit];
53541
+ }
53542
+ const unix = text.match(/["']?reset(?:_at)?["']?\s*[:=]\s*(\d{10,13})/);
53543
+ if (unix) {
53544
+ const raw = parseInt(unix[1], 10);
53545
+ return unix[1].length === 13 ? raw : raw * 1000;
53546
+ }
53547
+ const clock = text.match(/resets?\s+at\s+(\d{1,2}):(\d{2})\s*(utc|gmt)?/i);
53548
+ if (clock) {
53549
+ const hh = parseInt(clock[1], 10);
53550
+ const mm = parseInt(clock[2], 10);
53551
+ if (hh < 24 && mm < 60) {
53552
+ const reference = new Date(now);
53553
+ const target = new Date(Date.UTC(reference.getUTCFullYear(), reference.getUTCMonth(), reference.getUTCDate(), hh, mm)).getTime();
53554
+ return target > now ? target : target + 86400000;
53555
+ }
53556
+ }
53557
+ return;
53558
+ }
53559
+
53560
+ // src/claude/cli.ts
54153
53561
  var log7 = createLogger("claude");
54154
53562
  function cleanupBrowserBridgeSockets() {
54155
53563
  try {
@@ -54171,6 +53579,14 @@ function cleanupBrowserBridgeSockets() {
54171
53579
  log7.debug(`Browser bridge cleanup failed: ${err}`);
54172
53580
  }
54173
53581
  }
53582
+ function isErrorResultEvent(event) {
53583
+ const ev = event;
53584
+ if (typeof ev.subtype === "string" && ev.subtype.startsWith("error"))
53585
+ return true;
53586
+ if (ev.is_error === true)
53587
+ return true;
53588
+ return false;
53589
+ }
54174
53590
 
54175
53591
  class ClaudeCli extends EventEmitter2 {
54176
53592
  process = null;
@@ -54180,6 +53596,7 @@ class ClaudeCli extends EventEmitter2 {
54180
53596
  statusFilePath = null;
54181
53597
  lastStatusData = null;
54182
53598
  stderrBuffer = "";
53599
+ rateLimitEmitted = false;
54183
53600
  log;
54184
53601
  constructor(options2) {
54185
53602
  super();
@@ -54231,6 +53648,7 @@ class ClaudeCli extends EventEmitter2 {
54231
53648
  if (this.process)
54232
53649
  throw new Error("Already running");
54233
53650
  this.stderrBuffer = "";
53651
+ this.rateLimitEmitted = false;
54234
53652
  cleanupBrowserBridgeSockets();
54235
53653
  const claudePath = getClaudePath();
54236
53654
  const args = [
@@ -54300,9 +53718,13 @@ class ClaudeCli extends EventEmitter2 {
54300
53718
  args.push("--settings", JSON.stringify(statusLineSettings));
54301
53719
  }
54302
53720
  this.log.debug(`Starting: ${claudePath} ${args.slice(0, 5).join(" ")}...`);
53721
+ const childEnv = this.buildChildEnv();
53722
+ if (this.options.account) {
53723
+ this.log.debug(`Spawning under Claude account "${this.options.account.id}"`);
53724
+ }
54303
53725
  this.process = crossSpawn(claudePath, args, {
54304
53726
  cwd: this.options.workingDir,
54305
- env: process.env,
53727
+ env: childEnv,
54306
53728
  stdio: ["pipe", "pipe", "pipe"]
54307
53729
  });
54308
53730
  this.log.debug(`Claude process spawned: pid=${this.process.pid}`);
@@ -54316,6 +53738,7 @@ class ClaudeCli extends EventEmitter2 {
54316
53738
  this.stderrBuffer = this.stderrBuffer.slice(-10240);
54317
53739
  }
54318
53740
  this.log.debug(`stderr: ${text.trim()}`);
53741
+ this.maybeEmitRateLimit(text);
54319
53742
  });
54320
53743
  this.process.on("error", (err) => {
54321
53744
  this.log.error(`Claude error: ${err}`);
@@ -54370,9 +53793,22 @@ class ClaudeCli extends EventEmitter2 {
54370
53793
  try {
54371
53794
  const event = JSON.parse(trimmed);
54372
53795
  this.emit("event", event);
53796
+ if (event.type === "result" && isErrorResultEvent(event)) {
53797
+ this.maybeEmitRateLimit(trimmed);
53798
+ }
54373
53799
  } catch {}
54374
53800
  }
54375
53801
  }
53802
+ maybeEmitRateLimit(text) {
53803
+ const hit = detectRateLimit(text);
53804
+ if (!hit.detected)
53805
+ return;
53806
+ if (this.rateLimitEmitted)
53807
+ return;
53808
+ this.rateLimitEmitted = true;
53809
+ this.log.warn(`Rate limit detected: ${hit.matched ?? "(no match text)"}`);
53810
+ this.emit("rate-limit", hit);
53811
+ }
54376
53812
  isRunning() {
54377
53813
  return this.process !== null;
54378
53814
  }
@@ -54441,6 +53877,22 @@ class ClaudeCli extends EventEmitter2 {
54441
53877
  this.process.kill("SIGINT");
54442
53878
  return true;
54443
53879
  }
53880
+ buildChildEnv() {
53881
+ const account = this.options.account;
53882
+ if (!account)
53883
+ return process.env;
53884
+ const env = { ...process.env };
53885
+ if (account.home) {
53886
+ env.HOME = account.home;
53887
+ env.USERPROFILE = account.home;
53888
+ delete env.ANTHROPIC_API_KEY;
53889
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
53890
+ } else if (account.apiKey) {
53891
+ env.ANTHROPIC_API_KEY = account.apiKey;
53892
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
53893
+ }
53894
+ return env;
53895
+ }
54444
53896
  getMcpServerPath() {
54445
53897
  const __filename2 = fileURLToPath3(import.meta.url);
54446
53898
  const __dirname4 = dirname4(__filename2);
@@ -54461,42 +53913,6 @@ class ClaudeCli extends EventEmitter2 {
54461
53913
  }
54462
53914
  }
54463
53915
 
54464
- // src/update-notifier.ts
54465
- var import_semver2 = __toESM(require_semver2(), 1);
54466
-
54467
- // src/operations/commands/handler.ts
54468
- init_emoji();
54469
-
54470
- // src/utils/error-handler/index.ts
54471
- var log8 = createLogger("error");
54472
-
54473
- // src/utils/session-log.ts
54474
- function createSessionLog(baseLog) {
54475
- return (session) => {
54476
- if (session?.sessionId) {
54477
- return baseLog.forSession(session.sessionId);
54478
- }
54479
- return baseLog;
54480
- };
54481
- }
54482
-
54483
- // src/operations/post-helpers/index.ts
54484
- init_emoji();
54485
-
54486
- // src/git/worktree.ts
54487
- import * as path from "path";
54488
- import { homedir as homedir2 } from "os";
54489
- var log9 = createLogger("git-wt");
54490
- var WORKTREES_DIR = path.join(homedir2(), ".claude-threads", "worktrees");
54491
- var METADATA_STORE_PATH = path.join(homedir2(), ".claude-threads", "worktree-metadata.json");
54492
-
54493
- // src/operations/post-helpers/index.ts
54494
- var log10 = createLogger("helpers");
54495
- var sessionLog = createSessionLog(log10);
54496
-
54497
- // src/claude/quick-query.ts
54498
- var log11 = createLogger("query");
54499
-
54500
53916
  // src/commands/registry.ts
54501
53917
  var COMMAND_REGISTRY = [
54502
53918
  {
@@ -55049,6 +54465,36 @@ ${avoidCommands.map((c) => `- \`!${c.command}\` - ${c.reason}`).join(`
55049
54465
  `)}
55050
54466
  `.trim();
55051
54467
  }
54468
+ // src/utils/error-handler/index.ts
54469
+ var log8 = createLogger("error");
54470
+
54471
+ // src/utils/session-log.ts
54472
+ function createSessionLog(baseLog) {
54473
+ return (session) => {
54474
+ if (session?.sessionId) {
54475
+ return baseLog.forSession(session.sessionId);
54476
+ }
54477
+ return baseLog;
54478
+ };
54479
+ }
54480
+
54481
+ // src/operations/post-helpers/index.ts
54482
+ init_emoji();
54483
+
54484
+ // src/git/worktree.ts
54485
+ import * as path from "path";
54486
+ import { homedir as homedir2 } from "os";
54487
+ var log9 = createLogger("git-wt");
54488
+ var WORKTREES_DIR = path.join(homedir2(), ".claude-threads", "worktrees");
54489
+ var METADATA_STORE_PATH = path.join(homedir2(), ".claude-threads", "worktree-metadata.json");
54490
+
54491
+ // src/operations/post-helpers/index.ts
54492
+ var log10 = createLogger("helpers");
54493
+ var sessionLog = createSessionLog(log10);
54494
+
54495
+ // src/claude/quick-query.ts
54496
+ var log11 = createLogger("query");
54497
+
55052
54498
  // src/operations/suggestions/title.ts
55053
54499
  var log12 = createLogger("title");
55054
54500
 
@@ -55066,7 +54512,11 @@ var log15 = createLogger("lifecycle");
55066
54512
  var sessionLog3 = createSessionLog(log15);
55067
54513
  var CHAT_PLATFORM_PROMPT = generateChatPlatformPrompt();
55068
54514
 
54515
+ // src/update-notifier.ts
54516
+ var import_semver2 = __toESM(require_semver2(), 1);
54517
+
55069
54518
  // src/operations/commands/handler.ts
54519
+ init_emoji();
55070
54520
  var log16 = createLogger("commands");
55071
54521
  var sessionLog4 = createSessionLog(log16);
55072
54522
  // src/operations/suggestions/branch.ts