deepline 0.1.76 → 0.1.77

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/cli/index.js CHANGED
@@ -229,10 +229,10 @@ var import_node_path2 = require("path");
229
229
 
230
230
  // src/release.ts
231
231
  var SDK_RELEASE = {
232
- version: "0.1.76",
232
+ version: "0.1.77",
233
233
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
234
234
  supportPolicy: {
235
- latest: "0.1.76",
235
+ latest: "0.1.77",
236
236
  minimumSupported: "0.1.53",
237
237
  deprecatedBelow: "0.1.53"
238
238
  }
@@ -4190,13 +4190,18 @@ function registerDbCommands(program) {
4190
4190
  "after",
4191
4191
  `
4192
4192
  Notes:
4193
- Runs SQL against the active workspace customer database through Deepline APIs.
4193
+ Agent-safe SQL for the active workspace customer database.
4194
+ Reads: SELECT, EXPLAIN, and read-only WITH can inspect permitted schemas.
4195
+ Writes: CREATE TABLE, INSERT, UPDATE, DELETE, ALTER, DROP, TRUNCATE, and
4196
+ CREATE INDEX must target schema-qualified storage tables, such as storage.agent_notes.
4197
+ If CREATE TABLE blocked (...) fails, use CREATE TABLE storage.blocked (...).
4194
4198
  Results are bounded by the server and --max-rows. Use --json for stable output.
4195
4199
  Use --format csv or --format markdown for agent-readable exports and display tables.
4196
4200
 
4197
4201
  Examples:
4198
4202
  deepline db query --sql "select * from companies limit 20"
4199
4203
  deepline db query --sql "select domain, name from companies limit 20" --json
4204
+ deepline db query --sql "create table if not exists storage.agent_notes (id text primary key, note text not null)"
4200
4205
  deepline db query --sql "select * from contacts" --max-rows 100 --json
4201
4206
  deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
4202
4207
  deepline db query --sql "select domain, name from companies limit 20" --format markdown
@@ -4208,12 +4213,16 @@ Examples:
4208
4213
  Notes:
4209
4214
  Requires --sql. Output is a compact table in a terminal and raw JSON with
4210
4215
  --json or when stdout is piped. The active auth workspace determines scope.
4216
+ Read permitted schemas with SELECT, EXPLAIN, or read-only WITH.
4217
+ Write only to schema-qualified storage tables. For example, use
4218
+ CREATE TABLE storage.blocked (...) instead of CREATE TABLE blocked (...).
4211
4219
  --format csv and --format markdown are explicit data/display formats and can
4212
4220
  be written directly with --out.
4213
4221
 
4214
4222
  Examples:
4215
4223
  deepline db query --sql "select * from companies limit 20"
4216
4224
  deepline db query --sql "select domain, name from companies limit 20" --json
4225
+ deepline db query --sql "create table if not exists storage.agent_notes (id text primary key, note text not null)"
4217
4226
  deepline db psql --sql "select count(*) from contacts" --json
4218
4227
  deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
4219
4228
  deepline db query --sql "select domain, name from companies limit 20" --format markdown
@@ -206,10 +206,10 @@ import { join as join2 } from "path";
206
206
 
207
207
  // src/release.ts
208
208
  var SDK_RELEASE = {
209
- version: "0.1.76",
209
+ version: "0.1.77",
210
210
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
211
211
  supportPolicy: {
212
- latest: "0.1.76",
212
+ latest: "0.1.77",
213
213
  minimumSupported: "0.1.53",
214
214
  deprecatedBelow: "0.1.53"
215
215
  }
@@ -4173,13 +4173,18 @@ function registerDbCommands(program) {
4173
4173
  "after",
4174
4174
  `
4175
4175
  Notes:
4176
- Runs SQL against the active workspace customer database through Deepline APIs.
4176
+ Agent-safe SQL for the active workspace customer database.
4177
+ Reads: SELECT, EXPLAIN, and read-only WITH can inspect permitted schemas.
4178
+ Writes: CREATE TABLE, INSERT, UPDATE, DELETE, ALTER, DROP, TRUNCATE, and
4179
+ CREATE INDEX must target schema-qualified storage tables, such as storage.agent_notes.
4180
+ If CREATE TABLE blocked (...) fails, use CREATE TABLE storage.blocked (...).
4177
4181
  Results are bounded by the server and --max-rows. Use --json for stable output.
4178
4182
  Use --format csv or --format markdown for agent-readable exports and display tables.
4179
4183
 
4180
4184
  Examples:
4181
4185
  deepline db query --sql "select * from companies limit 20"
4182
4186
  deepline db query --sql "select domain, name from companies limit 20" --json
4187
+ deepline db query --sql "create table if not exists storage.agent_notes (id text primary key, note text not null)"
4183
4188
  deepline db query --sql "select * from contacts" --max-rows 100 --json
4184
4189
  deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
4185
4190
  deepline db query --sql "select domain, name from companies limit 20" --format markdown
@@ -4191,12 +4196,16 @@ Examples:
4191
4196
  Notes:
4192
4197
  Requires --sql. Output is a compact table in a terminal and raw JSON with
4193
4198
  --json or when stdout is piped. The active auth workspace determines scope.
4199
+ Read permitted schemas with SELECT, EXPLAIN, or read-only WITH.
4200
+ Write only to schema-qualified storage tables. For example, use
4201
+ CREATE TABLE storage.blocked (...) instead of CREATE TABLE blocked (...).
4194
4202
  --format csv and --format markdown are explicit data/display formats and can
4195
4203
  be written directly with --out.
4196
4204
 
4197
4205
  Examples:
4198
4206
  deepline db query --sql "select * from companies limit 20"
4199
4207
  deepline db query --sql "select domain, name from companies limit 20" --json
4208
+ deepline db query --sql "create table if not exists storage.agent_notes (id text primary key, note text not null)"
4200
4209
  deepline db psql --sql "select count(*) from contacts" --json
4201
4210
  deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
4202
4211
  deepline db query --sql "select domain, name from companies limit 20" --format markdown
package/dist/index.d.mts CHANGED
@@ -1997,6 +1997,37 @@ interface PlayDataset<T> extends AsyncIterable<T> {
1997
1997
  };
1998
1998
  }
1999
1999
 
2000
+ type EmailStatusVerdict = 'send' | 'send_with_caution' | 'verify_next' | 'hold' | 'drop';
2001
+ type EmailStatusValue = 'valid' | 'invalid' | 'catch_all' | 'valid_catch_all' | 'unknown' | 'do_not_mail' | 'spamtrap' | 'abuse' | 'disposable';
2002
+ type EmailStatusMapEntry = {
2003
+ status: EmailStatusValue;
2004
+ verdict?: EmailStatusVerdict;
2005
+ verified?: boolean;
2006
+ reason?: string;
2007
+ };
2008
+ type EmailStatusRule = EmailStatusMapEntry & {
2009
+ when: Record<string, string | number | boolean | null>;
2010
+ };
2011
+ type EmailStatusExtractorConfig = {
2012
+ provider: string;
2013
+ rawStatus?: string[];
2014
+ rawScore?: string[];
2015
+ valid?: string[];
2016
+ deliverability?: string[];
2017
+ catchAll?: string[];
2018
+ mxProvider?: string[];
2019
+ mxRecord?: string[];
2020
+ fraudScore?: string[];
2021
+ disposable?: string[];
2022
+ roleBased?: string[];
2023
+ freeEmail?: string[];
2024
+ abuse?: string[];
2025
+ spamtrap?: string[];
2026
+ suspect?: string[];
2027
+ statusMap?: Record<string, EmailStatusMapEntry>;
2028
+ rules?: EmailStatusRule[];
2029
+ };
2030
+
2000
2031
  type ToolResultExecutionMetadata = {
2001
2032
  idempotent: true;
2002
2033
  cached: boolean;
@@ -2017,6 +2048,7 @@ type ToolResultExtractorDescriptor = {
2017
2048
  transforms?: readonly string[];
2018
2049
  enum?: readonly string[];
2019
2050
  overrides?: readonly ToolResultExtractorOverride[];
2051
+ emailStatus?: EmailStatusExtractorConfig;
2020
2052
  };
2021
2053
  type ToolResultExtractorOverride = {
2022
2054
  paths: readonly string[];
package/dist/index.d.ts CHANGED
@@ -1997,6 +1997,37 @@ interface PlayDataset<T> extends AsyncIterable<T> {
1997
1997
  };
1998
1998
  }
1999
1999
 
2000
+ type EmailStatusVerdict = 'send' | 'send_with_caution' | 'verify_next' | 'hold' | 'drop';
2001
+ type EmailStatusValue = 'valid' | 'invalid' | 'catch_all' | 'valid_catch_all' | 'unknown' | 'do_not_mail' | 'spamtrap' | 'abuse' | 'disposable';
2002
+ type EmailStatusMapEntry = {
2003
+ status: EmailStatusValue;
2004
+ verdict?: EmailStatusVerdict;
2005
+ verified?: boolean;
2006
+ reason?: string;
2007
+ };
2008
+ type EmailStatusRule = EmailStatusMapEntry & {
2009
+ when: Record<string, string | number | boolean | null>;
2010
+ };
2011
+ type EmailStatusExtractorConfig = {
2012
+ provider: string;
2013
+ rawStatus?: string[];
2014
+ rawScore?: string[];
2015
+ valid?: string[];
2016
+ deliverability?: string[];
2017
+ catchAll?: string[];
2018
+ mxProvider?: string[];
2019
+ mxRecord?: string[];
2020
+ fraudScore?: string[];
2021
+ disposable?: string[];
2022
+ roleBased?: string[];
2023
+ freeEmail?: string[];
2024
+ abuse?: string[];
2025
+ spamtrap?: string[];
2026
+ suspect?: string[];
2027
+ statusMap?: Record<string, EmailStatusMapEntry>;
2028
+ rules?: EmailStatusRule[];
2029
+ };
2030
+
2000
2031
  type ToolResultExecutionMetadata = {
2001
2032
  idempotent: true;
2002
2033
  cached: boolean;
@@ -2017,6 +2048,7 @@ type ToolResultExtractorDescriptor = {
2017
2048
  transforms?: readonly string[];
2018
2049
  enum?: readonly string[];
2019
2050
  overrides?: readonly ToolResultExtractorOverride[];
2051
+ emailStatus?: EmailStatusExtractorConfig;
2020
2052
  };
2021
2053
  type ToolResultExtractorOverride = {
2022
2054
  paths: readonly string[];
package/dist/index.js CHANGED
@@ -241,10 +241,10 @@ var import_node_path2 = require("path");
241
241
 
242
242
  // src/release.ts
243
243
  var SDK_RELEASE = {
244
- version: "0.1.76",
244
+ version: "0.1.77",
245
245
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
246
246
  supportPolicy: {
247
- latest: "0.1.76",
247
+ latest: "0.1.77",
248
248
  minimumSupported: "0.1.53",
249
249
  deprecatedBelow: "0.1.53"
250
250
  }
@@ -1906,6 +1906,160 @@ function formatPlayBootstrapFinderKindsForSentence() {
1906
1906
  return `${allButLast.join(", ")} or ${last}`;
1907
1907
  }
1908
1908
 
1909
+ // ../shared_libs/play-runtime/email-status.ts
1910
+ var DEFAULT_STATUS_MAP = {
1911
+ verified: { status: "valid", verdict: "send", verified: true },
1912
+ valid: { status: "valid", verdict: "send", verified: true },
1913
+ deliverable: { status: "valid", verdict: "send", verified: true },
1914
+ true: { status: "valid", verdict: "send", verified: true },
1915
+ invalid: { status: "invalid", verdict: "drop", verified: false },
1916
+ undeliverable: { status: "invalid", verdict: "drop", verified: false },
1917
+ false: { status: "invalid", verdict: "drop", verified: false },
1918
+ "catch-all": {
1919
+ status: "catch_all",
1920
+ verdict: "verify_next",
1921
+ verified: false
1922
+ },
1923
+ catch_all: {
1924
+ status: "catch_all",
1925
+ verdict: "verify_next",
1926
+ verified: false
1927
+ },
1928
+ valid_catch_all: {
1929
+ status: "valid_catch_all",
1930
+ verdict: "send_with_caution",
1931
+ verified: true
1932
+ },
1933
+ accept_all: {
1934
+ status: "catch_all",
1935
+ verdict: "verify_next",
1936
+ verified: false
1937
+ },
1938
+ unknown: { status: "unknown", verdict: "hold", verified: false },
1939
+ unavailable: { status: "unknown", verdict: "hold", verified: false },
1940
+ do_not_mail: { status: "do_not_mail", verdict: "drop", verified: false },
1941
+ spamtrap: { status: "spamtrap", verdict: "drop", verified: false },
1942
+ abuse: { status: "abuse", verdict: "drop", verified: false },
1943
+ disposable: { status: "disposable", verdict: "drop", verified: false }
1944
+ };
1945
+ function normalizeKey(value) {
1946
+ if (value == null) return null;
1947
+ if (typeof value === "boolean") return String(value);
1948
+ const normalized = String(value).trim().toLowerCase().replace(/\s+/g, "_");
1949
+ return normalized || null;
1950
+ }
1951
+ function boolish(value) {
1952
+ if (typeof value === "boolean") return value;
1953
+ if (typeof value === "number") return value === 1 ? true : value === 0 ? false : null;
1954
+ if (typeof value !== "string") return null;
1955
+ const normalized = value.trim().toLowerCase();
1956
+ if (["true", "yes", "y", "1"].includes(normalized)) return true;
1957
+ if (["false", "no", "n", "0"].includes(normalized)) return false;
1958
+ return null;
1959
+ }
1960
+ function numberish(value) {
1961
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1962
+ if (typeof value !== "string" || value.trim() === "") return null;
1963
+ const parsed = Number(value);
1964
+ return Number.isFinite(parsed) ? parsed : null;
1965
+ }
1966
+ function stringish(value) {
1967
+ return typeof value === "string" && value.trim() ? value.trim() : null;
1968
+ }
1969
+ function deliverability(value) {
1970
+ const normalized = normalizeKey(value);
1971
+ return normalized === "high" || normalized === "medium" || normalized === "low" ? normalized : "unknown";
1972
+ }
1973
+ function mxClass(mxProvider, mxRecord) {
1974
+ const haystack = `${stringish(mxProvider) ?? ""} ${stringish(mxRecord) ?? ""}`.toLowerCase();
1975
+ if (!haystack.trim()) return "unknown";
1976
+ if (/proofpoint|pphosted|mimecast|barracuda|ess\.barracudanetworks|ironport|cisco|iphmx|messagelabs|symantec/.test(
1977
+ haystack
1978
+ )) {
1979
+ return "security_gateway";
1980
+ }
1981
+ if (/aspmx\.l\.google|google|g-suite|google workspace/.test(haystack)) {
1982
+ return "workspace_mailbox";
1983
+ }
1984
+ if (/protection\.outlook|office365|microsoft|outlook|exchange online/.test(haystack)) {
1985
+ return "workspace_mailbox";
1986
+ }
1987
+ if (/gmail|yahoo|icloud|aol|hotmail/.test(haystack)) return "consumer_mailbox";
1988
+ if (/postfix|exim|sendmail|zimbra|plesk|cpanel|mail\./.test(haystack)) return "on_prem";
1989
+ return "unknown";
1990
+ }
1991
+ function entryForStatus(key, map) {
1992
+ if (!key) return null;
1993
+ return map?.[key] ?? DEFAULT_STATUS_MAP[key] ?? null;
1994
+ }
1995
+ function read(values, name) {
1996
+ return values[name];
1997
+ }
1998
+ function matchesRule(rule, values) {
1999
+ return Object.entries(rule.when).every(([key, expected]) => {
2000
+ const actual = read(values, key);
2001
+ if (key.endsWith("Lt")) {
2002
+ const source = numberish(read(values, key.slice(0, -2)));
2003
+ return typeof expected === "number" && source != null && source < expected;
2004
+ }
2005
+ if (typeof expected === "boolean") return boolish(actual) === expected;
2006
+ if (typeof expected === "number") return numberish(actual) === expected;
2007
+ return normalizeKey(actual) === normalizeKey(expected);
2008
+ });
2009
+ }
2010
+ function buildEmailStatus({
2011
+ config,
2012
+ values
2013
+ }) {
2014
+ const rawStatus = read(values, "rawStatus");
2015
+ const rawScore = numberish(read(values, "rawScore"));
2016
+ const valid = boolish(read(values, "valid"));
2017
+ const catchAll = boolish(read(values, "catchAll"));
2018
+ const disposable = boolish(read(values, "disposable"));
2019
+ const abuse = boolish(read(values, "abuse"));
2020
+ const spamtrap = boolish(read(values, "spamtrap"));
2021
+ const suspect = boolish(read(values, "suspect"));
2022
+ const rawKey = normalizeKey(rawStatus);
2023
+ const mapped = config.rules?.find((rule) => matchesRule(rule, values)) ?? entryForStatus(rawKey, config.statusMap) ?? entryForStatus(valid == null ? null : String(valid), config.statusMap);
2024
+ const status = mapped?.status ?? (disposable ? "disposable" : abuse ? "abuse" : spamtrap ? "spamtrap" : catchAll ? "catch_all" : valid === true ? "valid" : valid === false ? "invalid" : "unknown");
2025
+ const defaultVerdict = status === "valid" ? "send" : status === "valid_catch_all" ? "send_with_caution" : status === "catch_all" ? "verify_next" : status === "unknown" ? "hold" : "drop";
2026
+ const verdict = mapped?.verdict ?? defaultVerdict;
2027
+ const verified = mapped?.verified ?? (status === "valid" || status === "valid_catch_all" || verdict === "send");
2028
+ const reasons = [
2029
+ mapped?.reason,
2030
+ catchAll ? "catch_all_domain" : null,
2031
+ mxClass(read(values, "mxProvider"), read(values, "mxRecord")) === "security_gateway" ? "security_gateway_mx" : null,
2032
+ suspect ? "provider_marked_suspect" : null
2033
+ ].filter((reason) => typeof reason === "string");
2034
+ return {
2035
+ verdict,
2036
+ status,
2037
+ verified,
2038
+ confidence: rawScore,
2039
+ reasons,
2040
+ signals: {
2041
+ catch_all: catchAll,
2042
+ deliverability: deliverability(read(values, "deliverability")),
2043
+ mx_class: mxClass(read(values, "mxProvider"), read(values, "mxRecord")),
2044
+ mx_provider: stringish(read(values, "mxProvider")),
2045
+ mx_record: stringish(read(values, "mxRecord")),
2046
+ fraud_score: numberish(read(values, "fraudScore")),
2047
+ disposable,
2048
+ role_based: boolish(read(values, "roleBased")),
2049
+ free_email: boolish(read(values, "freeEmail")),
2050
+ abuse,
2051
+ spamtrap,
2052
+ suspect,
2053
+ valid
2054
+ },
2055
+ provider: {
2056
+ name: config.provider,
2057
+ raw_status: typeof rawStatus === "string" || typeof rawStatus === "boolean" || typeof rawStatus === "number" ? rawStatus : null,
2058
+ raw_score: rawScore
2059
+ }
2060
+ };
2061
+ }
2062
+
1909
2063
  // ../shared_libs/play-runtime/tool-result.ts
1910
2064
  var TARGET_FALLBACK_KEYS = {
1911
2065
  email: [/^email$/i, /^address$/i, /email/i],
@@ -2089,6 +2243,42 @@ function findFirstTargetByPath(result, paths) {
2089
2243
  }
2090
2244
  return null;
2091
2245
  }
2246
+ function firstValueForPaths(result, paths) {
2247
+ return findFirstTargetByPath(result, paths);
2248
+ }
2249
+ function buildEmailStatusTarget(result, descriptor) {
2250
+ const config = descriptor.emailStatus;
2251
+ if (!config) return null;
2252
+ const values = {};
2253
+ const pathSets = {
2254
+ rawStatus: config.rawStatus,
2255
+ rawScore: config.rawScore,
2256
+ valid: config.valid,
2257
+ deliverability: config.deliverability,
2258
+ catchAll: config.catchAll,
2259
+ mxProvider: config.mxProvider,
2260
+ mxRecord: config.mxRecord,
2261
+ fraudScore: config.fraudScore,
2262
+ disposable: config.disposable,
2263
+ roleBased: config.roleBased,
2264
+ freeEmail: config.freeEmail,
2265
+ abuse: config.abuse,
2266
+ spamtrap: config.spamtrap,
2267
+ suspect: config.suspect
2268
+ };
2269
+ let firstPath = null;
2270
+ for (const [name, paths] of Object.entries(pathSets)) {
2271
+ const match = firstValueForPaths(result, paths);
2272
+ if (!match) continue;
2273
+ values[name] = match.value;
2274
+ firstPath ??= match.path;
2275
+ }
2276
+ if (!firstPath) return null;
2277
+ return {
2278
+ path: firstPath,
2279
+ value: buildEmailStatus({ config, values })
2280
+ };
2281
+ }
2092
2282
  function findFirstTargetByKey(result, target, depth = 0, path = []) {
2093
2283
  if (depth > 6) return null;
2094
2284
  if (Array.isArray(result)) {
@@ -2238,6 +2428,11 @@ function deriveListKeys(input) {
2238
2428
  function buildTargets(result, extractors, targetGetters) {
2239
2429
  const targets = {};
2240
2430
  for (const [target, descriptor] of Object.entries(extractors ?? {})) {
2431
+ const emailStatusTarget = buildEmailStatusTarget(result, descriptor);
2432
+ if (emailStatusTarget) {
2433
+ targets[target] = emailStatusTarget;
2434
+ continue;
2435
+ }
2241
2436
  const fromExtractor = findFirstTargetByPath(result, descriptor.paths);
2242
2437
  if (!fromExtractor) continue;
2243
2438
  const transformed = coerceToEnum(
package/dist/index.mjs CHANGED
@@ -179,10 +179,10 @@ import { join as join2 } from "path";
179
179
 
180
180
  // src/release.ts
181
181
  var SDK_RELEASE = {
182
- version: "0.1.76",
182
+ version: "0.1.77",
183
183
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
184
184
  supportPolicy: {
185
- latest: "0.1.76",
185
+ latest: "0.1.77",
186
186
  minimumSupported: "0.1.53",
187
187
  deprecatedBelow: "0.1.53"
188
188
  }
@@ -1844,6 +1844,160 @@ function formatPlayBootstrapFinderKindsForSentence() {
1844
1844
  return `${allButLast.join(", ")} or ${last}`;
1845
1845
  }
1846
1846
 
1847
+ // ../shared_libs/play-runtime/email-status.ts
1848
+ var DEFAULT_STATUS_MAP = {
1849
+ verified: { status: "valid", verdict: "send", verified: true },
1850
+ valid: { status: "valid", verdict: "send", verified: true },
1851
+ deliverable: { status: "valid", verdict: "send", verified: true },
1852
+ true: { status: "valid", verdict: "send", verified: true },
1853
+ invalid: { status: "invalid", verdict: "drop", verified: false },
1854
+ undeliverable: { status: "invalid", verdict: "drop", verified: false },
1855
+ false: { status: "invalid", verdict: "drop", verified: false },
1856
+ "catch-all": {
1857
+ status: "catch_all",
1858
+ verdict: "verify_next",
1859
+ verified: false
1860
+ },
1861
+ catch_all: {
1862
+ status: "catch_all",
1863
+ verdict: "verify_next",
1864
+ verified: false
1865
+ },
1866
+ valid_catch_all: {
1867
+ status: "valid_catch_all",
1868
+ verdict: "send_with_caution",
1869
+ verified: true
1870
+ },
1871
+ accept_all: {
1872
+ status: "catch_all",
1873
+ verdict: "verify_next",
1874
+ verified: false
1875
+ },
1876
+ unknown: { status: "unknown", verdict: "hold", verified: false },
1877
+ unavailable: { status: "unknown", verdict: "hold", verified: false },
1878
+ do_not_mail: { status: "do_not_mail", verdict: "drop", verified: false },
1879
+ spamtrap: { status: "spamtrap", verdict: "drop", verified: false },
1880
+ abuse: { status: "abuse", verdict: "drop", verified: false },
1881
+ disposable: { status: "disposable", verdict: "drop", verified: false }
1882
+ };
1883
+ function normalizeKey(value) {
1884
+ if (value == null) return null;
1885
+ if (typeof value === "boolean") return String(value);
1886
+ const normalized = String(value).trim().toLowerCase().replace(/\s+/g, "_");
1887
+ return normalized || null;
1888
+ }
1889
+ function boolish(value) {
1890
+ if (typeof value === "boolean") return value;
1891
+ if (typeof value === "number") return value === 1 ? true : value === 0 ? false : null;
1892
+ if (typeof value !== "string") return null;
1893
+ const normalized = value.trim().toLowerCase();
1894
+ if (["true", "yes", "y", "1"].includes(normalized)) return true;
1895
+ if (["false", "no", "n", "0"].includes(normalized)) return false;
1896
+ return null;
1897
+ }
1898
+ function numberish(value) {
1899
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1900
+ if (typeof value !== "string" || value.trim() === "") return null;
1901
+ const parsed = Number(value);
1902
+ return Number.isFinite(parsed) ? parsed : null;
1903
+ }
1904
+ function stringish(value) {
1905
+ return typeof value === "string" && value.trim() ? value.trim() : null;
1906
+ }
1907
+ function deliverability(value) {
1908
+ const normalized = normalizeKey(value);
1909
+ return normalized === "high" || normalized === "medium" || normalized === "low" ? normalized : "unknown";
1910
+ }
1911
+ function mxClass(mxProvider, mxRecord) {
1912
+ const haystack = `${stringish(mxProvider) ?? ""} ${stringish(mxRecord) ?? ""}`.toLowerCase();
1913
+ if (!haystack.trim()) return "unknown";
1914
+ if (/proofpoint|pphosted|mimecast|barracuda|ess\.barracudanetworks|ironport|cisco|iphmx|messagelabs|symantec/.test(
1915
+ haystack
1916
+ )) {
1917
+ return "security_gateway";
1918
+ }
1919
+ if (/aspmx\.l\.google|google|g-suite|google workspace/.test(haystack)) {
1920
+ return "workspace_mailbox";
1921
+ }
1922
+ if (/protection\.outlook|office365|microsoft|outlook|exchange online/.test(haystack)) {
1923
+ return "workspace_mailbox";
1924
+ }
1925
+ if (/gmail|yahoo|icloud|aol|hotmail/.test(haystack)) return "consumer_mailbox";
1926
+ if (/postfix|exim|sendmail|zimbra|plesk|cpanel|mail\./.test(haystack)) return "on_prem";
1927
+ return "unknown";
1928
+ }
1929
+ function entryForStatus(key, map) {
1930
+ if (!key) return null;
1931
+ return map?.[key] ?? DEFAULT_STATUS_MAP[key] ?? null;
1932
+ }
1933
+ function read(values, name) {
1934
+ return values[name];
1935
+ }
1936
+ function matchesRule(rule, values) {
1937
+ return Object.entries(rule.when).every(([key, expected]) => {
1938
+ const actual = read(values, key);
1939
+ if (key.endsWith("Lt")) {
1940
+ const source = numberish(read(values, key.slice(0, -2)));
1941
+ return typeof expected === "number" && source != null && source < expected;
1942
+ }
1943
+ if (typeof expected === "boolean") return boolish(actual) === expected;
1944
+ if (typeof expected === "number") return numberish(actual) === expected;
1945
+ return normalizeKey(actual) === normalizeKey(expected);
1946
+ });
1947
+ }
1948
+ function buildEmailStatus({
1949
+ config,
1950
+ values
1951
+ }) {
1952
+ const rawStatus = read(values, "rawStatus");
1953
+ const rawScore = numberish(read(values, "rawScore"));
1954
+ const valid = boolish(read(values, "valid"));
1955
+ const catchAll = boolish(read(values, "catchAll"));
1956
+ const disposable = boolish(read(values, "disposable"));
1957
+ const abuse = boolish(read(values, "abuse"));
1958
+ const spamtrap = boolish(read(values, "spamtrap"));
1959
+ const suspect = boolish(read(values, "suspect"));
1960
+ const rawKey = normalizeKey(rawStatus);
1961
+ const mapped = config.rules?.find((rule) => matchesRule(rule, values)) ?? entryForStatus(rawKey, config.statusMap) ?? entryForStatus(valid == null ? null : String(valid), config.statusMap);
1962
+ const status = mapped?.status ?? (disposable ? "disposable" : abuse ? "abuse" : spamtrap ? "spamtrap" : catchAll ? "catch_all" : valid === true ? "valid" : valid === false ? "invalid" : "unknown");
1963
+ const defaultVerdict = status === "valid" ? "send" : status === "valid_catch_all" ? "send_with_caution" : status === "catch_all" ? "verify_next" : status === "unknown" ? "hold" : "drop";
1964
+ const verdict = mapped?.verdict ?? defaultVerdict;
1965
+ const verified = mapped?.verified ?? (status === "valid" || status === "valid_catch_all" || verdict === "send");
1966
+ const reasons = [
1967
+ mapped?.reason,
1968
+ catchAll ? "catch_all_domain" : null,
1969
+ mxClass(read(values, "mxProvider"), read(values, "mxRecord")) === "security_gateway" ? "security_gateway_mx" : null,
1970
+ suspect ? "provider_marked_suspect" : null
1971
+ ].filter((reason) => typeof reason === "string");
1972
+ return {
1973
+ verdict,
1974
+ status,
1975
+ verified,
1976
+ confidence: rawScore,
1977
+ reasons,
1978
+ signals: {
1979
+ catch_all: catchAll,
1980
+ deliverability: deliverability(read(values, "deliverability")),
1981
+ mx_class: mxClass(read(values, "mxProvider"), read(values, "mxRecord")),
1982
+ mx_provider: stringish(read(values, "mxProvider")),
1983
+ mx_record: stringish(read(values, "mxRecord")),
1984
+ fraud_score: numberish(read(values, "fraudScore")),
1985
+ disposable,
1986
+ role_based: boolish(read(values, "roleBased")),
1987
+ free_email: boolish(read(values, "freeEmail")),
1988
+ abuse,
1989
+ spamtrap,
1990
+ suspect,
1991
+ valid
1992
+ },
1993
+ provider: {
1994
+ name: config.provider,
1995
+ raw_status: typeof rawStatus === "string" || typeof rawStatus === "boolean" || typeof rawStatus === "number" ? rawStatus : null,
1996
+ raw_score: rawScore
1997
+ }
1998
+ };
1999
+ }
2000
+
1847
2001
  // ../shared_libs/play-runtime/tool-result.ts
1848
2002
  var TARGET_FALLBACK_KEYS = {
1849
2003
  email: [/^email$/i, /^address$/i, /email/i],
@@ -2027,6 +2181,42 @@ function findFirstTargetByPath(result, paths) {
2027
2181
  }
2028
2182
  return null;
2029
2183
  }
2184
+ function firstValueForPaths(result, paths) {
2185
+ return findFirstTargetByPath(result, paths);
2186
+ }
2187
+ function buildEmailStatusTarget(result, descriptor) {
2188
+ const config = descriptor.emailStatus;
2189
+ if (!config) return null;
2190
+ const values = {};
2191
+ const pathSets = {
2192
+ rawStatus: config.rawStatus,
2193
+ rawScore: config.rawScore,
2194
+ valid: config.valid,
2195
+ deliverability: config.deliverability,
2196
+ catchAll: config.catchAll,
2197
+ mxProvider: config.mxProvider,
2198
+ mxRecord: config.mxRecord,
2199
+ fraudScore: config.fraudScore,
2200
+ disposable: config.disposable,
2201
+ roleBased: config.roleBased,
2202
+ freeEmail: config.freeEmail,
2203
+ abuse: config.abuse,
2204
+ spamtrap: config.spamtrap,
2205
+ suspect: config.suspect
2206
+ };
2207
+ let firstPath = null;
2208
+ for (const [name, paths] of Object.entries(pathSets)) {
2209
+ const match = firstValueForPaths(result, paths);
2210
+ if (!match) continue;
2211
+ values[name] = match.value;
2212
+ firstPath ??= match.path;
2213
+ }
2214
+ if (!firstPath) return null;
2215
+ return {
2216
+ path: firstPath,
2217
+ value: buildEmailStatus({ config, values })
2218
+ };
2219
+ }
2030
2220
  function findFirstTargetByKey(result, target, depth = 0, path = []) {
2031
2221
  if (depth > 6) return null;
2032
2222
  if (Array.isArray(result)) {
@@ -2176,6 +2366,11 @@ function deriveListKeys(input) {
2176
2366
  function buildTargets(result, extractors, targetGetters) {
2177
2367
  const targets = {};
2178
2368
  for (const [target, descriptor] of Object.entries(extractors ?? {})) {
2369
+ const emailStatusTarget = buildEmailStatusTarget(result, descriptor);
2370
+ if (emailStatusTarget) {
2371
+ targets[target] = emailStatusTarget;
2372
+ continue;
2373
+ }
2179
2374
  const fromExtractor = findFirstTargetByPath(result, descriptor.paths);
2180
2375
  if (!fromExtractor) continue;
2181
2376
  const transformed = coerceToEnum(
@@ -50,10 +50,10 @@ export type SdkRelease = {
50
50
  };
51
51
 
52
52
  export const SDK_RELEASE = {
53
- version: '0.1.76',
53
+ version: '0.1.77',
54
54
  apiContract: '2026-06-dataset-column-cell-stale-hard-cutover',
55
55
  supportPolicy: {
56
- latest: '0.1.76',
56
+ latest: '0.1.77',
57
57
  minimumSupported: '0.1.53',
58
58
  deprecatedBelow: '0.1.53',
59
59
  },
@@ -0,0 +1,301 @@
1
+ export type EmailStatusVerdict =
2
+ | 'send'
3
+ | 'send_with_caution'
4
+ | 'verify_next'
5
+ | 'hold'
6
+ | 'drop';
7
+
8
+ export type EmailStatusValue =
9
+ | 'valid'
10
+ | 'invalid'
11
+ | 'catch_all'
12
+ | 'valid_catch_all'
13
+ | 'unknown'
14
+ | 'do_not_mail'
15
+ | 'spamtrap'
16
+ | 'abuse'
17
+ | 'disposable';
18
+
19
+ export type EmailDeliverability = 'high' | 'medium' | 'low' | 'unknown';
20
+
21
+ export type EmailMxClass =
22
+ | 'consumer_mailbox'
23
+ | 'workspace_mailbox'
24
+ | 'security_gateway'
25
+ | 'on_prem'
26
+ | 'unknown';
27
+
28
+ export type EmailStatus = {
29
+ verdict: EmailStatusVerdict;
30
+ status: EmailStatusValue;
31
+ verified: boolean;
32
+ confidence: number | null;
33
+ reasons: string[];
34
+ signals: {
35
+ catch_all: boolean | null;
36
+ deliverability: EmailDeliverability;
37
+ mx_class: EmailMxClass;
38
+ mx_provider: string | null;
39
+ mx_record: string | null;
40
+ fraud_score: number | null;
41
+ disposable: boolean | null;
42
+ role_based: boolean | null;
43
+ free_email: boolean | null;
44
+ abuse: boolean | null;
45
+ spamtrap: boolean | null;
46
+ suspect: boolean | null;
47
+ valid: boolean | null;
48
+ };
49
+ provider: {
50
+ name: string;
51
+ raw_status: string | boolean | number | null;
52
+ raw_score: number | null;
53
+ };
54
+ };
55
+
56
+ export type EmailStatusMapEntry = {
57
+ status: EmailStatusValue;
58
+ verdict?: EmailStatusVerdict;
59
+ verified?: boolean;
60
+ reason?: string;
61
+ };
62
+
63
+ export type EmailStatusRule = EmailStatusMapEntry & {
64
+ when: Record<string, string | number | boolean | null>;
65
+ };
66
+
67
+ export type EmailStatusExtractorConfig = {
68
+ provider: string;
69
+ rawStatus?: string[];
70
+ rawScore?: string[];
71
+ valid?: string[];
72
+ deliverability?: string[];
73
+ catchAll?: string[];
74
+ mxProvider?: string[];
75
+ mxRecord?: string[];
76
+ fraudScore?: string[];
77
+ disposable?: string[];
78
+ roleBased?: string[];
79
+ freeEmail?: string[];
80
+ abuse?: string[];
81
+ spamtrap?: string[];
82
+ suspect?: string[];
83
+ statusMap?: Record<string, EmailStatusMapEntry>;
84
+ rules?: EmailStatusRule[];
85
+ };
86
+
87
+ export type EmailStatusBuildInput = {
88
+ config: EmailStatusExtractorConfig;
89
+ values: Record<string, unknown>;
90
+ };
91
+
92
+ const DEFAULT_STATUS_MAP: Record<string, EmailStatusMapEntry> = {
93
+ verified: { status: 'valid', verdict: 'send', verified: true },
94
+ valid: { status: 'valid', verdict: 'send', verified: true },
95
+ deliverable: { status: 'valid', verdict: 'send', verified: true },
96
+ true: { status: 'valid', verdict: 'send', verified: true },
97
+ invalid: { status: 'invalid', verdict: 'drop', verified: false },
98
+ undeliverable: { status: 'invalid', verdict: 'drop', verified: false },
99
+ false: { status: 'invalid', verdict: 'drop', verified: false },
100
+ 'catch-all': {
101
+ status: 'catch_all',
102
+ verdict: 'verify_next',
103
+ verified: false,
104
+ },
105
+ catch_all: {
106
+ status: 'catch_all',
107
+ verdict: 'verify_next',
108
+ verified: false,
109
+ },
110
+ valid_catch_all: {
111
+ status: 'valid_catch_all',
112
+ verdict: 'send_with_caution',
113
+ verified: true,
114
+ },
115
+ accept_all: {
116
+ status: 'catch_all',
117
+ verdict: 'verify_next',
118
+ verified: false,
119
+ },
120
+ unknown: { status: 'unknown', verdict: 'hold', verified: false },
121
+ unavailable: { status: 'unknown', verdict: 'hold', verified: false },
122
+ do_not_mail: { status: 'do_not_mail', verdict: 'drop', verified: false },
123
+ spamtrap: { status: 'spamtrap', verdict: 'drop', verified: false },
124
+ abuse: { status: 'abuse', verdict: 'drop', verified: false },
125
+ disposable: { status: 'disposable', verdict: 'drop', verified: false },
126
+ };
127
+
128
+ function normalizeKey(value: unknown): string | null {
129
+ if (value == null) return null;
130
+ if (typeof value === 'boolean') return String(value);
131
+ const normalized = String(value).trim().toLowerCase().replace(/\s+/g, '_');
132
+ return normalized || null;
133
+ }
134
+
135
+ function boolish(value: unknown): boolean | null {
136
+ if (typeof value === 'boolean') return value;
137
+ if (typeof value === 'number') return value === 1 ? true : value === 0 ? false : null;
138
+ if (typeof value !== 'string') return null;
139
+ const normalized = value.trim().toLowerCase();
140
+ if (['true', 'yes', 'y', '1'].includes(normalized)) return true;
141
+ if (['false', 'no', 'n', '0'].includes(normalized)) return false;
142
+ return null;
143
+ }
144
+
145
+ function numberish(value: unknown): number | null {
146
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
147
+ if (typeof value !== 'string' || value.trim() === '') return null;
148
+ const parsed = Number(value);
149
+ return Number.isFinite(parsed) ? parsed : null;
150
+ }
151
+
152
+ function stringish(value: unknown): string | null {
153
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
154
+ }
155
+
156
+ function deliverability(value: unknown): EmailDeliverability {
157
+ const normalized = normalizeKey(value);
158
+ return normalized === 'high' || normalized === 'medium' || normalized === 'low'
159
+ ? normalized
160
+ : 'unknown';
161
+ }
162
+
163
+ function mxClass(mxProvider: unknown, mxRecord: unknown): EmailMxClass {
164
+ const haystack = `${stringish(mxProvider) ?? ''} ${stringish(mxRecord) ?? ''}`.toLowerCase();
165
+ if (!haystack.trim()) return 'unknown';
166
+ if (
167
+ /proofpoint|pphosted|mimecast|barracuda|ess\.barracudanetworks|ironport|cisco|iphmx|messagelabs|symantec/.test(
168
+ haystack,
169
+ )
170
+ ) {
171
+ return 'security_gateway';
172
+ }
173
+ if (/aspmx\.l\.google|google|g-suite|google workspace/.test(haystack)) {
174
+ return 'workspace_mailbox';
175
+ }
176
+ if (/protection\.outlook|office365|microsoft|outlook|exchange online/.test(haystack)) {
177
+ return 'workspace_mailbox';
178
+ }
179
+ if (/gmail|yahoo|icloud|aol|hotmail/.test(haystack)) return 'consumer_mailbox';
180
+ if (/postfix|exim|sendmail|zimbra|plesk|cpanel|mail\./.test(haystack)) return 'on_prem';
181
+ return 'unknown';
182
+ }
183
+
184
+ function entryForStatus(
185
+ key: string | null,
186
+ map: Record<string, EmailStatusMapEntry> | undefined,
187
+ ): EmailStatusMapEntry | null {
188
+ if (!key) return null;
189
+ return map?.[key] ?? DEFAULT_STATUS_MAP[key] ?? null;
190
+ }
191
+
192
+ function read(values: Record<string, unknown>, name: string): unknown {
193
+ return values[name];
194
+ }
195
+
196
+ function matchesRule(
197
+ rule: EmailStatusRule,
198
+ values: Record<string, unknown>,
199
+ ): boolean {
200
+ return Object.entries(rule.when).every(([key, expected]) => {
201
+ const actual = read(values, key);
202
+ if (key.endsWith('Lt')) {
203
+ const source = numberish(read(values, key.slice(0, -2)));
204
+ return typeof expected === 'number' && source != null && source < expected;
205
+ }
206
+ if (typeof expected === 'boolean') return boolish(actual) === expected;
207
+ if (typeof expected === 'number') return numberish(actual) === expected;
208
+ return normalizeKey(actual) === normalizeKey(expected);
209
+ });
210
+ }
211
+
212
+ export function buildEmailStatus({
213
+ config,
214
+ values,
215
+ }: EmailStatusBuildInput): EmailStatus {
216
+ const rawStatus = read(values, 'rawStatus');
217
+ const rawScore = numberish(read(values, 'rawScore'));
218
+ const valid = boolish(read(values, 'valid'));
219
+ const catchAll = boolish(read(values, 'catchAll'));
220
+ const disposable = boolish(read(values, 'disposable'));
221
+ const abuse = boolish(read(values, 'abuse'));
222
+ const spamtrap = boolish(read(values, 'spamtrap'));
223
+ const suspect = boolish(read(values, 'suspect'));
224
+ const rawKey = normalizeKey(rawStatus);
225
+ const mapped =
226
+ config.rules?.find((rule) => matchesRule(rule, values)) ??
227
+ entryForStatus(rawKey, config.statusMap) ??
228
+ entryForStatus(valid == null ? null : String(valid), config.statusMap);
229
+
230
+ const status =
231
+ mapped?.status ??
232
+ (disposable
233
+ ? 'disposable'
234
+ : abuse
235
+ ? 'abuse'
236
+ : spamtrap
237
+ ? 'spamtrap'
238
+ : catchAll
239
+ ? 'catch_all'
240
+ : valid === true
241
+ ? 'valid'
242
+ : valid === false
243
+ ? 'invalid'
244
+ : 'unknown');
245
+ const defaultVerdict: EmailStatusVerdict =
246
+ status === 'valid'
247
+ ? 'send'
248
+ : status === 'valid_catch_all'
249
+ ? 'send_with_caution'
250
+ : status === 'catch_all'
251
+ ? 'verify_next'
252
+ : status === 'unknown'
253
+ ? 'hold'
254
+ : 'drop';
255
+ const verdict = mapped?.verdict ?? defaultVerdict;
256
+ const verified =
257
+ mapped?.verified ??
258
+ (status === 'valid' || status === 'valid_catch_all' || verdict === 'send');
259
+ const reasons = [
260
+ mapped?.reason,
261
+ catchAll ? 'catch_all_domain' : null,
262
+ mxClass(read(values, 'mxProvider'), read(values, 'mxRecord')) ===
263
+ 'security_gateway'
264
+ ? 'security_gateway_mx'
265
+ : null,
266
+ suspect ? 'provider_marked_suspect' : null,
267
+ ].filter((reason): reason is string => typeof reason === 'string');
268
+
269
+ return {
270
+ verdict,
271
+ status,
272
+ verified,
273
+ confidence: rawScore,
274
+ reasons,
275
+ signals: {
276
+ catch_all: catchAll,
277
+ deliverability: deliverability(read(values, 'deliverability')),
278
+ mx_class: mxClass(read(values, 'mxProvider'), read(values, 'mxRecord')),
279
+ mx_provider: stringish(read(values, 'mxProvider')),
280
+ mx_record: stringish(read(values, 'mxRecord')),
281
+ fraud_score: numberish(read(values, 'fraudScore')),
282
+ disposable,
283
+ role_based: boolish(read(values, 'roleBased')),
284
+ free_email: boolish(read(values, 'freeEmail')),
285
+ abuse,
286
+ spamtrap,
287
+ suspect,
288
+ valid,
289
+ },
290
+ provider: {
291
+ name: config.provider,
292
+ raw_status:
293
+ typeof rawStatus === 'string' ||
294
+ typeof rawStatus === 'boolean' ||
295
+ typeof rawStatus === 'number'
296
+ ? rawStatus
297
+ : null,
298
+ raw_score: rawScore,
299
+ },
300
+ };
301
+ }
@@ -9,7 +9,15 @@ export type {
9
9
  ToolResultTargetMetadata,
10
10
  ToolResultTargetAccessor,
11
11
  } from './tool-result-types';
12
-
12
+ export type {
13
+ EmailDeliverability,
14
+ EmailMxClass,
15
+ EmailStatus,
16
+ EmailStatusValue,
17
+ EmailStatusVerdict,
18
+ } from './email-status';
19
+
20
+ import { buildEmailStatus } from './email-status';
13
21
  import type {
14
22
  SerializedToolExecuteResult,
15
23
  ToolExecuteResult,
@@ -302,6 +310,50 @@ function findFirstTargetByPath(
302
310
  return null;
303
311
  }
304
312
 
313
+ function firstValueForPaths(
314
+ result: unknown,
315
+ paths: readonly string[] | undefined,
316
+ ): ToolResultTargetMetadata | null {
317
+ return findFirstTargetByPath(result, paths);
318
+ }
319
+
320
+ function buildEmailStatusTarget(
321
+ result: unknown,
322
+ descriptor: ToolResultExtractorDescriptor,
323
+ ): ToolResultTargetMetadata | null {
324
+ const config = descriptor.emailStatus;
325
+ if (!config) return null;
326
+ const values: Record<string, unknown> = {};
327
+ const pathSets: Record<string, readonly string[] | undefined> = {
328
+ rawStatus: config.rawStatus,
329
+ rawScore: config.rawScore,
330
+ valid: config.valid,
331
+ deliverability: config.deliverability,
332
+ catchAll: config.catchAll,
333
+ mxProvider: config.mxProvider,
334
+ mxRecord: config.mxRecord,
335
+ fraudScore: config.fraudScore,
336
+ disposable: config.disposable,
337
+ roleBased: config.roleBased,
338
+ freeEmail: config.freeEmail,
339
+ abuse: config.abuse,
340
+ spamtrap: config.spamtrap,
341
+ suspect: config.suspect,
342
+ };
343
+ let firstPath: string | null = null;
344
+ for (const [name, paths] of Object.entries(pathSets)) {
345
+ const match = firstValueForPaths(result, paths);
346
+ if (!match) continue;
347
+ values[name] = match.value;
348
+ firstPath ??= match.path;
349
+ }
350
+ if (!firstPath) return null;
351
+ return {
352
+ path: firstPath,
353
+ value: buildEmailStatus({ config, values }),
354
+ };
355
+ }
356
+
305
357
  function findFirstTargetByKey(
306
358
  result: unknown,
307
359
  target: string,
@@ -507,6 +559,11 @@ function buildTargets(
507
559
  ): Record<string, ToolResultTargetMetadata> {
508
560
  const targets: Record<string, ToolResultTargetMetadata> = {};
509
561
  for (const [target, descriptor] of Object.entries(extractors ?? {})) {
562
+ const emailStatusTarget = buildEmailStatusTarget(result, descriptor);
563
+ if (emailStatusTarget) {
564
+ targets[target] = emailStatusTarget;
565
+ continue;
566
+ }
510
567
  const fromExtractor = findFirstTargetByPath(result, descriptor.paths);
511
568
  if (!fromExtractor) continue;
512
569
  const transformed = coerceToEnum(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.76",
3
+ "version": "0.1.77",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {