@runtypelabs/sdk 1.23.0 → 1.24.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.
package/dist/index.cjs CHANGED
@@ -469,6 +469,53 @@ var FlowResult = class {
469
469
  };
470
470
 
471
471
  // src/flows-namespace.ts
472
+ function isRecord(value) {
473
+ return value !== null && typeof value === "object" && !Array.isArray(value);
474
+ }
475
+ function normalizeConfig(config) {
476
+ if (!isRecord(config)) return {};
477
+ const normalized = {};
478
+ for (const key of Object.keys(config).sort()) {
479
+ const value = config[key];
480
+ if (value === void 0) continue;
481
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
482
+ normalized[key] = normalizeConfig(value);
483
+ } else if (Array.isArray(value)) {
484
+ normalized[key] = value.map((item) => {
485
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
486
+ return normalizeConfig(item);
487
+ }
488
+ return item;
489
+ });
490
+ } else {
491
+ normalized[key] = value;
492
+ }
493
+ }
494
+ return normalized;
495
+ }
496
+ function normalizeStepForHash(step) {
497
+ const stepObj = isRecord(step) ? step : {};
498
+ return {
499
+ type: typeof stepObj.type === "string" ? stepObj.type : "",
500
+ name: typeof stepObj.name === "string" ? stepObj.name : "",
501
+ enabled: stepObj.enabled !== false,
502
+ ...typeof stepObj.when === "string" ? { when: stepObj.when } : {},
503
+ config: normalizeConfig(stepObj.config),
504
+ order: typeof stepObj.order === "number" ? stepObj.order : 0
505
+ };
506
+ }
507
+ async function computeFlowContentHash(steps) {
508
+ const normalized = [...steps].sort((a, b) => {
509
+ const orderA = isRecord(a) && typeof a.order === "number" ? a.order : 0;
510
+ const orderB = isRecord(b) && typeof b.order === "number" ? b.order : 0;
511
+ return orderA - orderB;
512
+ }).map(normalizeStepForHash);
513
+ const serialized = JSON.stringify(normalized);
514
+ const encoded = new TextEncoder().encode(serialized);
515
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
516
+ const hashArray = new Uint8Array(hashBuffer);
517
+ return Array.from(hashArray).map((b) => b.toString(16).padStart(2, "0")).join("");
518
+ }
472
519
  var FlowsNamespace = class {
473
520
  constructor(getClient) {
474
521
  this.getClient = getClient;
@@ -914,7 +961,7 @@ var RuntypeFlowBuilder = class {
914
961
  }
915
962
  config.options = { ...config.options, streamResponse: true };
916
963
  const client = this.getClient();
917
- const response = await client.dispatch(config);
964
+ const response = await this.dispatchWithPersistedFlow(client, config);
918
965
  const result = new FlowResult(response);
919
966
  if (callbacks) {
920
967
  await result.stream(callbacks);
@@ -943,7 +990,7 @@ var RuntypeFlowBuilder = class {
943
990
  }
944
991
  config.options = { ...config.options, streamResponse: true };
945
992
  const client = this.getClient();
946
- const response = await client.dispatch(config);
993
+ const response = await this.dispatchWithPersistedFlow(client, config);
947
994
  const result = new FlowResult(response);
948
995
  await result.getSummary();
949
996
  return result;
@@ -1127,6 +1174,37 @@ var RuntypeFlowBuilder = class {
1127
1174
  // ============================================================================
1128
1175
  // Private Helpers
1129
1176
  // ============================================================================
1177
+ /**
1178
+ * Persisted flow protocol (APQ-style): send hash-only first, retry with
1179
+ * full definition on FLOW_DEFINITION_REQUIRED. For non-upsert modes,
1180
+ * dispatches directly.
1181
+ */
1182
+ async dispatchWithPersistedFlow(client, config) {
1183
+ if (this.mode !== "upsert" || !this.steps.length) {
1184
+ return client.dispatch(config);
1185
+ }
1186
+ const contentHash = await this.computeContentHash();
1187
+ const hashOnlyConfig = {
1188
+ ...config,
1189
+ flow: { name: config.flow.name, contentHash }
1190
+ };
1191
+ try {
1192
+ return await client.dispatch(hashOnlyConfig);
1193
+ } catch (err) {
1194
+ const message = err instanceof Error ? err.message : "";
1195
+ if (!message.includes("422")) {
1196
+ throw err;
1197
+ }
1198
+ }
1199
+ const fullConfig = {
1200
+ ...config,
1201
+ flow: { ...config.flow, contentHash }
1202
+ };
1203
+ return client.dispatch(fullConfig);
1204
+ }
1205
+ async computeContentHash() {
1206
+ return computeFlowContentHash(this.steps);
1207
+ }
1130
1208
  addStep(type, name, config, enabled = true) {
1131
1209
  this.stepCounter++;
1132
1210
  const cleanConfig = {};
package/dist/index.d.cts CHANGED
@@ -1383,6 +1383,7 @@ interface DispatchRequest {
1383
1383
  flow: {
1384
1384
  id?: string;
1385
1385
  name?: string;
1386
+ contentHash?: string;
1386
1387
  steps?: Array<any>;
1387
1388
  };
1388
1389
  messages?: Array<{
@@ -1935,6 +1936,13 @@ declare class RuntypeFlowBuilder {
1935
1936
  */
1936
1937
  runWithLocalTools(localTools: Record<string, (args: unknown) => Promise<unknown>>, callbacks?: StreamCallbacks): Promise<FlowResult>;
1937
1938
  build(): DispatchRequest;
1939
+ /**
1940
+ * Persisted flow protocol (APQ-style): send hash-only first, retry with
1941
+ * full definition on FLOW_DEFINITION_REQUIRED. For non-upsert modes,
1942
+ * dispatches directly.
1943
+ */
1944
+ private dispatchWithPersistedFlow;
1945
+ private computeContentHash;
1938
1946
  private addStep;
1939
1947
  }
1940
1948
 
package/dist/index.d.ts CHANGED
@@ -1383,6 +1383,7 @@ interface DispatchRequest {
1383
1383
  flow: {
1384
1384
  id?: string;
1385
1385
  name?: string;
1386
+ contentHash?: string;
1386
1387
  steps?: Array<any>;
1387
1388
  };
1388
1389
  messages?: Array<{
@@ -1935,6 +1936,13 @@ declare class RuntypeFlowBuilder {
1935
1936
  */
1936
1937
  runWithLocalTools(localTools: Record<string, (args: unknown) => Promise<unknown>>, callbacks?: StreamCallbacks): Promise<FlowResult>;
1937
1938
  build(): DispatchRequest;
1939
+ /**
1940
+ * Persisted flow protocol (APQ-style): send hash-only first, retry with
1941
+ * full definition on FLOW_DEFINITION_REQUIRED. For non-upsert modes,
1942
+ * dispatches directly.
1943
+ */
1944
+ private dispatchWithPersistedFlow;
1945
+ private computeContentHash;
1938
1946
  private addStep;
1939
1947
  }
1940
1948
 
package/dist/index.mjs CHANGED
@@ -397,6 +397,53 @@ var FlowResult = class {
397
397
  };
398
398
 
399
399
  // src/flows-namespace.ts
400
+ function isRecord(value) {
401
+ return value !== null && typeof value === "object" && !Array.isArray(value);
402
+ }
403
+ function normalizeConfig(config) {
404
+ if (!isRecord(config)) return {};
405
+ const normalized = {};
406
+ for (const key of Object.keys(config).sort()) {
407
+ const value = config[key];
408
+ if (value === void 0) continue;
409
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
410
+ normalized[key] = normalizeConfig(value);
411
+ } else if (Array.isArray(value)) {
412
+ normalized[key] = value.map((item) => {
413
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
414
+ return normalizeConfig(item);
415
+ }
416
+ return item;
417
+ });
418
+ } else {
419
+ normalized[key] = value;
420
+ }
421
+ }
422
+ return normalized;
423
+ }
424
+ function normalizeStepForHash(step) {
425
+ const stepObj = isRecord(step) ? step : {};
426
+ return {
427
+ type: typeof stepObj.type === "string" ? stepObj.type : "",
428
+ name: typeof stepObj.name === "string" ? stepObj.name : "",
429
+ enabled: stepObj.enabled !== false,
430
+ ...typeof stepObj.when === "string" ? { when: stepObj.when } : {},
431
+ config: normalizeConfig(stepObj.config),
432
+ order: typeof stepObj.order === "number" ? stepObj.order : 0
433
+ };
434
+ }
435
+ async function computeFlowContentHash(steps) {
436
+ const normalized = [...steps].sort((a, b) => {
437
+ const orderA = isRecord(a) && typeof a.order === "number" ? a.order : 0;
438
+ const orderB = isRecord(b) && typeof b.order === "number" ? b.order : 0;
439
+ return orderA - orderB;
440
+ }).map(normalizeStepForHash);
441
+ const serialized = JSON.stringify(normalized);
442
+ const encoded = new TextEncoder().encode(serialized);
443
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
444
+ const hashArray = new Uint8Array(hashBuffer);
445
+ return Array.from(hashArray).map((b) => b.toString(16).padStart(2, "0")).join("");
446
+ }
400
447
  var FlowsNamespace = class {
401
448
  constructor(getClient) {
402
449
  this.getClient = getClient;
@@ -842,7 +889,7 @@ var RuntypeFlowBuilder = class {
842
889
  }
843
890
  config.options = { ...config.options, streamResponse: true };
844
891
  const client = this.getClient();
845
- const response = await client.dispatch(config);
892
+ const response = await this.dispatchWithPersistedFlow(client, config);
846
893
  const result = new FlowResult(response);
847
894
  if (callbacks) {
848
895
  await result.stream(callbacks);
@@ -871,7 +918,7 @@ var RuntypeFlowBuilder = class {
871
918
  }
872
919
  config.options = { ...config.options, streamResponse: true };
873
920
  const client = this.getClient();
874
- const response = await client.dispatch(config);
921
+ const response = await this.dispatchWithPersistedFlow(client, config);
875
922
  const result = new FlowResult(response);
876
923
  await result.getSummary();
877
924
  return result;
@@ -1055,6 +1102,37 @@ var RuntypeFlowBuilder = class {
1055
1102
  // ============================================================================
1056
1103
  // Private Helpers
1057
1104
  // ============================================================================
1105
+ /**
1106
+ * Persisted flow protocol (APQ-style): send hash-only first, retry with
1107
+ * full definition on FLOW_DEFINITION_REQUIRED. For non-upsert modes,
1108
+ * dispatches directly.
1109
+ */
1110
+ async dispatchWithPersistedFlow(client, config) {
1111
+ if (this.mode !== "upsert" || !this.steps.length) {
1112
+ return client.dispatch(config);
1113
+ }
1114
+ const contentHash = await this.computeContentHash();
1115
+ const hashOnlyConfig = {
1116
+ ...config,
1117
+ flow: { name: config.flow.name, contentHash }
1118
+ };
1119
+ try {
1120
+ return await client.dispatch(hashOnlyConfig);
1121
+ } catch (err) {
1122
+ const message = err instanceof Error ? err.message : "";
1123
+ if (!message.includes("422")) {
1124
+ throw err;
1125
+ }
1126
+ }
1127
+ const fullConfig = {
1128
+ ...config,
1129
+ flow: { ...config.flow, contentHash }
1130
+ };
1131
+ return client.dispatch(fullConfig);
1132
+ }
1133
+ async computeContentHash() {
1134
+ return computeFlowContentHash(this.steps);
1135
+ }
1058
1136
  addStep(type, name, config, enabled = true) {
1059
1137
  this.stepCounter++;
1060
1138
  const cleanConfig = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/sdk",
3
- "version": "1.23.0",
3
+ "version": "1.24.0",
4
4
  "type": "module",
5
5
  "description": "TypeScript SDK for the Runtype API with fluent methods. Use it to quickly realize AI products, agents, and workflows.",
6
6
  "main": "dist/index.cjs",