@usabledev/usable-chat 1.165.1 → 1.167.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.
Files changed (2) hide show
  1. package/cli.js +52 -5
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -90993,7 +90993,7 @@ var init_data_staging_prompt = __esm({
90993
90993
  "use strict";
90994
90994
  DATA_STAGING_PROMPT = `# Staged Data \u2014 read THIS first
90995
90995
 
90996
- The user can drop CSV / TSV / Parquet / XLSX / XLS / JSON / NDJSON / \`.duckdb\` files, paste a URL, or attach a local browser folder. Files stage into a browser-side DuckDB; bytes never leave the user's browser. You query them via the \`data_*\` tools and only see structured JSON results.
90996
+ The user can drop CSV / TSV / Parquet / XLSX / XLS / JSON / NDJSON / \`.duckdb\` files, paste URL, or attach a browser folder. Files stage into browser DuckDB. \`RAW\` entries have no rows/cols; host uses \`chat.data.readBytes(alias)\`.
90997
90997
 
90998
90998
  In the actual tool list these usually appear with a \`parent_\` prefix, e.g. \`parent_data_list\`, \`parent_data_load_browser_file\`, \`parent_data_describe\`, \`parent_data_query\`. If present, call the exact \`parent_data_*\` tool; shorter \`data_*\` names below are canonical.
90999
90999
 
@@ -91001,7 +91001,7 @@ In the actual tool list these usually appear with a \`parent_\` prefix, e.g. \`p
91001
91001
 
91002
91002
  Whenever the user types any of these (or anything similar): **"analyze this"**, **"what's in the file"**, **"the data"**, **"the file"**, **"this CSV / spreadsheet / parquet / dataset"**, **"summarise this"**, **"chart this"**, **"plot this"**, **"what does the data look like"** \u2014 **ALWAYS** call \`data_list\` BEFORE you reply.
91003
91003
 
91004
- - If \`data_list\` returns at least one file, **THAT IS WHAT THE USER MEANS**. Proceed with \`data_describe\` \u2192 tools.
91004
+ - If \`data_list\` returns a structured file, **THAT IS WHAT THE USER MEANS**. Proceed with \`data_describe\` \u2192 tools. If only \`RAW\` matches, explain that the host app parses it.
91005
91005
  - **NEVER** reply *"please upload a file"*, *"share the file with me"*, *"I don't have access to the file"*, or any variation when \`data_list\` returns \u2265 1 entry. The file is already staged. Just analyse it.
91006
91006
  - **NEVER** ask the user to paste rows. They already gave you the file; query it through the tools.
91007
91007
  - If the user names a file in the attached local folder and it is NOT in \`data_list\`, call \`parent_data_load_browser_file({ path })\` / \`data_load_browser_file({ path })\` first, then list + describe.
@@ -91432,6 +91432,20 @@ var init_parent_tools = __esm({
91432
91432
  required: ["alias"]
91433
91433
  }
91434
91434
  },
91435
+ {
91436
+ name: "data_get_bytes",
91437
+ description: "Return a staged file's raw browser bytes as base64 so the embed host can parse host-specific formats (for example GPX, KML, KMZ) without Usable Chat shipping per-format parsers. Pass a parent alias from data_list; child table aliases are rejected.",
91438
+ parameters: {
91439
+ type: "object",
91440
+ properties: {
91441
+ alias: {
91442
+ type: "string",
91443
+ description: "Alias from data_list. For .duckdb files, pass the parent alias."
91444
+ }
91445
+ },
91446
+ required: ["alias"]
91447
+ }
91448
+ },
91435
91449
  {
91436
91450
  name: "data_load_remote",
91437
91451
  description: "Fetch a CSV/TSV/Parquet/JSON/NDJSON/XLSX/.duckdb file from an HTTPS URL and stage it as if the user had uploaded it. By DEFAULT the request routes through a same-origin proxy (/api/data-proxy with RFC 7233 Range support) \u2014 this is required because the chat's CSP `connect-src` is locked to a small allowlist, so direct cross-origin fetches will fail. Only pass `useProxy: false` when the URL is on a CSP-allowlisted host (rare). Format is auto-detected from the URL extension; pass `format` explicitly when the URL has no extension (e.g. share-link APIs that return a UUID). For `.duckdb` files, multiple sub-aliases are returned \u2014 one per attached table.",
@@ -285711,6 +285725,16 @@ var init_file_storage_service = __esm({
285711
285725
  });
285712
285726
  this.totalCacheBytes += size2;
285713
285727
  }
285728
+ clearFileCache(fileId, userId) {
285729
+ for (const ensureCompressed of [false, true]) {
285730
+ const key = this.makeCacheKey(fileId, userId, ensureCompressed);
285731
+ const cached2 = this.cache.get(key);
285732
+ if (cached2) {
285733
+ this.cache.delete(key);
285734
+ this.totalCacheBytes -= cached2.size;
285735
+ }
285736
+ }
285737
+ }
285714
285738
  /**
285715
285739
  * Estimate tokens from content (rough estimation: 4 chars/token for text, size-based for images)
285716
285740
  */
@@ -285724,12 +285748,13 @@ var init_file_storage_service = __esm({
285724
285748
  /**
285725
285749
  * Store a file's extracted content with rotating storage
285726
285750
  * Automatically removes oldest files if user exceeds limit
285727
- * For images: stores both original (full quality) and compressed (for LLM) versions
285751
+ * For images: stores both original (full quality) and compressed (for LLM) versions.
285752
+ * For documents: keeps extractedContent as text and optional originalContent as base64 bytes.
285728
285753
  */
285729
285754
  async storeFile(params) {
285730
285755
  const fileId = randomUUID4();
285731
285756
  let extractedContent = params.extractedContent;
285732
- let originalContent = null;
285757
+ let originalContent = params.originalContent ?? null;
285733
285758
  if (params.fileType === "image" && params.contentType === "base64") {
285734
285759
  originalContent = params.extractedContent;
285735
285760
  try {
@@ -285885,6 +285910,28 @@ var init_file_storage_service = __esm({
285885
285910
  ).orderBy(desc(fileStorage.createdAt)).limit(1);
285886
285911
  return existing || null;
285887
285912
  }
285913
+ /**
285914
+ * Backfill original uploaded bytes onto a legacy duplicate row.
285915
+ * This preserves the rotating-storage dedupe behaviour while making older
285916
+ * text-only rows usable by file_to_sandbox after a user re-uploads the file.
285917
+ */
285918
+ async ensureOriginalContent(file2, userId, originalContent) {
285919
+ if (!originalContent || file2.originalContent) {
285920
+ return file2;
285921
+ }
285922
+ const [updatedFile] = await db.update(fileStorage).set({
285923
+ originalContent,
285924
+ lastUsedAt: /* @__PURE__ */ new Date()
285925
+ }).where(and(eq(fileStorage.id, file2.id), eq(fileStorage.userId, userId))).returning();
285926
+ this.clearFileCache(file2.id, userId);
285927
+ logger.info("FileStorage", "backfilled original bytes for duplicate upload", {
285928
+ fileId: file2.id,
285929
+ fileName: file2.fileName,
285930
+ fileType: file2.fileType,
285931
+ fileSize: file2.fileSize
285932
+ });
285933
+ return updatedFile || file2;
285934
+ }
285888
285935
  /**
285889
285936
  * Check if a file exists by ID
285890
285937
  */
@@ -308939,7 +308986,7 @@ init_tui_select();
308939
308986
  init_model_registry();
308940
308987
 
308941
308988
  // package.json
308942
- var version2 = "1.165.1";
308989
+ var version2 = "1.167.0";
308943
308990
 
308944
308991
  // src/adapters/cli/model-catalog.ts
308945
308992
  init_codex_auth();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usabledev/usable-chat",
3
- "version": "1.165.1",
3
+ "version": "1.167.0",
4
4
  "description": "usable-chat — terminal harness for usable-chat (headless + TUI)",
5
5
  "type": "module",
6
6
  "bin": {