@superatomai/sdk-node 0.0.73-dsp → 0.0.74-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.mts CHANGED
@@ -6456,6 +6456,7 @@ declare class SuperatomSDK {
6456
6456
  private components;
6457
6457
  private tools;
6458
6458
  private workflows;
6459
+ private registryWaiters;
6459
6460
  private anthropicApiKey;
6460
6461
  private groqApiKey;
6461
6462
  private geminiApiKey;
@@ -6622,6 +6623,19 @@ declare class SuperatomSDK {
6622
6623
  * the workflow component is returned directly, with the LLM-extracted props.
6623
6624
  */
6624
6625
  setWorkflows(workflows: WorkflowDescriptor[]): void;
6626
+ /**
6627
+ * Make sure the agent has a tool surface before answering a prompt.
6628
+ *
6629
+ * Components and workflows are pushed by the browser once per page load and
6630
+ * kept only in memory, so a backend restart leaves both lists empty until
6631
+ * someone reloads a tab. The agent still answers — with no workflows to route
6632
+ * to and no components to render — which looks like a model regression rather
6633
+ * than lost state. Ask the client to re-send instead, and proceed regardless
6634
+ * so a stale or silent client degrades no worse than it does today.
6635
+ */
6636
+ private ensureRegistry;
6637
+ /** Release anyone blocked in ensureRegistry() now that the list has arrived. */
6638
+ private resolveRegistryWaiters;
6625
6639
  /**
6626
6640
  * Get the registered workflow components.
6627
6641
  */
package/dist/index.d.ts CHANGED
@@ -6456,6 +6456,7 @@ declare class SuperatomSDK {
6456
6456
  private components;
6457
6457
  private tools;
6458
6458
  private workflows;
6459
+ private registryWaiters;
6459
6460
  private anthropicApiKey;
6460
6461
  private groqApiKey;
6461
6462
  private geminiApiKey;
@@ -6622,6 +6623,19 @@ declare class SuperatomSDK {
6622
6623
  * the workflow component is returned directly, with the LLM-extracted props.
6623
6624
  */
6624
6625
  setWorkflows(workflows: WorkflowDescriptor[]): void;
6626
+ /**
6627
+ * Make sure the agent has a tool surface before answering a prompt.
6628
+ *
6629
+ * Components and workflows are pushed by the browser once per page load and
6630
+ * kept only in memory, so a backend restart leaves both lists empty until
6631
+ * someone reloads a tab. The agent still answers — with no workflows to route
6632
+ * to and no components to render — which looks like a model regression rather
6633
+ * than lost state. Ask the client to re-send instead, and proceed regardless
6634
+ * so a stale or silent client degrades no worse than it does today.
6635
+ */
6636
+ private ensureRegistry;
6637
+ /** Release anyone blocked in ensureRegistry() now that the list has arrived. */
6638
+ private resolveRegistryWaiters;
6625
6639
  /**
6626
6640
  * Get the registered workflow components.
6627
6641
  */
package/dist/index.js CHANGED
@@ -121,7 +121,7 @@ module.exports = __toCommonJS(index_exports);
121
121
  // src/websocket.ts
122
122
  var import_ws = __toESM(require("ws"));
123
123
  function createWebSocket(url) {
124
- return new import_ws.default(url);
124
+ return new import_ws.default(url, { handshakeTimeout: 15e3 });
125
125
  }
126
126
 
127
127
  // src/types.ts
@@ -5021,6 +5021,7 @@ var LLM = class {
5021
5021
  try {
5022
5022
  currentToolUse.input = currentToolUse.inputJson ? JSON.parse(currentToolUse.inputJson) : {};
5023
5023
  } catch (error) {
5024
+ logger.error(`[LLM] Tool "${currentToolUse.name}" input was not valid JSON, falling back to {}: ${String(currentToolUse.inputJson).substring(0, 500)}`);
5024
5025
  currentToolUse.input = {};
5025
5026
  }
5026
5027
  delete currentToolUse.inputJson;
@@ -5100,10 +5101,12 @@ var LLM = class {
5100
5101
  content: stripLoneSurrogates(resultContent)
5101
5102
  };
5102
5103
  } catch (error) {
5104
+ const message = error instanceof Error ? error.message : String(error);
5105
+ logger.error(`[LLM] Tool "${toolUse.name}" threw: ${message} | input: ${JSON.stringify(toolUse.input).substring(0, 300)}`);
5103
5106
  return {
5104
5107
  type: "tool_result",
5105
5108
  tool_use_id: toolUse.id,
5106
- content: error instanceof Error ? error.message : String(error),
5109
+ content: message,
5107
5110
  is_error: true
5108
5111
  };
5109
5112
  }
@@ -5111,6 +5114,9 @@ var LLM = class {
5111
5114
  toolResultEntries.forEach((entry) => toolResults.content.push(entry));
5112
5115
  conversationMessages.push(toolResults);
5113
5116
  } else {
5117
+ if (stopReason === "tool_use") {
5118
+ logger.error(`[LLM] stop_reason=tool_use but no tool_use block survived parsing (${contentBlocks.length} content block(s))`);
5119
+ }
5114
5120
  break;
5115
5121
  }
5116
5122
  }
@@ -29277,6 +29283,8 @@ var SuperatomSDK = class {
29277
29283
  this.components = [];
29278
29284
  this.tools = [];
29279
29285
  this.workflows = [];
29286
+ // Resolvers waiting on a registry re-send, keyed by the RES type expected.
29287
+ this.registryWaiters = /* @__PURE__ */ new Map();
29280
29288
  // Heartbeat properties for keeping WebSocket connection alive.
29281
29289
  // 25s ping + 10s grace stays under common NAT/LB idle thresholds (~60-100s)
29282
29290
  // so we detect dead sockets within seconds instead of minutes.
@@ -29486,21 +29494,23 @@ var SuperatomSDK = class {
29486
29494
  });
29487
29495
  break;
29488
29496
  case "USER_PROMPT_REQ":
29489
- handleUserPromptRequest(
29490
- parsed,
29491
- this.components,
29492
- (msg) => this.send(msg),
29493
- this.anthropicApiKey,
29494
- this.groqApiKey,
29495
- this.geminiApiKey,
29496
- this.openaiApiKey,
29497
- this.llmProviders,
29498
- this.collections,
29499
- this.tools,
29500
- this.mainAgentModel,
29501
- this.sourceAgentModel,
29502
- this.workflows,
29503
- this.answerFlow === "custom" ? this.answerProvider : void 0
29497
+ this.ensureRegistry().then(
29498
+ () => handleUserPromptRequest(
29499
+ parsed,
29500
+ this.components,
29501
+ (msg) => this.send(msg),
29502
+ this.anthropicApiKey,
29503
+ this.groqApiKey,
29504
+ this.geminiApiKey,
29505
+ this.openaiApiKey,
29506
+ this.llmProviders,
29507
+ this.collections,
29508
+ this.tools,
29509
+ this.mainAgentModel,
29510
+ this.sourceAgentModel,
29511
+ this.workflows,
29512
+ this.answerFlow === "custom" ? this.answerProvider : void 0
29513
+ )
29504
29514
  ).catch((error) => {
29505
29515
  logger.error("Failed to handle user prompt request:", error);
29506
29516
  });
@@ -29518,8 +29528,12 @@ var SuperatomSDK = class {
29518
29528
  });
29519
29529
  break;
29520
29530
  case "COMPONENT_LIST_RES":
29521
- handleComponentListResponse(parsed, (com) => this.storeComponents(com), this.collections).catch((error) => {
29531
+ handleComponentListResponse(parsed, (com) => {
29532
+ this.storeComponents(com);
29533
+ this.resolveRegistryWaiters("COMPONENT_LIST_RES");
29534
+ }, this.collections).catch((error) => {
29522
29535
  logger.error("Failed to handle component list request:", error);
29536
+ this.resolveRegistryWaiters("COMPONENT_LIST_RES");
29523
29537
  });
29524
29538
  break;
29525
29539
  /* The dashboard contract, pushed by the bundle that holds it — on
@@ -29530,8 +29544,12 @@ var SuperatomSDK = class {
29530
29544
  });
29531
29545
  break;
29532
29546
  case "WORKFLOW_LIST_RES":
29533
- handleWorkflowListResponse(parsed, (wf) => this.setWorkflows(wf)).catch((error) => {
29547
+ handleWorkflowListResponse(parsed, (wf) => {
29548
+ this.setWorkflows(wf);
29549
+ this.resolveRegistryWaiters("WORKFLOW_LIST_RES");
29550
+ }).catch((error) => {
29534
29551
  logger.error("Failed to handle workflow list request:", error);
29552
+ this.resolveRegistryWaiters("WORKFLOW_LIST_RES");
29535
29553
  });
29536
29554
  break;
29537
29555
  case "USERS":
@@ -29876,6 +29894,50 @@ var SuperatomSDK = class {
29876
29894
  this.workflows = workflows;
29877
29895
  logger.info(`Workflows stored in SDK: ${workflows.length} workflow(s)`);
29878
29896
  }
29897
+ /**
29898
+ * Make sure the agent has a tool surface before answering a prompt.
29899
+ *
29900
+ * Components and workflows are pushed by the browser once per page load and
29901
+ * kept only in memory, so a backend restart leaves both lists empty until
29902
+ * someone reloads a tab. The agent still answers — with no workflows to route
29903
+ * to and no components to render — which looks like a model regression rather
29904
+ * than lost state. Ask the client to re-send instead, and proceed regardless
29905
+ * so a stale or silent client degrades no worse than it does today.
29906
+ */
29907
+ async ensureRegistry(timeoutMs = 4e3) {
29908
+ const missing = [];
29909
+ if (this.workflows.length === 0) missing.push(["WORKFLOW_LIST_REQ", "WORKFLOW_LIST_RES"]);
29910
+ if (this.components.length === 0) missing.push(["COMPONENT_LIST_REQ", "COMPONENT_LIST_RES"]);
29911
+ if (missing.length === 0) return;
29912
+ logger.warn(`Registry empty, requesting from client: ${missing.map(([req]) => req).join(", ")}`);
29913
+ await Promise.all(missing.map(([reqType, resType]) => new Promise((resolve4) => {
29914
+ let settled = false;
29915
+ const finish = () => {
29916
+ if (!settled) {
29917
+ settled = true;
29918
+ resolve4();
29919
+ }
29920
+ };
29921
+ const waiters = this.registryWaiters.get(resType) ?? [];
29922
+ waiters.push(finish);
29923
+ this.registryWaiters.set(resType, waiters);
29924
+ const sent = this.send({
29925
+ type: reqType,
29926
+ from: { type: this.type },
29927
+ to: { type: "runtime" },
29928
+ payload: {}
29929
+ });
29930
+ if (!sent) finish();
29931
+ else setTimeout(finish, timeoutMs);
29932
+ })));
29933
+ }
29934
+ /** Release anyone blocked in ensureRegistry() now that the list has arrived. */
29935
+ resolveRegistryWaiters(resType) {
29936
+ const waiters = this.registryWaiters.get(resType);
29937
+ if (!waiters || waiters.length === 0) return;
29938
+ this.registryWaiters.delete(resType);
29939
+ waiters.forEach((resolve4) => resolve4());
29940
+ }
29879
29941
  /**
29880
29942
  * Get the registered workflow components.
29881
29943
  */
package/dist/index.mjs CHANGED
@@ -8,7 +8,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
8
8
  // src/websocket.ts
9
9
  import WebSocket from "ws";
10
10
  function createWebSocket(url) {
11
- return new WebSocket(url);
11
+ return new WebSocket(url, { handshakeTimeout: 15e3 });
12
12
  }
13
13
 
14
14
  // src/types.ts
@@ -4908,6 +4908,7 @@ var LLM = class {
4908
4908
  try {
4909
4909
  currentToolUse.input = currentToolUse.inputJson ? JSON.parse(currentToolUse.inputJson) : {};
4910
4910
  } catch (error) {
4911
+ logger.error(`[LLM] Tool "${currentToolUse.name}" input was not valid JSON, falling back to {}: ${String(currentToolUse.inputJson).substring(0, 500)}`);
4911
4912
  currentToolUse.input = {};
4912
4913
  }
4913
4914
  delete currentToolUse.inputJson;
@@ -4987,10 +4988,12 @@ var LLM = class {
4987
4988
  content: stripLoneSurrogates(resultContent)
4988
4989
  };
4989
4990
  } catch (error) {
4991
+ const message = error instanceof Error ? error.message : String(error);
4992
+ logger.error(`[LLM] Tool "${toolUse.name}" threw: ${message} | input: ${JSON.stringify(toolUse.input).substring(0, 300)}`);
4990
4993
  return {
4991
4994
  type: "tool_result",
4992
4995
  tool_use_id: toolUse.id,
4993
- content: error instanceof Error ? error.message : String(error),
4996
+ content: message,
4994
4997
  is_error: true
4995
4998
  };
4996
4999
  }
@@ -4998,6 +5001,9 @@ var LLM = class {
4998
5001
  toolResultEntries.forEach((entry) => toolResults.content.push(entry));
4999
5002
  conversationMessages.push(toolResults);
5000
5003
  } else {
5004
+ if (stopReason === "tool_use") {
5005
+ logger.error(`[LLM] stop_reason=tool_use but no tool_use block survived parsing (${contentBlocks.length} content block(s))`);
5006
+ }
5001
5007
  break;
5002
5008
  }
5003
5009
  }
@@ -29164,6 +29170,8 @@ var SuperatomSDK = class {
29164
29170
  this.components = [];
29165
29171
  this.tools = [];
29166
29172
  this.workflows = [];
29173
+ // Resolvers waiting on a registry re-send, keyed by the RES type expected.
29174
+ this.registryWaiters = /* @__PURE__ */ new Map();
29167
29175
  // Heartbeat properties for keeping WebSocket connection alive.
29168
29176
  // 25s ping + 10s grace stays under common NAT/LB idle thresholds (~60-100s)
29169
29177
  // so we detect dead sockets within seconds instead of minutes.
@@ -29373,21 +29381,23 @@ var SuperatomSDK = class {
29373
29381
  });
29374
29382
  break;
29375
29383
  case "USER_PROMPT_REQ":
29376
- handleUserPromptRequest(
29377
- parsed,
29378
- this.components,
29379
- (msg) => this.send(msg),
29380
- this.anthropicApiKey,
29381
- this.groqApiKey,
29382
- this.geminiApiKey,
29383
- this.openaiApiKey,
29384
- this.llmProviders,
29385
- this.collections,
29386
- this.tools,
29387
- this.mainAgentModel,
29388
- this.sourceAgentModel,
29389
- this.workflows,
29390
- this.answerFlow === "custom" ? this.answerProvider : void 0
29384
+ this.ensureRegistry().then(
29385
+ () => handleUserPromptRequest(
29386
+ parsed,
29387
+ this.components,
29388
+ (msg) => this.send(msg),
29389
+ this.anthropicApiKey,
29390
+ this.groqApiKey,
29391
+ this.geminiApiKey,
29392
+ this.openaiApiKey,
29393
+ this.llmProviders,
29394
+ this.collections,
29395
+ this.tools,
29396
+ this.mainAgentModel,
29397
+ this.sourceAgentModel,
29398
+ this.workflows,
29399
+ this.answerFlow === "custom" ? this.answerProvider : void 0
29400
+ )
29391
29401
  ).catch((error) => {
29392
29402
  logger.error("Failed to handle user prompt request:", error);
29393
29403
  });
@@ -29405,8 +29415,12 @@ var SuperatomSDK = class {
29405
29415
  });
29406
29416
  break;
29407
29417
  case "COMPONENT_LIST_RES":
29408
- handleComponentListResponse(parsed, (com) => this.storeComponents(com), this.collections).catch((error) => {
29418
+ handleComponentListResponse(parsed, (com) => {
29419
+ this.storeComponents(com);
29420
+ this.resolveRegistryWaiters("COMPONENT_LIST_RES");
29421
+ }, this.collections).catch((error) => {
29409
29422
  logger.error("Failed to handle component list request:", error);
29423
+ this.resolveRegistryWaiters("COMPONENT_LIST_RES");
29410
29424
  });
29411
29425
  break;
29412
29426
  /* The dashboard contract, pushed by the bundle that holds it — on
@@ -29417,8 +29431,12 @@ var SuperatomSDK = class {
29417
29431
  });
29418
29432
  break;
29419
29433
  case "WORKFLOW_LIST_RES":
29420
- handleWorkflowListResponse(parsed, (wf) => this.setWorkflows(wf)).catch((error) => {
29434
+ handleWorkflowListResponse(parsed, (wf) => {
29435
+ this.setWorkflows(wf);
29436
+ this.resolveRegistryWaiters("WORKFLOW_LIST_RES");
29437
+ }).catch((error) => {
29421
29438
  logger.error("Failed to handle workflow list request:", error);
29439
+ this.resolveRegistryWaiters("WORKFLOW_LIST_RES");
29422
29440
  });
29423
29441
  break;
29424
29442
  case "USERS":
@@ -29763,6 +29781,50 @@ var SuperatomSDK = class {
29763
29781
  this.workflows = workflows;
29764
29782
  logger.info(`Workflows stored in SDK: ${workflows.length} workflow(s)`);
29765
29783
  }
29784
+ /**
29785
+ * Make sure the agent has a tool surface before answering a prompt.
29786
+ *
29787
+ * Components and workflows are pushed by the browser once per page load and
29788
+ * kept only in memory, so a backend restart leaves both lists empty until
29789
+ * someone reloads a tab. The agent still answers — with no workflows to route
29790
+ * to and no components to render — which looks like a model regression rather
29791
+ * than lost state. Ask the client to re-send instead, and proceed regardless
29792
+ * so a stale or silent client degrades no worse than it does today.
29793
+ */
29794
+ async ensureRegistry(timeoutMs = 4e3) {
29795
+ const missing = [];
29796
+ if (this.workflows.length === 0) missing.push(["WORKFLOW_LIST_REQ", "WORKFLOW_LIST_RES"]);
29797
+ if (this.components.length === 0) missing.push(["COMPONENT_LIST_REQ", "COMPONENT_LIST_RES"]);
29798
+ if (missing.length === 0) return;
29799
+ logger.warn(`Registry empty, requesting from client: ${missing.map(([req]) => req).join(", ")}`);
29800
+ await Promise.all(missing.map(([reqType, resType]) => new Promise((resolve4) => {
29801
+ let settled = false;
29802
+ const finish = () => {
29803
+ if (!settled) {
29804
+ settled = true;
29805
+ resolve4();
29806
+ }
29807
+ };
29808
+ const waiters = this.registryWaiters.get(resType) ?? [];
29809
+ waiters.push(finish);
29810
+ this.registryWaiters.set(resType, waiters);
29811
+ const sent = this.send({
29812
+ type: reqType,
29813
+ from: { type: this.type },
29814
+ to: { type: "runtime" },
29815
+ payload: {}
29816
+ });
29817
+ if (!sent) finish();
29818
+ else setTimeout(finish, timeoutMs);
29819
+ })));
29820
+ }
29821
+ /** Release anyone blocked in ensureRegistry() now that the list has arrived. */
29822
+ resolveRegistryWaiters(resType) {
29823
+ const waiters = this.registryWaiters.get(resType);
29824
+ if (!waiters || waiters.length === 0) return;
29825
+ this.registryWaiters.delete(resType);
29826
+ waiters.forEach((resolve4) => resolve4());
29827
+ }
29766
29828
  /**
29767
29829
  * Get the registered workflow components.
29768
29830
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superatomai/sdk-node",
3
- "version": "0.0.73-dsp",
3
+ "version": "0.0.74-dsp",
4
4
  "description": "Node.js TypeScript SDK for Superatom",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",