@superatomai/sdk-node 0.0.3-s → 0.0.4-dsp

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/index.d.ts CHANGED
@@ -1406,6 +1406,10 @@ interface ExecutedToolInfo {
1406
1406
  _recordsShown: number;
1407
1407
  _metadata?: any;
1408
1408
  _sampleData: any[];
1409
+ /** Bounded summary over the FULL fetched result (complete structure). */
1410
+ _summary?: any;
1411
+ /** Up to MAIN_AGENT_COMPLETE_ROWS rows — the complete result when small. */
1412
+ _mainAgentRows?: any[];
1409
1413
  };
1410
1414
  outputSchema?: any;
1411
1415
  sourceSchema?: string;
@@ -1619,8 +1623,16 @@ interface AgentWrittenScript {
1619
1623
  * Controls limits, models, and behavior.
1620
1624
  */
1621
1625
  interface AgentConfig {
1622
- /** Max rows a source agent can return (default: 50) */
1626
+ /** Max rows shown to the UI preview / inlined per source (default: 10) */
1623
1627
  maxRowsPerSource: number;
1628
+ /**
1629
+ * Max rows a source query may FETCH from the DB server-side (default: 2000).
1630
+ * Decoupled from what the main agent is shown: the full result is fetched and
1631
+ * summarized (bounded), but only a small/complete slice enters LLM context.
1632
+ * This lets small lookups (benchmark maps) arrive COMPLETE without letting
1633
+ * large results blow up context.
1634
+ */
1635
+ maxRowsFetched: number;
1624
1636
  /** Model for the main agent (routing + analysis in one LLM call) */
1625
1637
  mainAgentModel: string;
1626
1638
  /** Model for source agent query generation */
@@ -1674,8 +1686,18 @@ interface ScriptRecipe {
1674
1686
  tables: string[];
1675
1687
  /** Parameter definitions — what can vary */
1676
1688
  parameters: ScriptParameter[];
1677
- /** The script function body as a string */
1689
+ /** The script function body as a string. Loaded from disk (scripts-store/<fileBase>.ts). */
1678
1690
  scriptBody: string;
1691
+ /**
1692
+ * On-disk filename stem for the body: scripts-store/<fileBase>.ts.
1693
+ * Editable in the IDE. Decided at authoring time (slug of `name`, with a
1694
+ * short id suffix on collision) and stable across promotion.
1695
+ */
1696
+ fileBase?: string;
1697
+ /** sha256 of the on-disk body — lets the runtime detect manual edits. */
1698
+ bodyHash?: string;
1699
+ /** Project scope (single-VM deployments may leave this undefined). */
1700
+ projectId?: string;
1679
1701
  /** Times this script was used successfully */
1680
1702
  successCount: number;
1681
1703
  /** Times this script failed */
@@ -1701,10 +1723,19 @@ interface ScriptRecipe {
1701
1723
  * (sourced from the matcher's `modificationHint`).
1702
1724
  */
1703
1725
  forkReason?: string;
1726
+ /**
1727
+ * Validated component specs captured at authoring time. On a tier-high
1728
+ * replay these are rebound to fresh queryIds deterministically — no
1729
+ * component-generation LLM call, and the rendered columns can't drift from
1730
+ * what was validated when the script was authored. Absent on recipes
1731
+ * authored before this landed; those fall back to LLM component generation.
1732
+ * See backend/docs/SCRIPT-COMPONENT-CONSISTENCY.md.
1733
+ */
1734
+ components?: ScriptComponentSpec[];
1704
1735
  /**
1705
1736
  * Lifecycle stage of this recipe on disk.
1706
1737
  * - 'draft': written by MainAgent's write_script during a turn; filtered out
1707
- * of `ScriptStore.getAll()` so the matcher never picks it.
1738
+ * of FTS results (status='verified' only) so the matcher never picks it.
1708
1739
  * Filename is suffixed with `turnId` to keep concurrent turns
1709
1740
  * from clobbering each other's drafts.
1710
1741
  * - 'verified': promoted after `execute_script` succeeded; the matcher sees it.
@@ -1746,25 +1777,227 @@ interface ScriptParameter {
1746
1777
  /** Human-readable description (used in the matcher LLM prompt) */
1747
1778
  description: string;
1748
1779
  }
1780
+ /**
1781
+ * A reusable component binding captured when a script is authored. Stored on
1782
+ * the recipe so tier-high replays rebuild components deterministically (rebind
1783
+ * to fresh queryIds) instead of re-running the component-picker LLM.
1784
+ */
1785
+ interface ScriptComponentSpec {
1786
+ /** Registered component name (e.g. "DynamicBarChart") — matched against the available component library. */
1787
+ componentType: string;
1788
+ /** `executedQuery.sourceId` to bind to (e.g. a tool id or 'computed:_final'), 'federation' for a cross-source component, or 'markdown' for a content-only narrative block (no data source). */
1789
+ sourceRef: string;
1790
+ /** Present only when sourceRef === 'federation' — the DuckDB SQL to re-execute on replay. */
1791
+ federationSql?: string;
1792
+ /** Present only when sourceRef === 'markdown' — the narrative text to render on replay (markdown has no data source, so its content must be persisted). */
1793
+ content?: string;
1794
+ title?: string;
1795
+ description?: string;
1796
+ /** Validated axis/value keys + aggregation — all referencing real columns of the bound source. */
1797
+ config: Record<string, any>;
1798
+ }
1799
+ /**
1800
+ * Result from executing a script via ScriptRunner.
1801
+ */
1802
+ interface ScriptResult {
1803
+ /** Whether the script executed successfully */
1804
+ success: boolean;
1805
+ /** Combined data from all queries */
1806
+ data: any[];
1807
+ /** Individual query results tracked during execution */
1808
+ executedQueries: ScriptQueryResult[];
1809
+ /** Error message if failed */
1810
+ error?: string;
1811
+ /**
1812
+ * Where in the lifecycle the error occurred. Lets MainAgent's fix-loop
1813
+ * decide between "rewrite the whole draft" (compile) and "patch the
1814
+ * specific line" (runtime).
1815
+ */
1816
+ errorPhase?: 'compile' | 'runtime' | 'timeout' | 'ipc';
1817
+ /** Total execution time in milliseconds */
1818
+ executionTimeMs: number;
1819
+ }
1820
+ /**
1821
+ * A single query executed during script runtime.
1822
+ * Tracked by ScriptContext for component generation and debugging.
1823
+ */
1824
+ interface ScriptQueryResult {
1825
+ /** Source tool ID */
1826
+ sourceId: string;
1827
+ /** Human-readable source name */
1828
+ sourceName: string;
1829
+ /** The SQL that was executed */
1830
+ sql: string;
1831
+ /** Result data rows */
1832
+ data: any[];
1833
+ /** Number of rows returned */
1834
+ count: number;
1835
+ /** Total rows that matched before limit (if available) */
1836
+ totalCount?: number;
1837
+ /** Query execution time in milliseconds */
1838
+ executionTimeMs: number;
1839
+ /**
1840
+ * True for rows that did NOT come from a real SQL execution — either a
1841
+ * ctx.emit() dataset or the synthesized "computed:_final" entry that
1842
+ * carries the script's post-JS returned data. The component generator
1843
+ * uses this to route the resulting component through the script_dataset
1844
+ * sentinel toolId so the frontend resolves it via the queryCache short-circuit.
1845
+ */
1846
+ virtual?: boolean;
1847
+ }
1848
+ /**
1849
+ * Match tier returned by the LLM script matcher.
1850
+ *
1851
+ * - 'high': the script answers the question directly; only parameter values
1852
+ * may differ. The runtime replays it with extracted params (cheapest path).
1853
+ * - 'near': the script answers a STRUCTURALLY similar question but needs
1854
+ * body modification (different metric, dimension, table, filter shape).
1855
+ * The runtime forks the parent and adapts the body via MainAgent's normal
1856
+ * write_script + execute_script loop — no SourceAgent dispatch needed.
1857
+ * See backend/docs/SCRIPT-FLOW-FORK-ADAPT.md for the full design.
1858
+ * - 'none': no script is relevant; full agent flow runs.
1859
+ */
1860
+ type MatchTier = 'high' | 'near' | 'none';
1861
+ /**
1862
+ * Result from the LLM-based script matcher.
1863
+ *
1864
+ * For `tier: 'high'`, `extractedParams` carries the values to pass to the
1865
+ * existing script. For `tier: 'near'`, `gaps` and `modificationHint` describe
1866
+ * what the fork-author needs to change in the parent body.
1867
+ */
1868
+ interface ScriptMatch {
1869
+ /** The matched script recipe */
1870
+ recipe: ScriptRecipe;
1871
+ /** Match tier — see MatchTier docs */
1872
+ tier: MatchTier;
1873
+ /** Similarity score (0-1, derived from LLM tier) */
1874
+ similarity: number;
1875
+ /**
1876
+ * Legacy confidence level. Mirrors `tier === 'high'`/`'near'` for now;
1877
+ * kept so existing callers compile while we migrate to tier-based logic.
1878
+ */
1879
+ confidence: 'high' | 'medium';
1880
+ /** Parameters extracted from the user question by the LLM (tier='high') */
1881
+ extractedParams?: Record<string, any>;
1882
+ /** What the user question needs that the parent doesn't cover (tier='near') */
1883
+ gaps?: string[];
1884
+ /** One-sentence description of the change the fork-author should make (tier='near') */
1885
+ modificationHint?: string;
1886
+ /** Why the matcher made this choice (for logs and telemetry) */
1887
+ reasoning?: string;
1888
+ }
1749
1889
 
1750
1890
  /**
1751
- * ScriptStorePersistent Storage for Script Recipes
1891
+ * ScriptRecipeStoreinjected metadata backend for the script flow.
1752
1892
  *
1753
- * Layout:
1754
- * scripts-store/
1755
- * metadata/<name>.json ← recipe metadata (params, tables, counts, ...)
1756
- * <name>.ts ← the getData() function body, editable in your IDE
1893
+ * The SDK is standalone (no DB dependency). The backend implements this
1894
+ * interface over Postgres (full-text search + atomic counters) and injects it
1895
+ * via `collections['script-recipes']`, exactly like `collections['source-embeddings']`.
1896
+ * `ScriptStore` consumes it for all METADATA operations while keeping the
1897
+ * executable body on disk as scripts-store/<fileBase>.ts.
1757
1898
  *
1758
- * Each VM deployment is a single project, so no project ID prefix needed.
1759
- * Legacy single-file format (JSON with embedded scriptBody) is still loadable
1760
- * for backwards compatibility and auto-migrated to the split format on next save.
1899
+ * All metadata rows are plain JSON (no scriptBody that lives on disk).
1900
+ * See backend/docs/SCRIPT-FLOW-SCALING-ISSUES.md (#1, #3, #7).
1761
1901
  */
1762
1902
 
1903
+ /** One recipe's metadata as stored in Postgres (mirrors the script_recipes table). */
1904
+ interface ScriptRecipeMetaRow {
1905
+ id: string;
1906
+ projectId?: string | null;
1907
+ version: number;
1908
+ name: string;
1909
+ intentDescription: string;
1910
+ tags: string[] | null;
1911
+ createdFrom: string | null;
1912
+ sourceIds: string[] | null;
1913
+ tables: string[] | null;
1914
+ parameters: ScriptParameter[] | null;
1915
+ components?: ScriptComponentSpec[] | null;
1916
+ fileBase: string;
1917
+ bodyHash?: string | null;
1918
+ successCount: number;
1919
+ failureCount: number;
1920
+ lastUsed: string | null;
1921
+ parentId?: string | null;
1922
+ forkDepth?: number | null;
1923
+ forkReason?: string | null;
1924
+ status: 'draft' | 'verified' | string;
1925
+ turnId?: string | null;
1926
+ lastError?: {
1927
+ phase: 'compile' | 'runtime' | 'timeout' | 'ipc';
1928
+ message: string;
1929
+ at: string;
1930
+ attempt: number;
1931
+ } | null;
1932
+ createdAt?: string | null;
1933
+ updatedAt?: string | null;
1934
+ }
1935
+ interface ScriptRecipeStore {
1936
+ /** FTS shortlist of healthy verified recipes for the matcher (metadata only). */
1937
+ search(params: {
1938
+ prompt: string;
1939
+ projectId?: string;
1940
+ limit?: number;
1941
+ }): Promise<ScriptRecipeMetaRow[]>;
1942
+ /** Fetch one recipe by id (any status). */
1943
+ getById(id: string): Promise<ScriptRecipeMetaRow | null>;
1944
+ /** Count healthy verified recipes (drives the "any scripts?" gate). */
1945
+ count(params?: {
1946
+ projectId?: string;
1947
+ }): Promise<number>;
1948
+ /** Insert or update a recipe row (keyed by id). */
1949
+ upsert(row: ScriptRecipeMetaRow): Promise<void>;
1950
+ /** Atomically bump counters / last-used. */
1951
+ updateStats(id: string, patch: {
1952
+ successDelta?: number;
1953
+ failureDelta?: number;
1954
+ lastUsed?: string;
1955
+ }): Promise<void>;
1956
+ /** Flip a draft to verified, applying provenance + optional fork lineage. */
1957
+ promote(id: string, patch: {
1958
+ sourceIds: string[];
1959
+ tables: string[];
1960
+ fileBase?: string;
1961
+ parentId?: string;
1962
+ forkDepth?: number;
1963
+ forkReason?: string;
1964
+ components?: ScriptComponentSpec[];
1965
+ }): Promise<ScriptRecipeMetaRow | null>;
1966
+ /** Stamp a draft's last execution error. */
1967
+ recordDraftError(id: string, err: {
1968
+ phase: string;
1969
+ message: string;
1970
+ attempt: number;
1971
+ at: string;
1972
+ }): Promise<void>;
1973
+ /** Delete a recipe row (body file removed separately). */
1974
+ remove(id: string): Promise<void>;
1975
+ /** True if `fileBase` is taken by a different recipe in this project. */
1976
+ fileBaseTaken(fileBase: string, excludeId: string, projectId?: string): Promise<boolean>;
1977
+ }
1978
+ /** Pull the injected store off the collections bag (or null if not wired). */
1979
+ declare function resolveScriptRecipeStore(collections: any): ScriptRecipeStore | null;
1980
+
1763
1981
  /**
1764
- * Input for `ScriptStore.saveDraft()`. Identifies the draft by `recipeId` so
1765
- * retries within the same turn overwrite the same files (or omit `recipeId`
1766
- * on the first call to mint a fresh draft).
1982
+ * ScriptStore Postgres metadata + on-disk body for script recipes.
1983
+ *
1984
+ * Split of responsibilities:
1985
+ * - METADATA → injected `ScriptRecipeStore` (Postgres FTS + atomic counters),
1986
+ * resolved from `collections['script-recipes']`.
1987
+ * - BODY → scripts-store/<fileBase>.ts, editable in your IDE. Written
1988
+ * atomically (temp + rename); `bodyHash` (sha256) detects edits.
1989
+ *
1990
+ * The old "read every file every turn + send the whole catalog to the LLM"
1991
+ * matcher is gone — matching is `store.search(prompt)` (FTS shortlist). The
1992
+ * draft/verified filename dance is gone too: `status` is a DB column and the
1993
+ * file keeps a stable `<fileBase>.ts` name across promotion.
1994
+ *
1995
+ * When no metadata store is injected, the store degrades to a safe no-op
1996
+ * (count 0 → script flow disabled) instead of crashing.
1997
+ *
1998
+ * See backend/docs/SCRIPT-FLOW-SCALING-ISSUES.md.
1767
1999
  */
2000
+
1768
2001
  interface SaveDraftInput {
1769
2002
  /** Reuse an existing draft (retry); omit to mint a new one. */
1770
2003
  recipeId?: string;
@@ -1777,140 +2010,92 @@ interface SaveDraftInput {
1777
2010
  scriptBody: string;
1778
2011
  createdFrom: string;
1779
2012
  }
1780
- /**
1781
- * Lineage + provenance fields applied when a draft is promoted to verified.
1782
- * MainAgent gathers `sourceIds` and `tables` from the verified execution; the
1783
- * caller (agent-user-response.ts) supplies the optional fork lineage.
1784
- */
1785
2013
  interface PromoteToVerifiedInput {
1786
2014
  sourceIds: string[];
1787
2015
  tables: string[];
1788
2016
  parentId?: string;
1789
2017
  forkDepth?: number;
1790
2018
  forkReason?: string;
2019
+ components?: ScriptComponentSpec[];
1791
2020
  }
2021
+ interface ScriptStoreOptions {
2022
+ /** Explicit metadata store, or resolved from `collections['script-recipes']`. */
2023
+ store?: ScriptRecipeStore | null;
2024
+ collections?: any;
2025
+ /** Body directory (defaults to <cwd>/scripts-store). */
2026
+ baseDir?: string;
2027
+ /** Project scope stamped on every row. */
2028
+ projectId?: string;
2029
+ }
2030
+ /**
2031
+ * Normalize a scriptBody into the on-disk form (strip a leading comment block,
2032
+ * ensure `export async function getData`). Exported for MainAgent.
2033
+ */
2034
+ declare function normalizeScriptBody(scriptBody: string): string;
1792
2035
  declare class ScriptStore {
1793
- private recipes;
2036
+ private store;
1794
2037
  private storeDir;
1795
- private loaded;
1796
- constructor(baseDir?: string);
1797
- private metadataDir;
1798
- /**
1799
- * Filename base for a recipe. Drafts include the per-turn suffix so two
1800
- * concurrent turns can never clobber each other's draft files; verified
1801
- * recipes use the bare slug.
1802
- */
1803
- private fileBaseName;
1804
- /**
1805
- * Absolute path to the .ts file for a recipe. Callers (e.g. ScriptRunner,
1806
- * MainAgent's execute_script) use this to hand off the path to the tsx child.
1807
- */
1808
- getScriptPath(recipe: ScriptRecipe): string;
1809
- /**
1810
- * Absolute path to the metadata JSON for a recipe.
1811
- */
1812
- private getMetadataPath;
1813
- /**
1814
- * Get all VERIFIED recipes drafts are filtered out so the matcher never
1815
- * considers an unverified script. Loads from disk on first access.
1816
- */
1817
- getAll(): ScriptRecipe[];
1818
- /**
1819
- * Get a recipe by ID — returns drafts as well as verified scripts.
1820
- * Used by MainAgent and the promotion path.
1821
- */
1822
- get(id: string): ScriptRecipe | null;
1823
- /**
1824
- * Number of verified recipes (matches `getAll().length`).
1825
- */
1826
- count(): number;
1827
- /**
1828
- * Save a recipe (create or update).
1829
- * File is named after the script: "order-status-distribution.json"
1830
- */
1831
- save(recipe: ScriptRecipe): void;
1832
- /**
1833
- * Persist (or update) a draft recipe to disk. Always writes immediately so
1834
- * the `.ts` body is visible in the IDE the moment MainAgent calls
1835
- * `write_script`. Within one turn, retries that pass the same `recipeId`
1836
- * overwrite the same files (the LLM rewriting itself); a fresh `recipeId`
1837
- * is minted only on the first call of the turn.
1838
- *
1839
- * Filename includes the `turnId` suffix so concurrent turns never collide.
1840
- */
1841
- saveDraft(input: SaveDraftInput): ScriptRecipe;
1842
- /**
1843
- * Stamp the draft with the most recent execution failure so it is visible
1844
- * in the metadata JSON without grepping logs. No-op if the recipe doesn't
1845
- * exist or has already been promoted.
1846
- */
2038
+ private projectId?;
2039
+ constructor(opts?: ScriptStoreOptions);
2040
+ /** Whether a metadata store is wired (matcher / authoring are gated on this). */
2041
+ hasStore(): boolean;
2042
+ /** Number of healthy verified recipes (gates the script-matching path). */
2043
+ count(): Promise<number>;
2044
+ /**
2045
+ * FTS shortlist for the matcher (metadata only — bodies are loaded lazily by
2046
+ * `get()` once the LLM picks one). Returns verified, healthy recipes ranked
2047
+ * by relevance.
2048
+ */
2049
+ search(prompt: string, limit?: number): Promise<ScriptRecipe[]>;
2050
+ /** Fetch one recipe by id with its body loaded from disk. */
2051
+ get(id: string): Promise<ScriptRecipe | null>;
2052
+ /** Create or update a recipe (metadata upsert + body write when changed). */
2053
+ save(recipe: ScriptRecipe): Promise<void>;
2054
+ /**
2055
+ * Persist (or update) a draft. Within a turn, retries that pass the same
2056
+ * `recipeId` overwrite the same row + file; a fresh `recipeId` mints a new
2057
+ * draft. The body is visible at scripts-store/<fileBase>.ts immediately.
2058
+ */
2059
+ saveDraft(input: SaveDraftInput): Promise<ScriptRecipe>;
2060
+ /** Stamp a draft's last execution error (metadata only). */
1847
2061
  recordDraftError(recipeId: string, err: {
1848
2062
  phase: 'compile' | 'runtime' | 'timeout' | 'ipc';
1849
2063
  message: string;
1850
2064
  attempt: number;
1851
- }): void;
2065
+ }): Promise<void>;
1852
2066
  /**
1853
2067
  * Promote a successfully-executed draft into a verified script.
1854
- *
1855
- * - Renames the on-disk files from `<slug>-<turnId>.{ts,json}` to the bare
1856
- * `<slug>.{ts,json}`. If a verified file with the same slug already exists
1857
- * (concurrent turn won the race, or a prior session has it), the recipe
1858
- * keeps its turn-suffixed filename so we never overwrite verified work —
1859
- * the matcher keys on `recipe.id`, so two siblings with similar slugs is fine.
1860
- * - Flips `status: 'verified'`, clears `lastError`, applies provenance
1861
- * (`sourceIds`, `tables`) and optional fork lineage.
1862
- */
1863
- promoteToVerified(recipeId: string, input: PromoteToVerifiedInput): ScriptRecipe | null;
1864
- /**
1865
- * Drop a draft from disk + memory (e.g. when an outer error path wants to
1866
- * clean up). Per the agreed policy, MainAgent's normal failure path does
1867
- * NOT call this failed drafts are kept on disk for the user to inspect.
1868
- */
1869
- discardDraft(recipeId: string): void;
1870
- /**
1871
- * Delete a recipe by ID.
1872
- */
1873
- delete(id: string): void;
1874
- /**
1875
- * Record a successful execution for a recipe.
1876
- */
1877
- recordSuccess(id: string): void;
1878
- /**
1879
- * Record a failed execution for a recipe.
1880
- */
1881
- recordFailure(id: string): void;
1882
- /**
1883
- * Get the failure rate for a recipe (0 to 1).
1884
- * Returns 0 if the recipe has fewer than 5 total uses.
1885
- */
1886
- getFailureRate(id: string): number;
1887
- private ensureLoaded;
1888
- /**
1889
- * Convert a script name to a safe filename.
1890
- * "Order Status Distribution" → "order-status-distribution"
1891
- */
1892
- private toFileName;
1893
- /**
1894
- * Load all script files from disk.
1895
- *
1896
- * Supports two layouts:
1897
- * - New (preferred): metadata/<name>.json + <name>.ts
1898
- * - Legacy: <name>.json with embedded scriptBody (auto-migrated on next save)
1899
- */
1900
- private loadAllFromDisk;
1901
- /**
1902
- * Save a recipe to disk in split format:
1903
- * metadata/<name>.json (metadata, no scriptBody)
1904
- * <name>.ts (just the function body)
1905
- *
1906
- * If a legacy top-level <name>.json exists for the same recipe, remove it
1907
- * so we don't end up with duplicate sources of truth.
1908
- */
1909
- private saveRecipeToDisk;
1910
- /**
1911
- * Delete a recipe's files from disk (both metadata + body, plus any legacy file).
1912
- */
1913
- private deleteRecipeFromDisk;
2068
+ * The on-disk body already exists at <fileBase>.ts (written at write_script
2069
+ * time) and keeps its name — only the DB row flips status + provenance.
2070
+ */
2071
+ promoteToVerified(recipeId: string, input: PromoteToVerifiedInput): Promise<ScriptRecipe | null>;
2072
+ /**
2073
+ * Drop a draft (row + body file). MainAgent calls this at end-of-turn when a
2074
+ * draft was authored but never verified failed drafts are never matched, so
2075
+ * deleting them immediately avoids unbounded accumulation (#5). No-op if the
2076
+ * recipe isn't a draft (so a promoted/verified script is never removed here).
2077
+ */
2078
+ discardDraft(recipeId: string): Promise<void>;
2079
+ /** Delete a recipe (row + body file). */
2080
+ delete(id: string): Promise<void>;
2081
+ /** Record a successful execution (atomic counter bump). */
2082
+ recordSuccess(id: string): Promise<void>;
2083
+ /** Record a failed execution (atomic counter bump). */
2084
+ recordFailure(id: string): Promise<void>;
2085
+ /** Absolute path to the .ts body for a recipe (used by the runner/MainAgent). */
2086
+ getScriptPath(recipe: ScriptRecipe): string;
2087
+ private removeById;
2088
+ private rowToRecipe;
2089
+ private recipeToRow;
2090
+ /** slug of name, with a short id suffix when the bare slug is already taken. */
2091
+ private computeFileBase;
2092
+ private toSlug;
2093
+ private hash;
2094
+ private bodyPath;
2095
+ private readBody;
2096
+ /** Atomic body write (temp + rename) so concurrent reads never see a partial file. */
2097
+ private writeBody;
2098
+ private unlinkBody;
1914
2099
  }
1915
2100
 
1916
2101
  /**
@@ -2063,6 +2248,16 @@ declare class LLM {
2063
2248
  * @returns Normalized system prompt for Anthropic API
2064
2249
  */
2065
2250
  private static _normalizeSystemPrompt;
2251
+ /**
2252
+ * Strip unpaired UTF-16 surrogates from every text field of a message set.
2253
+ *
2254
+ * A lone surrogate (from mid-pair string slicing or corrupt source data)
2255
+ * serializes to a bare `\udXXX` escape that strict JSON parsers — including
2256
+ * the one on Anthropic's API — reject with "no low surrogate in string",
2257
+ * failing the whole request. Sanitizing here, at the single boundary every
2258
+ * provider call flows through, guarantees no request can carry one.
2259
+ */
2260
+ private static _sanitizeMessages;
2066
2261
  /**
2067
2262
  * Log cache usage metrics from Anthropic API response
2068
2263
  * Shows cache hits, costs, and savings
@@ -3224,6 +3419,65 @@ declare class DashboardConversationHistory {
3224
3419
  }
3225
3420
  declare const dashboardConversationHistory: DashboardConversationHistory;
3226
3421
 
3422
+ /**
3423
+ * ScriptMatcher — LLM-Based Script Matching + Parameter Extraction
3424
+ *
3425
+ * Uses ONE LLM call to:
3426
+ * 1. Pick the best matching script from the library (or "none")
3427
+ * 2. Extract parameter values from the user question
3428
+ *
3429
+ * Why LLM over embeddings:
3430
+ * - Embeddings capture topic similarity ("overstock" ≈ "inventory" ≈ "revenue")
3431
+ * but can't distinguish structurally different questions about the same domain
3432
+ * - LLM understands that "overstock by warehouse" needs a different script than
3433
+ * "revenue by warehouse" even though they're semantically close
3434
+ * - One call does both matching AND parameter extraction
3435
+ *
3436
+ * When script library grows past ~50, add an embedding pre-filter
3437
+ * (ChromaDB narrows to top 10 → LLM picks from those 10).
3438
+ */
3439
+
3440
+ declare class ScriptMatcher {
3441
+ private store;
3442
+ constructor(store: ScriptStore);
3443
+ /**
3444
+ * Find the best matching script for a user question.
3445
+ * Uses ONE LLM call that picks the script AND extracts parameters.
3446
+ * Returns null if no script matches.
3447
+ */
3448
+ match(userPrompt: string, apiKey?: string, model?: string): Promise<ScriptMatch | null>;
3449
+ /**
3450
+ * Build the script catalog string for the LLM prompt.
3451
+ * Each script gets: index, ID, name, description, and parameter definitions.
3452
+ */
3453
+ private buildScriptCatalog;
3454
+ }
3455
+
3456
+ /**
3457
+ * ScriptRunner — Execute scripts in an isolated tsx subprocess.
3458
+ *
3459
+ * The subprocess approach replaces the earlier `new Function()` eval and gives us:
3460
+ * - Real sandbox (separate process, SIGKILL on timeout).
3461
+ * - Real TypeScript (tsx transpiles on the fly).
3462
+ * - npm imports available to scripts (clustering, stats, geo, etc.).
3463
+ *
3464
+ * Protocol: NDJSON over the child's stdin/stdout. See script-ipc.ts + backend/docs/SCRIPT-FLOW-IMPLEMENTATION.md.
3465
+ */
3466
+
3467
+ interface RunScriptOptions {
3468
+ /** Data sources the script is allowed to query via ctx.query */
3469
+ externalTools: ExternalTool[];
3470
+ /** Optional — for propagating per-query UI progress to the user */
3471
+ streamBuffer?: StreamBuffer;
3472
+ /** Override the wall-clock timeout (default `SCRIPT_TIMEOUT_MS`, 60s). */
3473
+ timeoutMs?: number;
3474
+ }
3475
+ /**
3476
+ * Execute a recipe by spawning a tsx child on the script's .ts file.
3477
+ * `scriptPath` is the absolute path to the saved `.ts` body.
3478
+ */
3479
+ declare function runScript(recipe: ScriptRecipe, scriptPath: string, params: Record<string, any>, options: RunScriptOptions): Promise<ScriptResult>;
3480
+
3227
3481
  type MessageTypeHandler = (message: IncomingMessage) => void | Promise<void>;
3228
3482
  declare class SuperatomSDK {
3229
3483
  private ws;
@@ -3393,4 +3647,4 @@ declare class SuperatomSDK {
3393
3647
  getConversationSimilarityThreshold(): number;
3394
3648
  }
3395
3649
 
3396
- export { type Action, type AgentConfig, type AgentResponse, BM25L, type BM25LOptions, type BaseLLMConfig, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CollectionHandler, type CollectionOperation, type DBUIBlock, DEFAULT_AGENT_CONFIG, type DatabaseType, type HybridSearchOptions, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LLMUsageEntry, type LogLevel, MainAgent, type Message, type ModelStrategy, type OutputField, type RerankedResult, STORAGE_CONFIG, type SelectedWorkflow, SuperatomSDK, type SuperatomSDKConfig, type TaskType, Thread, ThreadManager, type Tool$1 as Tool, type ToolOutputSchema, UIBlock, UILogCollector, type User, UserManager, type UsersData, type WorkflowDescriptor, anthropicLLM, dashboardConversationHistory, geminiLLM, groqLLM, hybridRerank, llmUsageLogger, logger, openaiLLM, queryCache, rerankChromaResults, rerankConversationResults, userPromptErrorLogger };
3650
+ export { type Action, type AgentConfig, type AgentResponse, BM25L, type BM25LOptions, type BaseLLMConfig, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CollectionHandler, type CollectionOperation, type DBUIBlock, DEFAULT_AGENT_CONFIG, type DatabaseType, type HybridSearchOptions, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LLMUsageEntry, type LogLevel, MainAgent, type Message, type ModelStrategy, type OutputField, type RerankedResult, STORAGE_CONFIG, type ScriptComponentSpec, ScriptMatcher, type ScriptParameter, type ScriptRecipe, type ScriptRecipeMetaRow, type ScriptRecipeStore, type ScriptResult, ScriptStore, type ScriptStoreOptions, type SelectedWorkflow, SuperatomSDK, type SuperatomSDKConfig, type TaskType, Thread, ThreadManager, type Tool$1 as Tool, type ToolOutputSchema, UIBlock, UILogCollector, type User, UserManager, type UsersData, type WorkflowDescriptor, anthropicLLM, dashboardConversationHistory, geminiLLM, groqLLM, hybridRerank, llmUsageLogger, logger, normalizeScriptBody, openaiLLM, queryCache, rerankChromaResults, rerankConversationResults, resolveScriptRecipeStore, runScript, userPromptErrorLogger };