lemma-sdk 0.2.45 → 0.3.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 (129) hide show
  1. package/README.md +16 -0
  2. package/dist/assistant-events.js +12 -2
  3. package/dist/browser/lemma-client.js +13683 -4460
  4. package/dist/browser.d.ts +14 -6
  5. package/dist/browser.js +35 -6
  6. package/dist/client.d.ts +4 -0
  7. package/dist/client.js +16 -4
  8. package/dist/config.d.ts +4 -0
  9. package/dist/config.js +7 -1
  10. package/dist/generated.d.ts +14 -1
  11. package/dist/generated.js +62 -11
  12. package/dist/http.d.ts +47 -1
  13. package/dist/http.js +145 -26
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/namespaces/agents.d.ts +20 -1
  17. package/dist/namespaces/agents.js +29 -1
  18. package/dist/namespaces/conversations.d.ts +5 -2
  19. package/dist/namespaces/conversations.js +2 -0
  20. package/dist/namespaces/desks.d.ts +9 -0
  21. package/dist/namespaces/desks.js +6 -0
  22. package/dist/namespaces/files.d.ts +18 -0
  23. package/dist/namespaces/files.js +21 -0
  24. package/dist/namespaces/functions.d.ts +3 -0
  25. package/dist/namespaces/functions.js +5 -0
  26. package/dist/namespaces/integrations.d.ts +2 -4
  27. package/dist/namespaces/integrations.js +2 -2
  28. package/dist/namespaces/records.d.ts +3 -9
  29. package/dist/namespaces/records.js +4 -33
  30. package/dist/namespaces/widgets.d.ts +17 -0
  31. package/dist/namespaces/widgets.js +16 -0
  32. package/dist/openapi_client/core/OpenAPI.js +1 -1
  33. package/dist/openapi_client/index.d.ts +7 -5
  34. package/dist/openapi_client/index.js +3 -0
  35. package/dist/openapi_client/models/AgentRunStatus.d.ts +10 -0
  36. package/dist/openapi_client/models/AgentRunStatus.js +15 -0
  37. package/dist/openapi_client/models/AgentToolset.d.ts +3 -2
  38. package/dist/openapi_client/models/AgentToolset.js +2 -1
  39. package/dist/openapi_client/models/AppTriggerResponseSchema.d.ts +2 -0
  40. package/dist/openapi_client/models/ApprovalDecisionResponse.d.ts +6 -0
  41. package/dist/openapi_client/models/AuthProvider.d.ts +4 -0
  42. package/dist/openapi_client/models/AuthProvider.js +9 -0
  43. package/dist/openapi_client/models/ConversationResponse.d.ts +4 -0
  44. package/dist/openapi_client/models/ConversationType.d.ts +2 -1
  45. package/dist/openapi_client/models/ConversationType.js +1 -0
  46. package/dist/openapi_client/models/DatastoreQueryRequest.d.ts +1 -1
  47. package/dist/openapi_client/models/FileSearchResultSchema.d.ts +2 -0
  48. package/dist/openapi_client/models/FileSignedUrlRequest.d.ts +4 -0
  49. package/dist/openapi_client/models/FileSignedUrlResponse.d.ts +6 -0
  50. package/dist/openapi_client/models/FileUrlResponse.d.ts +6 -0
  51. package/dist/openapi_client/models/FlowRunStatus.d.ts +3 -2
  52. package/dist/openapi_client/models/FlowRunStatus.js +3 -2
  53. package/dist/openapi_client/models/MessageKind.d.ts +14 -0
  54. package/dist/openapi_client/models/MessageKind.js +19 -0
  55. package/dist/openapi_client/models/MessageResponse.d.ts +5 -6
  56. package/dist/openapi_client/models/WorkflowRunResponse.d.ts +2 -2
  57. package/dist/openapi_client/services/AgentConversationsService.d.ts +8 -7
  58. package/dist/openapi_client/services/AgentConversationsService.js +7 -5
  59. package/dist/openapi_client/services/ApplicationsService.d.ts +19 -19
  60. package/dist/openapi_client/services/ApplicationsService.js +48 -44
  61. package/dist/openapi_client/services/FilesService.d.ts +20 -0
  62. package/dist/openapi_client/services/FilesService.js +47 -0
  63. package/dist/openapi_client/services/QueryService.d.ts +1 -1
  64. package/dist/openapi_client/services/QueryService.js +1 -1
  65. package/dist/openapi_client/services/RecordsService.d.ts +1 -3
  66. package/dist/openapi_client/services/RecordsService.js +1 -5
  67. package/dist/react/assistant-output.d.ts +6 -0
  68. package/dist/react/assistant-output.js +16 -0
  69. package/dist/react/useAssistantController.d.ts +8 -1
  70. package/dist/react/useAssistantController.js +87 -250
  71. package/dist/react/useAssistantRuntime.js +6 -17
  72. package/dist/react/useAssistantSession.d.ts +4 -2
  73. package/dist/react/useAssistantSession.js +6 -7
  74. package/dist/react/useConversationMessages.d.ts +2 -2
  75. package/dist/react/useConversationMessages.js +3 -5
  76. package/dist/react/useRecords.d.ts +1 -1
  77. package/dist/react/useRecords.js +2 -13
  78. package/dist/react/useReferencingRecords.d.ts +4 -6
  79. package/dist/react/useReferencingRecords.js +5 -5
  80. package/dist/react/useReverseRelatedRecords.d.ts +3 -4
  81. package/dist/react/useReverseRelatedRecords.js +5 -5
  82. package/dist/run-utils.d.ts +24 -0
  83. package/dist/run-utils.js +54 -0
  84. package/dist/types.d.ts +12 -6
  85. package/dist/version.d.ts +5 -0
  86. package/dist/version.js +7 -0
  87. package/package.json +10 -5
  88. package/dist/hey_client/client/client.gen.d.ts +0 -2
  89. package/dist/hey_client/client/client.gen.js +0 -216
  90. package/dist/hey_client/client/index.d.ts +0 -10
  91. package/dist/hey_client/client/index.js +0 -6
  92. package/dist/hey_client/client/types.gen.d.ts +0 -120
  93. package/dist/hey_client/client/types.gen.js +0 -2
  94. package/dist/hey_client/client/utils.gen.d.ts +0 -37
  95. package/dist/hey_client/client/utils.gen.js +0 -228
  96. package/dist/hey_client/client.gen.d.ts +0 -12
  97. package/dist/hey_client/client.gen.js +0 -3
  98. package/dist/hey_client/core/auth.gen.d.ts +0 -25
  99. package/dist/hey_client/core/auth.gen.js +0 -14
  100. package/dist/hey_client/core/bodySerializer.gen.d.ts +0 -25
  101. package/dist/hey_client/core/bodySerializer.gen.js +0 -57
  102. package/dist/hey_client/core/params.gen.d.ts +0 -43
  103. package/dist/hey_client/core/params.gen.js +0 -100
  104. package/dist/hey_client/core/pathSerializer.gen.d.ts +0 -33
  105. package/dist/hey_client/core/pathSerializer.gen.js +0 -106
  106. package/dist/hey_client/core/queryKeySerializer.gen.d.ts +0 -18
  107. package/dist/hey_client/core/queryKeySerializer.gen.js +0 -92
  108. package/dist/hey_client/core/serverSentEvents.gen.d.ts +0 -71
  109. package/dist/hey_client/core/serverSentEvents.gen.js +0 -132
  110. package/dist/hey_client/core/types.gen.d.ts +0 -83
  111. package/dist/hey_client/core/types.gen.js +0 -2
  112. package/dist/hey_client/core/utils.gen.d.ts +0 -19
  113. package/dist/hey_client/core/utils.gen.js +0 -87
  114. package/dist/hey_client/index.d.ts +0 -2
  115. package/dist/hey_client/index.js +0 -2
  116. package/dist/hey_client/sdk.gen.d.ts +0 -1005
  117. package/dist/hey_client/sdk.gen.js +0 -1438
  118. package/dist/hey_client/types.gen.d.ts +0 -13004
  119. package/dist/hey_client/types.gen.js +0 -2
  120. package/dist/openapi_client/models/NotificationContent.d.ts +0 -4
  121. package/dist/openapi_client/models/TextContent.d.ts +0 -4
  122. package/dist/openapi_client/models/ThinkingContent.d.ts +0 -4
  123. package/dist/openapi_client/models/ToolCallContent.d.ts +0 -6
  124. package/dist/openapi_client/models/ToolReturnContent.d.ts +0 -6
  125. package/dist/openapi_client/models/ToolReturnContent.js +0 -1
  126. /package/dist/openapi_client/models/{NotificationContent.js → ApprovalDecisionResponse.js} +0 -0
  127. /package/dist/openapi_client/models/{TextContent.js → FileSignedUrlRequest.js} +0 -0
  128. /package/dist/openapi_client/models/{ThinkingContent.js → FileSignedUrlResponse.js} +0 -0
  129. /package/dist/openapi_client/models/{ToolCallContent.js → FileUrlResponse.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { normalizeError, resolvePodClient, stringifyComparable } from "./utils.js";
3
- export function useRecords({ client, podId, tableName, filters, sort, limit = 20, pageToken, offset, sortBy, order, params, enabled = true, autoLoad = true, }) {
3
+ export function useRecords({ client, podId, tableName, filters, sort, limit = 20, pageToken, offset, enabled = true, autoLoad = true, }) {
4
4
  const [records, setRecords] = useState([]);
5
5
  const [total, setTotal] = useState(0);
6
6
  const [nextPageToken, setNextPageToken] = useState(null);
@@ -11,10 +11,8 @@ export function useRecords({ client, podId, tableName, filters, sort, limit = 20
11
11
  const isEnabled = enabled && trimmedTableName.length > 0;
12
12
  const filtersKey = stringifyComparable(filters);
13
13
  const sortKey = stringifyComparable(sort);
14
- const paramsKey = stringifyComparable(params);
15
14
  const stableFilters = useMemo(() => filters, [filtersKey]);
16
15
  const stableSort = useMemo(() => sort, [sortKey]);
17
- const stableParams = useMemo(() => params, [paramsKey]);
18
16
  const refresh = useCallback(async (overrides = {}, signal) => {
19
17
  if (!isEnabled) {
20
18
  setRecords([]);
@@ -34,9 +32,6 @@ export function useRecords({ client, podId, tableName, filters, sort, limit = 20
34
32
  limit: overrides.limit ?? limit,
35
33
  pageToken: overrides.pageToken ?? pageToken,
36
34
  offset: overrides.offset ?? offset,
37
- sortBy: overrides.sortBy ?? sortBy,
38
- order: overrides.order ?? order,
39
- params: overrides.params ?? stableParams,
40
35
  });
41
36
  if (signal?.aborted)
42
37
  return [];
@@ -62,12 +57,9 @@ export function useRecords({ client, podId, tableName, filters, sort, limit = 20
62
57
  isEnabled,
63
58
  limit,
64
59
  offset,
65
- order,
66
60
  pageToken,
67
61
  podId,
68
- sortBy,
69
62
  stableFilters,
70
- stableParams,
71
63
  stableSort,
72
64
  trimmedTableName,
73
65
  ]);
@@ -88,9 +80,6 @@ export function useRecords({ client, podId, tableName, filters, sort, limit = 20
88
80
  limit: overrides.limit ?? limit,
89
81
  pageToken: overrides.pageToken ?? nextPageToken ?? undefined,
90
82
  offset: overrides.offset ?? (nextPageToken ? undefined : (offset ?? 0) + loadedCount),
91
- sortBy: overrides.sortBy ?? sortBy,
92
- order: overrides.order ?? order,
93
- params: overrides.params ?? stableParams,
94
83
  });
95
84
  const moreRecords = (response.items ?? []);
96
85
  setRecords((previous) => [...previous, ...moreRecords]);
@@ -106,7 +95,7 @@ export function useRecords({ client, podId, tableName, filters, sort, limit = 20
106
95
  finally {
107
96
  setIsLoadingMore(false);
108
97
  }
109
- }, [client, isEnabled, isLoading, isLoadingMore, limit, nextPageToken, offset, order, podId, records.length, sortBy, stableFilters, stableParams, stableSort, total, trimmedTableName]);
98
+ }, [client, isEnabled, isLoading, isLoadingMore, limit, nextPageToken, offset, podId, records.length, stableFilters, stableSort, total, trimmedTableName]);
110
99
  useEffect(() => {
111
100
  if (!isEnabled) {
112
101
  setRecords([]);
@@ -1,5 +1,5 @@
1
1
  import type { LemmaClient } from "../client.js";
2
- import type { Table } from "../types.js";
2
+ import type { RecordSort, Table } from "../types.js";
3
3
  /**
4
4
  * React hook for fetching records from a referencing table that point back
5
5
  * to a specific record via a foreign key.
@@ -25,8 +25,7 @@ import type { Table } from "../types.js";
25
25
  * table: "issue_history",
26
26
  * foreignKey: "issue_id",
27
27
  * recordId: selectedIssueId,
28
- * sortBy: "created_at",
29
- * order: "desc",
28
+ * sort: [{ field: "created_at", direction: "desc" }],
30
29
  * });
31
30
  * ```
32
31
  */
@@ -48,8 +47,7 @@ export interface UseReferencingRecordsOptions {
48
47
  fields?: string[];
49
48
  limit?: number;
50
49
  offset?: number;
51
- sortBy?: string;
52
- order?: "asc" | "desc" | string;
50
+ sort?: RecordSort[];
53
51
  enabled?: boolean;
54
52
  autoLoad?: boolean;
55
53
  }
@@ -63,4 +61,4 @@ export interface UseReferencingRecordsResult<TRow extends Record<string, unknown
63
61
  error: Error | null;
64
62
  refresh: () => Promise<TRow[]>;
65
63
  }
66
- export declare function useReferencingRecords<TRow extends Record<string, unknown> = Record<string, unknown>>({ client, podId, table, foreignKey, recordId, fields, limit, offset, sortBy, order, enabled, autoLoad, }: UseReferencingRecordsOptions): UseReferencingRecordsResult<TRow>;
64
+ export declare function useReferencingRecords<TRow extends Record<string, unknown> = Record<string, unknown>>({ client, podId, table, foreignKey, recordId, fields, limit, offset, sort, enabled, autoLoad, }: UseReferencingRecordsOptions): UseReferencingRecordsResult<TRow>;
@@ -25,7 +25,7 @@ function pickDefaultFields(table, foreignKey) {
25
25
  });
26
26
  return next.slice(0, 6);
27
27
  }
28
- export function useReferencingRecords({ client, podId, table, foreignKey, recordId = null, fields, limit = 20, offset, sortBy, order, enabled = true, autoLoad = true, }) {
28
+ export function useReferencingRecords({ client, podId, table, foreignKey, recordId = null, fields, limit = 20, offset, sort, enabled = true, autoLoad = true, }) {
29
29
  const [referencedTable, setReferencedTable] = useState(null);
30
30
  const [columns, setColumns] = useState([]);
31
31
  const [records, setRecords] = useState([]);
@@ -36,7 +36,9 @@ export function useReferencingRecords({ client, podId, table, foreignKey, record
36
36
  const trimmedTable = table.trim();
37
37
  const trimmedRecordId = typeof recordId === "string" ? recordId.trim() : "";
38
38
  const fieldsKey = stringifyComparable(fields);
39
+ const sortKey = stringifyComparable(sort);
39
40
  const stableFields = useMemo(() => fields, [fieldsKey]);
41
+ const stableSort = useMemo(() => sort, [sortKey]);
40
42
  const isEnabled = enabled && trimmedTable.length > 0 && trimmedRecordId.length > 0;
41
43
  const refresh = useCallback(async (signal) => {
42
44
  if (!isEnabled) {
@@ -67,8 +69,7 @@ export function useReferencingRecords({ client, podId, table, foreignKey, record
67
69
  }],
68
70
  limit,
69
71
  offset,
70
- sortBy,
71
- order,
72
+ sort: stableSort,
72
73
  });
73
74
  if (signal?.aborted)
74
75
  return [];
@@ -100,10 +101,9 @@ export function useReferencingRecords({ client, podId, table, foreignKey, record
100
101
  isEnabled,
101
102
  limit,
102
103
  offset,
103
- order,
104
104
  podId,
105
- sortBy,
106
105
  stableFields,
106
+ stableSort,
107
107
  trimmedRecordId,
108
108
  trimmedTable,
109
109
  ]);
@@ -1,5 +1,5 @@
1
1
  import type { LemmaClient } from "../client.js";
2
- import type { Table } from "../types.js";
2
+ import type { RecordSort, Table } from "../types.js";
3
3
  /**
4
4
  * React hook for finding records in *other* tables that reference a given
5
5
  * record. Starts from the parent table, discovers all tables with FK
@@ -45,8 +45,7 @@ export interface UseReverseRelatedRecordsOptions {
45
45
  fields?: string[];
46
46
  limit?: number;
47
47
  offset?: number;
48
- sortBy?: string;
49
- order?: "asc" | "desc" | string;
48
+ sort?: RecordSort[];
50
49
  tablesLimit?: number;
51
50
  enabled?: boolean;
52
51
  autoLoad?: boolean;
@@ -65,4 +64,4 @@ export interface UseReverseRelatedRecordsResult<TRow extends Record<string, unkn
65
64
  error: Error | null;
66
65
  refresh: () => Promise<TRow[]>;
67
66
  }
68
- export declare function useReverseRelatedRecords<TRow extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, recordId, relation, fields, limit, offset, sortBy, order, tablesLimit, enabled, autoLoad, }: UseReverseRelatedRecordsOptions): UseReverseRelatedRecordsResult<TRow>;
67
+ export declare function useReverseRelatedRecords<TRow extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, recordId, relation, fields, limit, offset, sort, tablesLimit, enabled, autoLoad, }: UseReverseRelatedRecordsOptions): UseReverseRelatedRecordsResult<TRow>;
@@ -26,7 +26,7 @@ function pickDefaultFields(table, foreignKey) {
26
26
  });
27
27
  return next.slice(0, 6);
28
28
  }
29
- export function useReverseRelatedRecords({ client, podId, tableName, recordId = null, relation = null, fields, limit = 20, offset, sortBy, order, tablesLimit = 100, enabled = true, autoLoad = true, }) {
29
+ export function useReverseRelatedRecords({ client, podId, tableName, recordId = null, relation = null, fields, limit = 20, offset, sort, tablesLimit = 100, enabled = true, autoLoad = true, }) {
30
30
  const [parentTable, setParentTable] = useState(null);
31
31
  const [relatedTable, setRelatedTable] = useState(null);
32
32
  const [parentRecord, setParentRecord] = useState(null);
@@ -42,8 +42,10 @@ export function useReverseRelatedRecords({ client, podId, tableName, recordId =
42
42
  const trimmedRecordId = typeof recordId === "string" ? recordId.trim() : "";
43
43
  const relationKey = stringifyComparable(relation);
44
44
  const fieldsKey = stringifyComparable(fields);
45
+ const sortKey = stringifyComparable(sort);
45
46
  const stableRelation = useMemo(() => relation, [relationKey]);
46
47
  const stableFields = useMemo(() => fields, [fieldsKey]);
48
+ const stableSort = useMemo(() => sort, [sortKey]);
47
49
  const isEnabled = enabled && trimmedTableName.length > 0 && trimmedRecordId.length > 0;
48
50
  const refresh = useCallback(async (signal) => {
49
51
  if (!isEnabled) {
@@ -130,8 +132,7 @@ export function useReverseRelatedRecords({ client, podId, tableName, recordId =
130
132
  }],
131
133
  limit,
132
134
  offset,
133
- sortBy,
134
- order,
135
+ sort: stableSort,
135
136
  });
136
137
  if (signal?.aborted)
137
138
  return [];
@@ -162,11 +163,10 @@ export function useReverseRelatedRecords({ client, podId, tableName, recordId =
162
163
  isEnabled,
163
164
  limit,
164
165
  offset,
165
- order,
166
166
  podId,
167
- sortBy,
168
167
  stableFields,
169
168
  stableRelation,
169
+ stableSort,
170
170
  tablesLimit,
171
171
  trimmedRecordId,
172
172
  trimmedTableName,
@@ -12,4 +12,28 @@ export declare function isTerminalFlowStatus(status: unknown, options?: {
12
12
  }): boolean;
13
13
  export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
14
14
  export declare function nextBackoffDelay(attempt: number, options?: BackoffOptions): number;
15
+ /**
16
+ * Statuses worth retrying with backoff. Deliberately conservative: 429 is an
17
+ * explicit "back off", and 502/503/504 are gateway errors where the request
18
+ * usually never reached the handler — so retrying is safe even for writes.
19
+ * 500 is excluded (it may indicate a partial side effect).
20
+ */
21
+ export declare const RETRYABLE_STATUS: ReadonlySet<number>;
22
+ /** Parse a server `Retry-After` header (delta-seconds or HTTP-date) into ms,
23
+ * capped at 30s. Returns null when the header is absent or unparseable. */
24
+ export declare function serverRetryAfterMs(retryAfter: string | null | undefined): number | null;
25
+ /** Resolve a backoff delay, honoring a server `Retry-After` header when present. */
26
+ export declare function retryAfterMs(retryAfter: string | null | undefined, attempt: number): number;
27
+ /** Equal jitter: keep half the delay fixed and randomize the other half, so the
28
+ * result lands in `[delay/2, delay]`. Spreads correlated retries (thundering
29
+ * herd) without ever waiting longer than the computed backoff. */
30
+ export declare function applyJitter(delayMs: number, random?: () => number): number;
31
+ /**
32
+ * Single source of truth for the retry decision shared by the hand-written
33
+ * `HttpClient` and the generated-client adapter. Returns the number of ms to
34
+ * wait before retrying, or `null` when the status is non-retryable or retries
35
+ * are exhausted. A server-advised `Retry-After` is honored verbatim (no jitter);
36
+ * the computed exponential backoff gets equal jitter.
37
+ */
38
+ export declare function retryDelayForStatus(status: number, attempt: number, maxRetries: number, retryAfterHeader: string | null | undefined, random?: () => number): number | null;
15
39
  export {};
package/dist/run-utils.js CHANGED
@@ -50,3 +50,57 @@ export function nextBackoffDelay(attempt, options = {}) {
50
50
  const delay = Math.round(baseMs * Math.pow(factor, safeAttempt));
51
51
  return Math.min(Math.max(baseMs, delay), maxMs);
52
52
  }
53
+ /**
54
+ * Statuses worth retrying with backoff. Deliberately conservative: 429 is an
55
+ * explicit "back off", and 502/503/504 are gateway errors where the request
56
+ * usually never reached the handler — so retrying is safe even for writes.
57
+ * 500 is excluded (it may indicate a partial side effect).
58
+ */
59
+ export const RETRYABLE_STATUS = new Set([429, 502, 503, 504]);
60
+ /** Parse a server `Retry-After` header (delta-seconds or HTTP-date) into ms,
61
+ * capped at 30s. Returns null when the header is absent or unparseable. */
62
+ export function serverRetryAfterMs(retryAfter) {
63
+ if (!retryAfter) {
64
+ return null;
65
+ }
66
+ const seconds = Number(retryAfter);
67
+ if (Number.isFinite(seconds) && seconds >= 0) {
68
+ return Math.min(seconds * 1000, 30_000);
69
+ }
70
+ const dateMs = Date.parse(retryAfter);
71
+ if (!Number.isNaN(dateMs)) {
72
+ return Math.max(0, Math.min(dateMs - Date.now(), 30_000));
73
+ }
74
+ return null;
75
+ }
76
+ /** Resolve a backoff delay, honoring a server `Retry-After` header when present. */
77
+ export function retryAfterMs(retryAfter, attempt) {
78
+ return serverRetryAfterMs(retryAfter) ?? nextBackoffDelay(attempt);
79
+ }
80
+ /** Equal jitter: keep half the delay fixed and randomize the other half, so the
81
+ * result lands in `[delay/2, delay]`. Spreads correlated retries (thundering
82
+ * herd) without ever waiting longer than the computed backoff. */
83
+ export function applyJitter(delayMs, random = Math.random) {
84
+ if (!Number.isFinite(delayMs) || delayMs <= 0) {
85
+ return 0;
86
+ }
87
+ const half = delayMs / 2;
88
+ return Math.round(half + random() * half);
89
+ }
90
+ /**
91
+ * Single source of truth for the retry decision shared by the hand-written
92
+ * `HttpClient` and the generated-client adapter. Returns the number of ms to
93
+ * wait before retrying, or `null` when the status is non-retryable or retries
94
+ * are exhausted. A server-advised `Retry-After` is honored verbatim (no jitter);
95
+ * the computed exponential backoff gets equal jitter.
96
+ */
97
+ export function retryDelayForStatus(status, attempt, maxRetries, retryAfterHeader, random = Math.random) {
98
+ if (!RETRYABLE_STATUS.has(status) || attempt >= maxRetries) {
99
+ return null;
100
+ }
101
+ const serverMs = serverRetryAfterMs(retryAfterHeader);
102
+ if (serverMs !== null) {
103
+ return serverMs;
104
+ }
105
+ return applyJitter(nextBackoffDelay(attempt), random);
106
+ }
package/dist/types.d.ts CHANGED
@@ -38,9 +38,6 @@ export interface ListRecordsOptions {
38
38
  limit?: number;
39
39
  pageToken?: string;
40
40
  offset?: number;
41
- sortBy?: string;
42
- order?: "asc" | "desc" | string;
43
- params?: Record<string, string | number | boolean | undefined | null>;
44
41
  }
45
42
  export interface RunFunctionOptions {
46
43
  /** Input payload for the function */
@@ -62,17 +59,26 @@ export type Conversation = GeneratedConversationResponse & {
62
59
  model?: ConversationModel | null;
63
60
  status?: string | null;
64
61
  };
62
+ /** Discriminator for the flat message shape (replaces the old nested content union). */
63
+ export type MessageKind = "text" | "thinking" | "notification" | "tool_call" | "tool_return";
65
64
  export interface ConversationMessageResponse {
66
65
  id: string;
67
66
  role: string;
68
- content: unknown;
67
+ kind: MessageKind;
68
+ /** Body for text / thinking / notification messages. */
69
+ text?: string | null;
70
+ /** Set on tool_call / tool_return messages. */
71
+ tool_name?: string | null;
72
+ tool_call_id?: string | null;
73
+ /** Inputs for a tool_call (arbitrary JSON). */
74
+ tool_args?: unknown;
75
+ /** Output for a tool_return (arbitrary JSON). */
76
+ tool_result?: unknown;
69
77
  created_at: string;
70
78
  conversation_id?: string;
71
79
  sequence?: number;
72
80
  agent_run_id?: string | null;
73
81
  metadata?: Record<string, unknown> | null;
74
- tool_call_id?: string | null;
75
- tool_name?: string | null;
76
82
  }
77
83
  export type ConversationMessage = ConversationMessageResponse;
78
84
  export type FunctionRun = FunctionRunResponse;
@@ -0,0 +1,5 @@
1
+ export declare const SDK_VERSION = "0.3.0";
2
+ /** Sent as `X-Lemma-Client` on every request so the backend can log which client
3
+ * + version hit an endpoint (User-Agent is a forbidden header in browser fetch). */
4
+ export declare const CLIENT_HEADER_NAME = "X-Lemma-Client";
5
+ export declare const CLIENT_HEADER_VALUE = "lemma-sdk-ts/0.3.0";
@@ -0,0 +1,7 @@
1
+ // Keep SDK_VERSION in sync with package.json "version". The CI codegen/drift
2
+ // gate (workstream A) asserts they match so this can't silently drift.
3
+ export const SDK_VERSION = "0.3.0";
4
+ /** Sent as `X-Lemma-Client` on every request so the backend can log which client
5
+ * + version hit an endpoint (User-Agent is a forbidden header in browser fetch). */
6
+ export const CLIENT_HEADER_NAME = "X-Lemma-Client";
7
+ export const CLIENT_HEADER_VALUE = `lemma-sdk-ts/${SDK_VERSION}`;
package/package.json CHANGED
@@ -1,7 +1,9 @@
1
1
  {
2
2
  "name": "lemma-sdk",
3
- "version": "0.2.45",
3
+ "version": "0.3.0",
4
4
  "description": "Official TypeScript SDK for Lemma APIs, optimized around pod-scoped workflows",
5
+ "license": "Apache-2.0",
6
+ "author": "Lemma",
5
7
  "type": "module",
6
8
  "main": "dist/index.js",
7
9
  "types": "dist/index.d.ts",
@@ -44,9 +46,9 @@
44
46
  ],
45
47
  "scripts": {
46
48
  "generate:client": "./scripts/generate_openapi_client.sh",
47
- "generate:client:hey": "LEMMA_TS_GENERATOR=hey ./scripts/generate_openapi_client.sh",
48
49
  "build": "npm run clean && tsc -p tsconfig.json && npm run build:bundle",
49
- "build:bundle": "tsc -p tsconfig.bundle.json && node ./scripts/build_browser_bundle.mjs",
50
+ "build:bundle": "node ./scripts/build_browser_bundle.mjs",
51
+ "test": "npm run build:bundle && vitest run",
50
52
  "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })\"",
51
53
  "registry:build": "shadcn build ./registry.json -o ./public/r",
52
54
  "registry:check": "npm run registry:build && node ./scripts/check_registry_blocks.mjs",
@@ -65,10 +67,13 @@
65
67
  "supertokens-web-js": "^0.16.0"
66
68
  },
67
69
  "devDependencies": {
68
- "@hey-api/openapi-ts": "^0.73.0",
70
+ "@types/node": "^24.13.2",
69
71
  "@types/react": "^19.2.14",
72
+ "esbuild": "^0.28.1",
73
+ "jsdom": "^24.1.1",
70
74
  "openapi-typescript-codegen": "^0.29.0",
71
75
  "shadcn": "^4.2.0",
72
- "typescript": "^5.6.3"
76
+ "typescript": "^5.6.3",
77
+ "vitest": "^2.0.5"
73
78
  }
74
79
  }
@@ -1,2 +0,0 @@
1
- import type { Client, Config } from './types.gen';
2
- export declare const createClient: (config?: Config) => Client;
@@ -1,216 +0,0 @@
1
- // This file is auto-generated by @hey-api/openapi-ts
2
- import { createSseClient } from '../core/serverSentEvents.gen';
3
- import { getValidRequestBody } from '../core/utils.gen';
4
- import { buildUrl, createConfig, createInterceptors, getParseAs, mergeConfigs, mergeHeaders, setAuthParams, } from './utils.gen';
5
- export const createClient = (config = {}) => {
6
- let _config = mergeConfigs(createConfig(), config);
7
- const getConfig = () => ({ ..._config });
8
- const setConfig = (config) => {
9
- _config = mergeConfigs(_config, config);
10
- return getConfig();
11
- };
12
- const interceptors = createInterceptors();
13
- const beforeRequest = async (options) => {
14
- const opts = {
15
- ..._config,
16
- ...options,
17
- fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
18
- headers: mergeHeaders(_config.headers, options.headers),
19
- serializedBody: undefined,
20
- };
21
- if (opts.security) {
22
- await setAuthParams(opts);
23
- }
24
- if (opts.requestValidator) {
25
- await opts.requestValidator(opts);
26
- }
27
- if (opts.body !== undefined && opts.bodySerializer) {
28
- opts.serializedBody = opts.bodySerializer(opts.body);
29
- }
30
- // remove Content-Type header if body is empty to avoid sending invalid requests
31
- if (opts.body === undefined || opts.serializedBody === '') {
32
- opts.headers.delete('Content-Type');
33
- }
34
- const resolvedOpts = opts;
35
- const url = buildUrl(resolvedOpts);
36
- return { opts: resolvedOpts, url };
37
- };
38
- const request = async (options) => {
39
- const throwOnError = options.throwOnError ?? _config.throwOnError;
40
- const responseStyle = options.responseStyle ?? _config.responseStyle;
41
- let request;
42
- let response;
43
- try {
44
- const { opts, url } = await beforeRequest(options);
45
- const requestInit = {
46
- redirect: 'follow',
47
- ...opts,
48
- body: getValidRequestBody(opts),
49
- };
50
- request = new Request(url, requestInit);
51
- for (const fn of interceptors.request.fns) {
52
- if (fn) {
53
- request = await fn(request, opts);
54
- }
55
- }
56
- // fetch must be assigned here, otherwise it would throw the error:
57
- // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
58
- const _fetch = opts.fetch;
59
- response = await _fetch(request);
60
- for (const fn of interceptors.response.fns) {
61
- if (fn) {
62
- response = await fn(response, request, opts);
63
- }
64
- }
65
- const result = {
66
- request,
67
- response,
68
- };
69
- if (response.ok) {
70
- const parseAs = (opts.parseAs === 'auto'
71
- ? getParseAs(response.headers.get('Content-Type'))
72
- : opts.parseAs) ?? 'json';
73
- if (response.status === 204 || response.headers.get('Content-Length') === '0') {
74
- let emptyData;
75
- switch (parseAs) {
76
- case 'arrayBuffer':
77
- case 'blob':
78
- case 'text':
79
- emptyData = await response[parseAs]();
80
- break;
81
- case 'formData':
82
- emptyData = new FormData();
83
- break;
84
- case 'stream':
85
- emptyData = response.body;
86
- break;
87
- case 'json':
88
- default:
89
- emptyData = {};
90
- break;
91
- }
92
- return opts.responseStyle === 'data'
93
- ? emptyData
94
- : {
95
- data: emptyData,
96
- ...result,
97
- };
98
- }
99
- let data;
100
- switch (parseAs) {
101
- case 'arrayBuffer':
102
- case 'blob':
103
- case 'formData':
104
- case 'text':
105
- data = await response[parseAs]();
106
- break;
107
- case 'json': {
108
- // Some servers return 200 with no Content-Length and empty body.
109
- // response.json() would throw; read as text and parse if non-empty.
110
- const text = await response.text();
111
- data = text ? JSON.parse(text) : {};
112
- break;
113
- }
114
- case 'stream':
115
- return opts.responseStyle === 'data'
116
- ? response.body
117
- : {
118
- data: response.body,
119
- ...result,
120
- };
121
- }
122
- if (parseAs === 'json') {
123
- if (opts.responseValidator) {
124
- await opts.responseValidator(data);
125
- }
126
- if (opts.responseTransformer) {
127
- data = await opts.responseTransformer(data);
128
- }
129
- }
130
- return opts.responseStyle === 'data'
131
- ? data
132
- : {
133
- data,
134
- ...result,
135
- };
136
- }
137
- const textError = await response.text();
138
- let jsonError;
139
- try {
140
- jsonError = JSON.parse(textError);
141
- }
142
- catch {
143
- // noop
144
- }
145
- throw jsonError ?? textError;
146
- }
147
- catch (error) {
148
- let finalError = error;
149
- for (const fn of interceptors.error.fns) {
150
- if (fn) {
151
- finalError = await fn(finalError, response, request, options);
152
- }
153
- }
154
- finalError = finalError || {};
155
- if (throwOnError) {
156
- throw finalError;
157
- }
158
- // TODO: we probably want to return error and improve types
159
- return responseStyle === 'data'
160
- ? undefined
161
- : {
162
- error: finalError,
163
- request,
164
- response,
165
- };
166
- }
167
- };
168
- const makeMethodFn = (method) => (options) => request({ ...options, method });
169
- const makeSseFn = (method) => async (options) => {
170
- const { opts, url } = await beforeRequest(options);
171
- return createSseClient({
172
- ...opts,
173
- body: opts.body,
174
- method,
175
- onRequest: async (url, init) => {
176
- let request = new Request(url, init);
177
- for (const fn of interceptors.request.fns) {
178
- if (fn) {
179
- request = await fn(request, opts);
180
- }
181
- }
182
- return request;
183
- },
184
- serializedBody: getValidRequestBody(opts),
185
- url,
186
- });
187
- };
188
- const _buildUrl = (options) => buildUrl({ ..._config, ...options });
189
- return {
190
- buildUrl: _buildUrl,
191
- connect: makeMethodFn('CONNECT'),
192
- delete: makeMethodFn('DELETE'),
193
- get: makeMethodFn('GET'),
194
- getConfig,
195
- head: makeMethodFn('HEAD'),
196
- interceptors,
197
- options: makeMethodFn('OPTIONS'),
198
- patch: makeMethodFn('PATCH'),
199
- post: makeMethodFn('POST'),
200
- put: makeMethodFn('PUT'),
201
- request,
202
- setConfig,
203
- sse: {
204
- connect: makeSseFn('CONNECT'),
205
- delete: makeSseFn('DELETE'),
206
- get: makeSseFn('GET'),
207
- head: makeSseFn('HEAD'),
208
- options: makeSseFn('OPTIONS'),
209
- patch: makeSseFn('PATCH'),
210
- post: makeSseFn('POST'),
211
- put: makeSseFn('PUT'),
212
- trace: makeSseFn('TRACE'),
213
- },
214
- trace: makeMethodFn('TRACE'),
215
- };
216
- };
@@ -1,10 +0,0 @@
1
- export type { Auth } from '../core/auth.gen';
2
- export type { QuerySerializerOptions } from '../core/bodySerializer.gen';
3
- export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen';
4
- export { buildClientParams } from '../core/params.gen';
5
- export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen';
6
- export type { ServerSentEventsResult } from '../core/serverSentEvents.gen';
7
- export type { ClientMeta } from '../core/types.gen';
8
- export { createClient } from './client.gen';
9
- export type { Client, ClientOptions, Config, CreateClientConfig, Options, RequestOptions, RequestResult, ResolvedRequestOptions, ResponseStyle, TDataShape, } from './types.gen';
10
- export { createConfig, mergeHeaders } from './utils.gen';
@@ -1,6 +0,0 @@
1
- // This file is auto-generated by @hey-api/openapi-ts
2
- export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen';
3
- export { buildClientParams } from '../core/params.gen';
4
- export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen';
5
- export { createClient } from './client.gen';
6
- export { createConfig, mergeHeaders } from './utils.gen';