@superatomai/sdk-node 0.0.61-dsp → 0.0.62-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.js CHANGED
@@ -3644,7 +3644,8 @@ var searchConversationsWithReranking = async (options) => {
3644
3644
  var findExactMatch = async ({
3645
3645
  userPrompt,
3646
3646
  collections,
3647
- userId
3647
+ userId,
3648
+ maxAgeHours
3648
3649
  }) => {
3649
3650
  try {
3650
3651
  if (!collections || !collections["conversation-history"] || !collections["conversation-history"]["exactMatch"]) {
@@ -3653,12 +3654,21 @@ var findExactMatch = async ({
3653
3654
  }
3654
3655
  const result = await collections["conversation-history"]["exactMatch"]({
3655
3656
  userPrompt,
3656
- userId
3657
+ userId,
3658
+ maxAgeHours
3657
3659
  });
3658
3660
  if (!result || !result.uiBlock) {
3659
3661
  logger.info("[ConversationSearch] No exact match found");
3660
3662
  return null;
3661
3663
  }
3664
+ const createdAt = result.metadata?.createdAt;
3665
+ if (maxAgeHours && maxAgeHours > 0 && createdAt) {
3666
+ const ageHours = (Date.now() - new Date(createdAt).getTime()) / 36e5;
3667
+ if (ageHours > maxAgeHours) {
3668
+ logger.info(`[ConversationSearch] Exact match is ${ageHours.toFixed(1)}h old (limit ${maxAgeHours}h) \u2014 not replaying`);
3669
+ return null;
3670
+ }
3671
+ }
3662
3672
  logger.info(`[ConversationSearch] \u2713 Exact prompt match found for "${userPrompt.substring(0, 50)}..."`);
3663
3673
  return result;
3664
3674
  } catch (error) {
@@ -18001,6 +18011,63 @@ function buildUserAccessContext(userConfig) {
18001
18011
  if (!anyRestriction && prompts.length === 0) return "";
18002
18012
  return [...restrictions, ...prompts].join("\n\n");
18003
18013
  }
18014
+ function replayMaxAgeHours() {
18015
+ const raw = process.env.SA_ANSWER_REPLAY_MAX_AGE_HOURS;
18016
+ const n = raw === void 0 || raw === "" ? 24 : Number(raw);
18017
+ return Number.isFinite(n) && n >= 0 ? n : 24;
18018
+ }
18019
+ async function replayExactMatch(opts) {
18020
+ const { prompt, collections, userId, streamCallback, method, startTime } = opts;
18021
+ let directReplay;
18022
+ const conversationMatch = await conversation_search_default.findExactMatch({
18023
+ userPrompt: prompt,
18024
+ collections,
18025
+ userId,
18026
+ maxAgeHours: replayMaxAgeHours()
18027
+ });
18028
+ if (conversationMatch) {
18029
+ const rawComponent = conversationMatch.uiBlock?.component || conversationMatch.uiBlock?.generatedComponentMetadata;
18030
+ const isValidComponent = rawComponent && typeof rawComponent === "object" && Object.keys(rawComponent).length > 0;
18031
+ const component = isValidComponent ? rawComponent : null;
18032
+ const cachedTextResponse = conversationMatch.uiBlock?.analysis || conversationMatch.uiBlock?.textResponse || conversationMatch.uiBlock?.text || "";
18033
+ if (component && hasExpiredScriptDataset(component)) {
18034
+ directReplay = extractBoundRecipe(component);
18035
+ logger.info(
18036
+ directReplay ? `[AgentFlow] Exact match w/ expired script_dataset \u2014 will re-run bound recipe ${directReplay.recipeId} directly (skip matcher LLM)` : `[AgentFlow] Exact match found but cached UIBlock has expired script_dataset \u2014 falling through to script matcher`
18037
+ );
18038
+ } else {
18039
+ if (streamCallback && cachedTextResponse) {
18040
+ streamCallback(cachedTextResponse);
18041
+ }
18042
+ const elapsedTime = Date.now() - startTime;
18043
+ logger.info(`[AgentFlow] Exact match \u2014 returning cached result (${elapsedTime}ms)`);
18044
+ const rehydratedComponent = component ? rehydrateCachedComponent(component) : null;
18045
+ const storedBinding = conversationMatch.uiBlock?.scriptBinding;
18046
+ const cachedBinding = storedBinding ? {
18047
+ recipeId: storedBinding.recipeId,
18048
+ params: storedBinding.params || {},
18049
+ name: storedBinding.name || "",
18050
+ columns: storedBinding.columns
18051
+ } : (() => {
18052
+ const bound = component ? extractBoundRecipe(component) : void 0;
18053
+ return bound ? { recipeId: bound.recipeId, params: bound.params, name: "" } : void 0;
18054
+ })();
18055
+ return { response: {
18056
+ success: true,
18057
+ data: {
18058
+ text: cachedTextResponse,
18059
+ component: rehydratedComponent,
18060
+ reasoning: `Exact match from previous conversation`,
18061
+ method,
18062
+ semanticSimilarity: 1,
18063
+ ...cachedBinding ? { scriptBinding: cachedBinding } : {}
18064
+ },
18065
+ errors: []
18066
+ } };
18067
+ }
18068
+ }
18069
+ return directReplay ? { directReplay } : null;
18070
+ }
18004
18071
  var get_agent_user_response = async (prompt, components, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, conversationHistory, streamCallback, collections, externalTools, userId, mainAgentModel, sourceAgentModel, workflows, signal, activeBindings, userConfig) => {
18005
18072
  const startTime = Date.now();
18006
18073
  const providers = llmProviders || ["anthropic"];
@@ -18013,53 +18080,9 @@ var get_agent_user_response = async (prompt, components, anthropicApiKey, groqAp
18013
18080
  logger.logLLMPrompt("agentUserResponse", "user", `User Prompt: ${prompt}`);
18014
18081
  logger.info(`[AgentFlow] Starting | provider: ${provider} | prompt: "${prompt.substring(0, 50)}..."`);
18015
18082
  try {
18016
- let directReplay;
18017
- const conversationMatch = await conversation_search_default.findExactMatch({
18018
- userPrompt: prompt,
18019
- collections,
18020
- userId
18021
- });
18022
- if (conversationMatch) {
18023
- const rawComponent = conversationMatch.uiBlock?.component || conversationMatch.uiBlock?.generatedComponentMetadata;
18024
- const isValidComponent = rawComponent && typeof rawComponent === "object" && Object.keys(rawComponent).length > 0;
18025
- const component = isValidComponent ? rawComponent : null;
18026
- const cachedTextResponse = conversationMatch.uiBlock?.analysis || conversationMatch.uiBlock?.textResponse || conversationMatch.uiBlock?.text || "";
18027
- if (component && hasExpiredScriptDataset(component)) {
18028
- directReplay = extractBoundRecipe(component);
18029
- logger.info(
18030
- directReplay ? `[AgentFlow] Exact match w/ expired script_dataset \u2014 will re-run bound recipe ${directReplay.recipeId} directly (skip matcher LLM)` : `[AgentFlow] Exact match found but cached UIBlock has expired script_dataset \u2014 falling through to script matcher`
18031
- );
18032
- } else {
18033
- if (streamCallback && cachedTextResponse) {
18034
- streamCallback(cachedTextResponse);
18035
- }
18036
- const elapsedTime2 = Date.now() - startTime;
18037
- logger.info(`[AgentFlow] Exact match \u2014 returning cached result (${elapsedTime2}ms)`);
18038
- const rehydratedComponent = component ? rehydrateCachedComponent(component) : null;
18039
- const storedBinding = conversationMatch.uiBlock?.scriptBinding;
18040
- const cachedBinding = storedBinding ? {
18041
- recipeId: storedBinding.recipeId,
18042
- params: storedBinding.params || {},
18043
- name: storedBinding.name || "",
18044
- columns: storedBinding.columns
18045
- } : (() => {
18046
- const bound = component ? extractBoundRecipe(component) : void 0;
18047
- return bound ? { recipeId: bound.recipeId, params: bound.params, name: "" } : void 0;
18048
- })();
18049
- return {
18050
- success: true,
18051
- data: {
18052
- text: cachedTextResponse,
18053
- component: rehydratedComponent,
18054
- reasoning: `Exact match from previous conversation`,
18055
- method: `${provider}-agent-exact-match`,
18056
- semanticSimilarity: 1,
18057
- ...cachedBinding ? { scriptBinding: cachedBinding } : {}
18058
- },
18059
- errors: []
18060
- };
18061
- }
18062
- }
18083
+ const cached = await replayExactMatch({ prompt, collections, userId, streamCallback, method: `${provider}-agent-exact-match`, startTime });
18084
+ if (cached?.response) return cached.response;
18085
+ const directReplay = cached?.directReplay;
18063
18086
  logger.info(`[AgentFlow] No exact match \u2014 proceeding with agent`);
18064
18087
  runTrace = createRunTrace();
18065
18088
  const apiKey = (() => {
@@ -19497,7 +19520,8 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
19497
19520
  }
19498
19521
  let userResponse;
19499
19522
  try {
19500
- userResponse = answerProvider ? await answerProvider({
19523
+ const replayed = answerProvider ? (await replayExactMatch({ prompt, collections, userId, streamCallback, method: "custom-exact-match", startTime: Date.now() }))?.response : void 0;
19524
+ userResponse = replayed ? replayed : answerProvider ? await answerProvider({
19501
19525
  prompt,
19502
19526
  components,
19503
19527
  collections,
@@ -24829,7 +24853,8 @@ fetch(BRIDGE + '/query', {
24829
24853
  // request context; absent, the data seam treats the query as anonymous.
24830
24854
  ...(CONTEXT_TOKEN ? { 'x-sa-context': CONTEXT_TOKEN } : {}),
24831
24855
  },
24832
- body: JSON.stringify({ toolId, sql, limit: Math.min(Number(limitArg) || MAX_ROWS, MAX_ROWS) }),
24856
+ // ref: ask for a handle the deliverable can cite instead of copying rows.
24857
+ body: JSON.stringify({ toolId, sql, limit: Math.min(Number(limitArg) || MAX_ROWS, MAX_ROWS), ref: true }),
24833
24858
  })
24834
24859
  .then(async (r) => {
24835
24860
  const text = await r.text();
@@ -24838,11 +24863,21 @@ fetch(BRIDGE + '/query', {
24838
24863
  if (p && p.error) { console.error(String(p.error)); process.exit(1); }
24839
24864
  const rows = (p && (p.data || p.rows)) || p;
24840
24865
  const all = Array.isArray(rows) ? rows : [rows];
24866
+ // The source truncates before we see the rows and rewrites count to the
24867
+ // truncated length; only metadata keeps the real total.
24868
+ const meta = (p && p.metadata) || {};
24869
+ const shown = all.slice(0, MAX_ROWS);
24870
+ const total = meta.totalCount != null ? meta.totalCount : ((p && p.count) != null ? p.count : all.length);
24871
+ const truncated = !!meta.truncated || all.length > MAX_ROWS || total > shown.length;
24841
24872
  // Capped so one careless SELECT * cannot flood the agent's context.
24842
24873
  console.log(JSON.stringify({
24843
- count: (p && p.count) != null ? p.count : all.length,
24844
- truncated: all.length > MAX_ROWS,
24845
- rows: all.slice(0, MAX_ROWS),
24874
+ ...(p && p.ref ? { ref: p.ref } : {}),
24875
+ count: total,
24876
+ returned: shown.length,
24877
+ truncated,
24878
+ ...(truncated ? { warning: 'INCOMPLETE: only the first ' + shown.length + ' of ' + total + ' rows are shown. Do not compute totals, counts, shares or rankings from these rows; aggregate in SQL instead.' } : {}),
24879
+ ...(meta.appliedPolicies && meta.appliedPolicies.length ? { restrictedBy: meta.appliedPolicies } : {}),
24880
+ rows: shown,
24846
24881
  }, null, 2));
24847
24882
  })
24848
24883
  .catch((e) => { console.error('Could not reach the data bridge: ' + (e && e.message)); process.exit(1); });
package/dist/index.mjs CHANGED
@@ -3538,7 +3538,8 @@ var searchConversationsWithReranking = async (options) => {
3538
3538
  var findExactMatch = async ({
3539
3539
  userPrompt,
3540
3540
  collections,
3541
- userId
3541
+ userId,
3542
+ maxAgeHours
3542
3543
  }) => {
3543
3544
  try {
3544
3545
  if (!collections || !collections["conversation-history"] || !collections["conversation-history"]["exactMatch"]) {
@@ -3547,12 +3548,21 @@ var findExactMatch = async ({
3547
3548
  }
3548
3549
  const result = await collections["conversation-history"]["exactMatch"]({
3549
3550
  userPrompt,
3550
- userId
3551
+ userId,
3552
+ maxAgeHours
3551
3553
  });
3552
3554
  if (!result || !result.uiBlock) {
3553
3555
  logger.info("[ConversationSearch] No exact match found");
3554
3556
  return null;
3555
3557
  }
3558
+ const createdAt = result.metadata?.createdAt;
3559
+ if (maxAgeHours && maxAgeHours > 0 && createdAt) {
3560
+ const ageHours = (Date.now() - new Date(createdAt).getTime()) / 36e5;
3561
+ if (ageHours > maxAgeHours) {
3562
+ logger.info(`[ConversationSearch] Exact match is ${ageHours.toFixed(1)}h old (limit ${maxAgeHours}h) \u2014 not replaying`);
3563
+ return null;
3564
+ }
3565
+ }
3556
3566
  logger.info(`[ConversationSearch] \u2713 Exact prompt match found for "${userPrompt.substring(0, 50)}..."`);
3557
3567
  return result;
3558
3568
  } catch (error) {
@@ -17895,6 +17905,63 @@ function buildUserAccessContext(userConfig) {
17895
17905
  if (!anyRestriction && prompts.length === 0) return "";
17896
17906
  return [...restrictions, ...prompts].join("\n\n");
17897
17907
  }
17908
+ function replayMaxAgeHours() {
17909
+ const raw = process.env.SA_ANSWER_REPLAY_MAX_AGE_HOURS;
17910
+ const n = raw === void 0 || raw === "" ? 24 : Number(raw);
17911
+ return Number.isFinite(n) && n >= 0 ? n : 24;
17912
+ }
17913
+ async function replayExactMatch(opts) {
17914
+ const { prompt, collections, userId, streamCallback, method, startTime } = opts;
17915
+ let directReplay;
17916
+ const conversationMatch = await conversation_search_default.findExactMatch({
17917
+ userPrompt: prompt,
17918
+ collections,
17919
+ userId,
17920
+ maxAgeHours: replayMaxAgeHours()
17921
+ });
17922
+ if (conversationMatch) {
17923
+ const rawComponent = conversationMatch.uiBlock?.component || conversationMatch.uiBlock?.generatedComponentMetadata;
17924
+ const isValidComponent = rawComponent && typeof rawComponent === "object" && Object.keys(rawComponent).length > 0;
17925
+ const component = isValidComponent ? rawComponent : null;
17926
+ const cachedTextResponse = conversationMatch.uiBlock?.analysis || conversationMatch.uiBlock?.textResponse || conversationMatch.uiBlock?.text || "";
17927
+ if (component && hasExpiredScriptDataset(component)) {
17928
+ directReplay = extractBoundRecipe(component);
17929
+ logger.info(
17930
+ directReplay ? `[AgentFlow] Exact match w/ expired script_dataset \u2014 will re-run bound recipe ${directReplay.recipeId} directly (skip matcher LLM)` : `[AgentFlow] Exact match found but cached UIBlock has expired script_dataset \u2014 falling through to script matcher`
17931
+ );
17932
+ } else {
17933
+ if (streamCallback && cachedTextResponse) {
17934
+ streamCallback(cachedTextResponse);
17935
+ }
17936
+ const elapsedTime = Date.now() - startTime;
17937
+ logger.info(`[AgentFlow] Exact match \u2014 returning cached result (${elapsedTime}ms)`);
17938
+ const rehydratedComponent = component ? rehydrateCachedComponent(component) : null;
17939
+ const storedBinding = conversationMatch.uiBlock?.scriptBinding;
17940
+ const cachedBinding = storedBinding ? {
17941
+ recipeId: storedBinding.recipeId,
17942
+ params: storedBinding.params || {},
17943
+ name: storedBinding.name || "",
17944
+ columns: storedBinding.columns
17945
+ } : (() => {
17946
+ const bound = component ? extractBoundRecipe(component) : void 0;
17947
+ return bound ? { recipeId: bound.recipeId, params: bound.params, name: "" } : void 0;
17948
+ })();
17949
+ return { response: {
17950
+ success: true,
17951
+ data: {
17952
+ text: cachedTextResponse,
17953
+ component: rehydratedComponent,
17954
+ reasoning: `Exact match from previous conversation`,
17955
+ method,
17956
+ semanticSimilarity: 1,
17957
+ ...cachedBinding ? { scriptBinding: cachedBinding } : {}
17958
+ },
17959
+ errors: []
17960
+ } };
17961
+ }
17962
+ }
17963
+ return directReplay ? { directReplay } : null;
17964
+ }
17898
17965
  var get_agent_user_response = async (prompt, components, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, conversationHistory, streamCallback, collections, externalTools, userId, mainAgentModel, sourceAgentModel, workflows, signal, activeBindings, userConfig) => {
17899
17966
  const startTime = Date.now();
17900
17967
  const providers = llmProviders || ["anthropic"];
@@ -17907,53 +17974,9 @@ var get_agent_user_response = async (prompt, components, anthropicApiKey, groqAp
17907
17974
  logger.logLLMPrompt("agentUserResponse", "user", `User Prompt: ${prompt}`);
17908
17975
  logger.info(`[AgentFlow] Starting | provider: ${provider} | prompt: "${prompt.substring(0, 50)}..."`);
17909
17976
  try {
17910
- let directReplay;
17911
- const conversationMatch = await conversation_search_default.findExactMatch({
17912
- userPrompt: prompt,
17913
- collections,
17914
- userId
17915
- });
17916
- if (conversationMatch) {
17917
- const rawComponent = conversationMatch.uiBlock?.component || conversationMatch.uiBlock?.generatedComponentMetadata;
17918
- const isValidComponent = rawComponent && typeof rawComponent === "object" && Object.keys(rawComponent).length > 0;
17919
- const component = isValidComponent ? rawComponent : null;
17920
- const cachedTextResponse = conversationMatch.uiBlock?.analysis || conversationMatch.uiBlock?.textResponse || conversationMatch.uiBlock?.text || "";
17921
- if (component && hasExpiredScriptDataset(component)) {
17922
- directReplay = extractBoundRecipe(component);
17923
- logger.info(
17924
- directReplay ? `[AgentFlow] Exact match w/ expired script_dataset \u2014 will re-run bound recipe ${directReplay.recipeId} directly (skip matcher LLM)` : `[AgentFlow] Exact match found but cached UIBlock has expired script_dataset \u2014 falling through to script matcher`
17925
- );
17926
- } else {
17927
- if (streamCallback && cachedTextResponse) {
17928
- streamCallback(cachedTextResponse);
17929
- }
17930
- const elapsedTime2 = Date.now() - startTime;
17931
- logger.info(`[AgentFlow] Exact match \u2014 returning cached result (${elapsedTime2}ms)`);
17932
- const rehydratedComponent = component ? rehydrateCachedComponent(component) : null;
17933
- const storedBinding = conversationMatch.uiBlock?.scriptBinding;
17934
- const cachedBinding = storedBinding ? {
17935
- recipeId: storedBinding.recipeId,
17936
- params: storedBinding.params || {},
17937
- name: storedBinding.name || "",
17938
- columns: storedBinding.columns
17939
- } : (() => {
17940
- const bound = component ? extractBoundRecipe(component) : void 0;
17941
- return bound ? { recipeId: bound.recipeId, params: bound.params, name: "" } : void 0;
17942
- })();
17943
- return {
17944
- success: true,
17945
- data: {
17946
- text: cachedTextResponse,
17947
- component: rehydratedComponent,
17948
- reasoning: `Exact match from previous conversation`,
17949
- method: `${provider}-agent-exact-match`,
17950
- semanticSimilarity: 1,
17951
- ...cachedBinding ? { scriptBinding: cachedBinding } : {}
17952
- },
17953
- errors: []
17954
- };
17955
- }
17956
- }
17977
+ const cached = await replayExactMatch({ prompt, collections, userId, streamCallback, method: `${provider}-agent-exact-match`, startTime });
17978
+ if (cached?.response) return cached.response;
17979
+ const directReplay = cached?.directReplay;
17957
17980
  logger.info(`[AgentFlow] No exact match \u2014 proceeding with agent`);
17958
17981
  runTrace = createRunTrace();
17959
17982
  const apiKey = (() => {
@@ -19391,7 +19414,8 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
19391
19414
  }
19392
19415
  let userResponse;
19393
19416
  try {
19394
- userResponse = answerProvider ? await answerProvider({
19417
+ const replayed = answerProvider ? (await replayExactMatch({ prompt, collections, userId, streamCallback, method: "custom-exact-match", startTime: Date.now() }))?.response : void 0;
19418
+ userResponse = replayed ? replayed : answerProvider ? await answerProvider({
19395
19419
  prompt,
19396
19420
  components,
19397
19421
  collections,
@@ -24723,7 +24747,8 @@ fetch(BRIDGE + '/query', {
24723
24747
  // request context; absent, the data seam treats the query as anonymous.
24724
24748
  ...(CONTEXT_TOKEN ? { 'x-sa-context': CONTEXT_TOKEN } : {}),
24725
24749
  },
24726
- body: JSON.stringify({ toolId, sql, limit: Math.min(Number(limitArg) || MAX_ROWS, MAX_ROWS) }),
24750
+ // ref: ask for a handle the deliverable can cite instead of copying rows.
24751
+ body: JSON.stringify({ toolId, sql, limit: Math.min(Number(limitArg) || MAX_ROWS, MAX_ROWS), ref: true }),
24727
24752
  })
24728
24753
  .then(async (r) => {
24729
24754
  const text = await r.text();
@@ -24732,11 +24757,21 @@ fetch(BRIDGE + '/query', {
24732
24757
  if (p && p.error) { console.error(String(p.error)); process.exit(1); }
24733
24758
  const rows = (p && (p.data || p.rows)) || p;
24734
24759
  const all = Array.isArray(rows) ? rows : [rows];
24760
+ // The source truncates before we see the rows and rewrites count to the
24761
+ // truncated length; only metadata keeps the real total.
24762
+ const meta = (p && p.metadata) || {};
24763
+ const shown = all.slice(0, MAX_ROWS);
24764
+ const total = meta.totalCount != null ? meta.totalCount : ((p && p.count) != null ? p.count : all.length);
24765
+ const truncated = !!meta.truncated || all.length > MAX_ROWS || total > shown.length;
24735
24766
  // Capped so one careless SELECT * cannot flood the agent's context.
24736
24767
  console.log(JSON.stringify({
24737
- count: (p && p.count) != null ? p.count : all.length,
24738
- truncated: all.length > MAX_ROWS,
24739
- rows: all.slice(0, MAX_ROWS),
24768
+ ...(p && p.ref ? { ref: p.ref } : {}),
24769
+ count: total,
24770
+ returned: shown.length,
24771
+ truncated,
24772
+ ...(truncated ? { warning: 'INCOMPLETE: only the first ' + shown.length + ' of ' + total + ' rows are shown. Do not compute totals, counts, shares or rankings from these rows; aggregate in SQL instead.' } : {}),
24773
+ ...(meta.appliedPolicies && meta.appliedPolicies.length ? { restrictedBy: meta.appliedPolicies } : {}),
24774
+ rows: shown,
24740
24775
  }, null, 2));
24741
24776
  })
24742
24777
  .catch((e) => { console.error('Could not reach the data bridge: ' + (e && e.message)); process.exit(1); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superatomai/sdk-node",
3
- "version": "0.0.61-dsp",
3
+ "version": "0.0.62-dsp",
4
4
  "description": "Node.js TypeScript SDK for Superatom",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",