deepline 0.3.466 → 0.3.467

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 (156) hide show
  1. package/dist/bundling-sources/sdk/src/client-core.ts +41 -2
  2. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  3. package/dist/bundling-sources/shared_libs/integration-contracts/billing-entitlements.ts +17 -0
  4. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +325 -44
  5. package/dist/bundling-sources/shared_libs/plays/field-catalog.ts +15 -0
  6. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +51 -6
  7. package/dist/bundling-sources/shared_libs/product-notifications/billing-sales.ts +37 -1
  8. package/dist/cli/{auth-HYIPYSKK.mjs → auth-7H62VDZD.mjs} +5 -5
  9. package/dist/cli/auth-EXW5E3DI.js +41 -0
  10. package/dist/cli/{billing-5S6SGBUP.mjs → billing-6TM3ZXDR.mjs} +44 -6
  11. package/dist/cli/{billing-NG62QGPT.js → billing-N5BNGGYN.js} +115 -77
  12. package/dist/cli/{chunk-7YDN6AEK.mjs → chunk-3RGWTKK3.mjs} +10 -0
  13. package/dist/cli/{chunk-A7MWUG5H.js → chunk-3ZGSR6X7.js} +1 -1
  14. package/dist/cli/{chunk-7IWYO475.js → chunk-42AW4SQR.js} +21 -21
  15. package/dist/cli/{chunk-7QITO7XO.mjs → chunk-4LTQYDZG.mjs} +4 -4
  16. package/dist/cli/{chunk-SQFBLBYC.mjs → chunk-5RXKAXFD.mjs} +2 -2
  17. package/dist/cli/{chunk-WKOWXTDR.mjs → chunk-6UZ4MRWN.mjs} +2 -2
  18. package/dist/cli/{chunk-RGRSUMN6.js → chunk-7Y53LJLE.js} +5 -5
  19. package/dist/cli/{chunk-B7BNWOG6.js → chunk-7ZZWOJTM.js} +3 -3
  20. package/dist/cli/{chunk-EOKUL6XS.js → chunk-AD5BZUAP.js} +5 -5
  21. package/dist/cli/{chunk-YHPUJI7G.mjs → chunk-AU4BWQTT.mjs} +5 -5
  22. package/dist/cli/{chunk-IEQPOBTK.mjs → chunk-AUVEQ3JX.mjs} +1 -1
  23. package/dist/cli/{chunk-QWMXP2IP.mjs → chunk-C4GBOGTW.mjs} +2 -2
  24. package/dist/cli/{chunk-3DVJ2IPG.js → chunk-C6EGT3LO.js} +183 -173
  25. package/dist/cli/{chunk-W4XWGNW4.mjs → chunk-CER3XHT3.mjs} +3 -3
  26. package/dist/cli/{chunk-VSEKVRAP.mjs → chunk-DCDTCG2D.mjs} +2 -2
  27. package/dist/cli/{chunk-R2SQN2CD.js → chunk-FFOAOQ77.js} +6 -6
  28. package/dist/cli/{chunk-6R36QIG4.mjs → chunk-FP3DH46P.mjs} +4 -4
  29. package/dist/cli/{chunk-RCMT6AZ5.mjs → chunk-FQIGRZ52.mjs} +1 -1
  30. package/dist/cli/{chunk-4QJ63YHC.js → chunk-FRY76AFK.js} +8 -8
  31. package/dist/cli/{chunk-VVCMP45C.js → chunk-FS55YDCW.js} +3 -3
  32. package/dist/cli/{chunk-HOFXQYRF.js → chunk-FVA6XLRA.js} +7 -7
  33. package/dist/cli/{chunk-OGCAGZXO.mjs → chunk-FVYAEBGO.mjs} +23 -2
  34. package/dist/cli/{chunk-RSXOGXGX.mjs → chunk-G35HNL6P.mjs} +1 -1
  35. package/dist/cli/{chunk-MTH7WIE3.js → chunk-H3QDLCRT.js} +53 -53
  36. package/dist/cli/{chunk-HIMV32ZX.js → chunk-HNP5V5BN.js} +23 -2
  37. package/dist/cli/{chunk-UHAKSLL2.mjs → chunk-HTXQUA3V.mjs} +11 -1
  38. package/dist/cli/{chunk-QTTYIPFY.mjs → chunk-I3LWDZRN.mjs} +1 -1
  39. package/dist/cli/{chunk-22BKA2QL.js → chunk-KMYCHMZF.js} +26 -26
  40. package/dist/cli/{chunk-F6H74EFB.mjs → chunk-KUCUHRLC.mjs} +2 -2
  41. package/dist/cli/{chunk-LDEJ4NUP.js → chunk-LAMHSZ74.js} +13 -13
  42. package/dist/cli/{chunk-CMXFOZVG.js → chunk-LKVOV7PX.js} +258 -258
  43. package/dist/cli/{chunk-OB5NON4G.js → chunk-MGUQGKHE.js} +8 -8
  44. package/dist/cli/{chunk-JLE5UYZV.mjs → chunk-MRBDFPJ7.mjs} +2 -2
  45. package/dist/cli/{chunk-JLZKF7EY.js → chunk-NPZBE64X.js} +11 -1
  46. package/dist/cli/{chunk-NJ6KQLMO.mjs → chunk-NU64F53H.mjs} +11 -11
  47. package/dist/cli/{chunk-V27TVT7P.mjs → chunk-OSKGEBE7.mjs} +1 -1
  48. package/dist/cli/{chunk-H5MYPDO2.mjs → chunk-R5ZJICJA.mjs} +1 -1
  49. package/dist/cli/{chunk-6CYRIFZ3.mjs → chunk-RH6DVPGW.mjs} +2 -2
  50. package/dist/cli/{chunk-NQH7E3F2.js → chunk-SHGBHP6J.js} +3 -3
  51. package/dist/cli/{chunk-5EUJKY3G.mjs → chunk-SYLR33PS.mjs} +3 -3
  52. package/dist/cli/{chunk-L3U44YVO.js → chunk-T5GRG37B.js} +5 -5
  53. package/dist/cli/{chunk-FWHRCZET.mjs → chunk-TNAXVJEG.mjs} +5 -5
  54. package/dist/cli/{chunk-6P4OYABU.js → chunk-V6OMS7E2.js} +6 -6
  55. package/dist/cli/{chunk-QMHBWFU5.mjs → chunk-VGWH3BFC.mjs} +1 -1
  56. package/dist/cli/{chunk-5BEKJUOK.js → chunk-VOPKDTJT.js} +2 -2
  57. package/dist/cli/{chunk-ZBWKWMSH.js → chunk-WBYC7AYC.js} +16 -16
  58. package/dist/cli/{chunk-RRCVAVM7.js → chunk-WGMNXDRZ.js} +2 -2
  59. package/dist/cli/{chunk-46YQQOTC.mjs → chunk-WISHPL5Q.mjs} +5 -5
  60. package/dist/cli/{chunk-QZKSG7XZ.js → chunk-WS2BY2ST.js} +36 -36
  61. package/dist/cli/{chunk-PEUWKPY4.js → chunk-WTVTCSPY.js} +4 -4
  62. package/dist/cli/{chunk-MUQAKLBP.mjs → chunk-X3RAGNNZ.mjs} +1 -1
  63. package/dist/cli/{chunk-I2TCWIW2.mjs → chunk-XSX6TJUA.mjs} +1 -1
  64. package/dist/cli/{chunk-NYNNNOJW.js → chunk-YIJS7ET7.js} +51 -51
  65. package/dist/cli/{chunk-SWUSDFG2.js → chunk-ZTA4JIAB.js} +9 -9
  66. package/dist/cli/command-docs.js +2 -2
  67. package/dist/cli/command-docs.mjs +1 -1
  68. package/dist/cli/{csv-ZGZX2IIP.mjs → csv-435JAJ45.mjs} +4 -4
  69. package/dist/cli/{csv-AD2DLLLL.js → csv-BPZ4VLR6.js} +13 -13
  70. package/dist/cli/{db-NBY4LHAY.js → db-EBR2UQK3.js} +27 -27
  71. package/dist/cli/{db-LUFEKPEK.mjs → db-IEUTNOOC.mjs} +6 -6
  72. package/dist/cli/describe-EPT52IUE.js +48 -0
  73. package/dist/cli/{describe-MLXMC47U.mjs → describe-GM233O75.mjs} +20 -20
  74. package/dist/cli/describe-core.d.mts +3 -3
  75. package/dist/cli/describe-core.d.ts +3 -3
  76. package/dist/cli/describe-core.js +4 -4
  77. package/dist/cli/describe-core.mjs +3 -3
  78. package/dist/cli/{executions-HDJMKNBR.js → executions-5SWPQTLB.js} +8 -8
  79. package/dist/cli/{executions-JKVWM3P4.mjs → executions-LDPBVSKZ.mjs} +6 -6
  80. package/dist/cli/{feedback-7E5Z4ZO5.mjs → feedback-JB7BBF64.mjs} +4 -4
  81. package/dist/cli/{feedback-VXMP7HDF.js → feedback-YFW5KT36.js} +8 -8
  82. package/dist/cli/git-7L7LKOKW.js +25 -0
  83. package/dist/cli/{git-UFMRAGMY.mjs → git-MCGUPO2W.mjs} +5 -5
  84. package/dist/cli/index.js +139 -139
  85. package/dist/cli/index.mjs +41 -41
  86. package/dist/cli/{legacy-noops-BDULCVGD.mjs → legacy-noops-EHNXHQL7.mjs} +5 -5
  87. package/dist/cli/{legacy-noops-4QKREY4K.js → legacy-noops-LMGSIBWM.js} +8 -8
  88. package/dist/cli/{monitors-S6TDJYCK.mjs → monitors-355EWLGN.mjs} +7 -7
  89. package/dist/cli/{monitors-LNNSTELC.js → monitors-VRQ32EAA.js} +91 -91
  90. package/dist/cli/{org-EEEMWKQ2.mjs → org-MJ6XMAI4.mjs} +51 -10
  91. package/dist/cli/{org-OWM3IGOK.js → org-W3YZN65W.js} +113 -72
  92. package/dist/cli/{pages-ZYYMQUDX.mjs → pages-62QNUXV2.mjs} +6 -6
  93. package/dist/cli/{pages-7JF6AKDI.js → pages-JPK6UVMV.js} +17 -17
  94. package/dist/cli/{play-5Z3E7KL6.mjs → play-CKBPES6Q.mjs} +18 -18
  95. package/dist/cli/{play-WLAFXAHR.js → play-WPPJQBBS.js} +305 -305
  96. package/dist/cli/{providers-TTDFQQU2.js → providers-DH5JJ5HN.js} +9 -9
  97. package/dist/cli/{providers-JUT7IYD3.mjs → providers-LLPKHVXL.mjs} +6 -6
  98. package/dist/cli/{quickstart-P4TL4NWJ.mjs → quickstart-K5HVPO2L.mjs} +4 -4
  99. package/dist/cli/{quickstart-OZYEGICI.js → quickstart-NNEF37EG.js} +6 -6
  100. package/dist/cli/{run-BYGE4LUB.js → run-N2KY2VNQ.js} +40 -40
  101. package/dist/cli/{run-CM6OZUBR.mjs → run-SKIZFJ5O.mjs} +25 -25
  102. package/dist/cli/{search-RZRJ73YL.mjs → search-2WXJ3GID.mjs} +7 -7
  103. package/dist/cli/search-HO7ARG3H.js +32 -0
  104. package/dist/cli/{secrets-XLTN3SCL.js → secrets-5YVX7LK7.js} +15 -15
  105. package/dist/cli/{secrets-DHI6ZMKE.mjs → secrets-6GEQ75OU.mjs} +6 -6
  106. package/dist/cli/{sessions-MZD5WZBT.mjs → sessions-GOPZ3YRN.mjs} +5 -5
  107. package/dist/cli/{sessions-6NCQQXVK.js → sessions-H3QES7C2.js} +6 -6
  108. package/dist/cli/{settings-JJH72W5V.js → settings-62SU5ROV.js} +78 -78
  109. package/dist/cli/{settings-S6GQ4XFZ.mjs → settings-IP47HYC6.mjs} +11 -11
  110. package/dist/cli/{setup-ZJ4G54XN.mjs → setup-LCKXUSW5.mjs} +9 -9
  111. package/dist/cli/{setup-3BIOG6N2.js → setup-YCCU7TU4.js} +44 -44
  112. package/dist/cli/{skills-FBH4REPB.mjs → skills-3QAQI6X7.mjs} +7 -7
  113. package/dist/cli/skills-UVAIKQNC.js +31 -0
  114. package/dist/cli/{tools-J6YGZTD5.mjs → tools-HKD4G6HA.mjs} +18 -18
  115. package/dist/cli/tools-KAIUUBTD.js +55 -0
  116. package/dist/cli/{update-6CSRALIQ.mjs → update-2VYSBSDD.mjs} +9 -9
  117. package/dist/cli/update-Q3AMECAG.js +33 -0
  118. package/dist/cli/{workflow-3ATSB7HQ.mjs → workflow-EFRGY6JD.mjs} +7 -7
  119. package/dist/cli/{workflow-2ETKYSJV.js → workflow-MMWGUSGY.js} +17 -17
  120. package/dist/{client-BClp2yrq.d.ts → client-BOEAXwJj.d.ts} +1 -1
  121. package/dist/{client-ADFHNtFM.d.mts → client-DL0koOaP.d.mts} +1 -1
  122. package/dist/{client-core-BFU73Ttw.d.ts → client-core-BX_8khUY.d.ts} +23 -1
  123. package/dist/{client-core-DWLmmJWo.d.mts → client-core-CowyuxKu.d.mts} +23 -1
  124. package/dist/{compiler-manifest-Cs17yO-H.d.ts → compiler-manifest-D5dARg3M.d.mts} +64 -10
  125. package/dist/{compiler-manifest-Cs17yO-H.d.mts → compiler-manifest-D5dARg3M.d.ts} +64 -10
  126. package/dist/{errors-Dee-QpVS.d.ts → errors-BtHHzWL8.d.ts} +1 -1
  127. package/dist/{errors-qNfvQuRB.d.mts → errors-BveMVPwA.d.mts} +1 -1
  128. package/dist/{index-BTqNNXmC.d.mts → index-BboApDGD.d.mts} +1 -1
  129. package/dist/{index-DXTh1Lxe.d.ts → index-C6sovoYP.d.ts} +1 -1
  130. package/dist/index.d.mts +7 -7
  131. package/dist/index.d.ts +7 -7
  132. package/dist/index.js +277 -76
  133. package/dist/index.mjs +239 -38
  134. package/dist/install-integrity.json +114 -114
  135. package/dist/plays/bundle-play-file.d.mts +3 -3
  136. package/dist/plays/bundle-play-file.d.ts +3 -3
  137. package/dist/plays/play-description.d.mts +2 -2
  138. package/dist/plays/play-description.d.ts +2 -2
  139. package/dist/plays/run-request.d.mts +2 -2
  140. package/dist/plays/run-request.d.ts +2 -2
  141. package/dist/release.d.mts +1 -1
  142. package/dist/release.d.ts +1 -1
  143. package/dist/release.js +2 -2
  144. package/dist/release.mjs +1 -1
  145. package/dist/server.d.mts +4 -4
  146. package/dist/server.d.ts +4 -4
  147. package/dist/server.js +5 -5
  148. package/dist/server.mjs +3 -3
  149. package/package.json +1 -1
  150. package/dist/cli/auth-Y3UTWD3W.js +0 -41
  151. package/dist/cli/describe-Z66CQESH.js +0 -48
  152. package/dist/cli/git-NNSJKW3V.js +0 -25
  153. package/dist/cli/search-RIG4LTCS.js +0 -32
  154. package/dist/cli/skills-ODISCL7S.js +0 -31
  155. package/dist/cli/tools-IHBHDI4P.js +0 -55
  156. package/dist/cli/update-5O3HDR5O.js +0 -33
@@ -1,8 +1,12 @@
1
1
  import type {
2
2
  BillingEntitlements,
3
+ IncludedCompute,
3
4
  BillingBenefitValues,
4
5
  } from '../../shared_libs/integration-contracts/billing-entitlements';
5
- export type { BillingEntitlements } from '../../shared_libs/integration-contracts/billing-entitlements';
6
+ export type {
7
+ BillingEntitlements,
8
+ IncludedCompute,
9
+ } from '../../shared_libs/integration-contracts/billing-entitlements';
6
10
  import type {
7
11
  PlaySettingsResult,
8
12
  PlaySettingsPatch,
@@ -2300,6 +2304,20 @@ export type WorkspaceCreateResult = {
2300
2304
 
2301
2305
  /** Deliberate workspace lifecycle operations. */
2302
2306
  export type WorkspacesNamespace = {
2307
+ /** One page of current workspace memberships and effective grants. */
2308
+ members: (options?: { cursor?: string }) => Promise<{
2309
+ members: { id: string; userId: string; role: string; grants: string[] }[];
2310
+ cursor: string | null;
2311
+ }>;
2312
+ /** Replace a member's grants. Requires admin; ownership changes require owner. */
2313
+ setMemberGrants: (
2314
+ memberId: string,
2315
+ grants: string[],
2316
+ ) => Promise<{
2317
+ ok: boolean;
2318
+ memberId: string;
2319
+ role: string;
2320
+ }>;
2303
2321
  /** Create an additional workspace and provision its active PAYG Contract. */
2304
2322
  create: (options: {
2305
2323
  name: string;
@@ -2453,6 +2471,10 @@ export type BillingNamespace = {
2453
2471
  quote: (input: BillingQuoteRequest) => Promise<BillingQuoteResult>;
2454
2472
  /** Current workspace provider benefits; discovery does not authorize a call. */
2455
2473
  entitlements: () => Promise<BillingEntitlements>;
2474
+ /** Play compute pricing and this period's sandbox minutes by day. */
2475
+ includedCompute: () => Promise<IncludedCompute>;
2476
+ /** Turn on included compute for the workspace. It cannot be turned off again. */
2477
+ enableIncludedCompute: () => Promise<IncludedCompute>;
2456
2478
  /** Charge the saved payment method and add Deepline credits to the active workspace. */
2457
2479
  topUp: (options: {
2458
2480
  credits: number;
@@ -2906,6 +2928,9 @@ export class DeeplineClient {
2906
2928
  quote: (input) =>
2907
2929
  this.http.post<BillingQuoteResult>('/api/v2/billing/quote', input),
2908
2930
  entitlements: () => this.http.get('/api/v2/billing/entitlements'),
2931
+ includedCompute: () => this.http.get('/api/v2/billing/included-compute'),
2932
+ enableIncludedCompute: () =>
2933
+ this.http.post<IncludedCompute>('/api/v2/billing/included-compute', {}),
2909
2934
  topUp: (options) => this.topUpBillingBalance(options),
2910
2935
  topUpWithReceipt: (options) =>
2911
2936
  this.topUpBillingBalanceWithReceipt(options),
@@ -2944,6 +2969,17 @@ export class DeeplineClient {
2944
2969
  this.createTargetBillingPortalSession(options),
2945
2970
  };
2946
2971
  this.workspaces = {
2972
+ members: (options) => {
2973
+ const query = options?.cursor
2974
+ ? `?cursor=${encodeURIComponent(options.cursor)}`
2975
+ : '';
2976
+ return this.http.get(`/api/v2/auth/workspace/members${query}`);
2977
+ },
2978
+ setMemberGrants: (memberId, grants) =>
2979
+ this.http.patch(
2980
+ `/api/v2/auth/workspace/members/${encodeURIComponent(memberId)}/role`,
2981
+ { role: grants.join(',') },
2982
+ ),
2947
2983
  create: (options) => this.createWorkspace(options),
2948
2984
  };
2949
2985
  this.monitors = {
@@ -3095,7 +3131,10 @@ export class DeeplineClient {
3095
3131
  }
3096
3132
 
3097
3133
  /** The explicit way to change who can see an existing page. */
3098
- async setPageVisibility(name: string, isPublic: boolean): Promise<HostedPage> {
3134
+ async setPageVisibility(
3135
+ name: string,
3136
+ isPublic: boolean,
3137
+ ): Promise<HostedPage> {
3099
3138
  return await this.http.post<HostedPage>(
3100
3139
  `/api/v2/pages/${encodeURIComponent(name)}/visibility`,
3101
3140
  { public: isPublic },
@@ -205,7 +205,7 @@ export const SDK_RELEASE = {
205
205
  // getters keep their established compatibility behavior.
206
206
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
207
207
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
208
- version: '0.3.466',
208
+ version: '0.3.467',
209
209
  updateSummary: {
210
210
  introducedIn: '0.3.90',
211
211
  summary:
@@ -69,3 +69,20 @@ export type BillingEntitlements = {
69
69
  };
70
70
  };
71
71
  };
72
+
73
+ /** `GET`/`POST /api/v2/billing/included-compute`. */
74
+ export type IncludedCompute = {
75
+ /** `unavailable`: the workspace is outside the November pricing rollout. */
76
+ status: 'on' | 'off' | 'unavailable';
77
+ /** Credits per minute of sandbox runtime in force; null at provider cost. */
78
+ credits_per_minute: number | null;
79
+ included_minutes: number;
80
+ /** What turning included compute on gives this workspace's plan. */
81
+ plan: { included_minutes: number; credits_per_minute: number };
82
+ period_start_at: string;
83
+ period_end_at: string;
84
+ resets_on: 'calendar_month' | 'billing_cycle';
85
+ included_minutes_used: number;
86
+ minutes_used: number;
87
+ days: Array<{ day: string; minutes: number }>;
88
+ };
@@ -33,8 +33,10 @@ import {
33
33
  type JobChangeStatus,
34
34
  } from '../plays/extractor-targets';
35
35
  import {
36
+ LIST_NAMES,
36
37
  fieldOfName,
37
38
  fieldPaths,
39
+ isFieldId,
38
40
  resolveFieldName,
39
41
  type FieldBinding,
40
42
  } from '../plays/field-catalog';
@@ -50,6 +52,7 @@ import type {
50
52
  ToolResultListMetadata,
51
53
  ToolResultEnvelope,
52
54
  ToolResultMetadataInput,
55
+ ToolResultRow,
53
56
  ToolResultTargetAccessor,
54
57
  ToolResultTargetMetadata,
55
58
  } from '../plays/tool-result-types';
@@ -1551,69 +1554,341 @@ function listTableNamespace(
1551
1554
  }
1552
1555
  }
1553
1556
 
1554
- /**
1555
- * The outputs of a result that are catalog fields: declared extractors whose
1556
- * meaning is known, from the tool's own `means` or the name's one meaning.
1557
- * Values found by guessing at keys are never bindings.
1558
- */
1559
- function resultFieldBindings(
1560
- result: ToolExecuteResult | BasicToolExecuteResult,
1557
+ /** What a result or one of its rows knows, for reading fields from it. */
1558
+ type FieldSource = {
1559
+ toolId: string;
1560
+ bindings: FieldBinding[];
1561
+ outputs: string[];
1562
+ values(output: string): unknown[];
1563
+ /** The list an output belongs to, when only its items can answer `.value`. */
1564
+ itemList?(output: string): string | undefined;
1565
+ };
1566
+
1567
+ /** The one value, or null. Returning one of several would miss the rest. */
1568
+ function oneValue(values: unknown[], toolId: string, name: string): unknown {
1569
+ if (values.length > 1) {
1570
+ throw new Error(
1571
+ `${toolId} has ${values.length} values for "${name}". Read ${name}.values.`,
1572
+ );
1573
+ }
1574
+ return values[0] ?? null;
1575
+ }
1576
+
1577
+ function extractorBindings(
1578
+ extractors: Record<string, ToolResultExtractorDescriptor>,
1561
1579
  ): FieldBinding[] {
1562
- const extractors = result._metadata.extractors ?? {};
1563
1580
  return Object.entries(extractors).flatMap(([name, descriptor]) => {
1564
- if (!result.extractedValues[name]) return [];
1565
1581
  const id = fieldOfName(name, descriptor.means);
1566
1582
  return id ? [{ name, id }] : [];
1567
1583
  });
1568
1584
  }
1569
1585
 
1570
- function resultOutputNames(
1586
+ /** Reads an id or short name, ending in `.value`, `.values` or neither. */
1587
+ function readField(source: FieldSource, name: string): unknown {
1588
+ const ending = /^(.*)\.(value|values)$/.exec(name);
1589
+ const base = ending?.[1] ?? name;
1590
+ const target = isFieldId(`${base}.value`) ? `${base}.value` : base;
1591
+ const binding = resolveFieldName(target, source.bindings, source.outputs);
1592
+ if (!binding) {
1593
+ const ids = [...new Set(source.bindings.map((known) => known.id))];
1594
+ throw new Error(
1595
+ `${source.toolId} has no field "${name}". ${ids.length > 0 ? `Its fields: ${ids.join(', ')}.` : 'It declares no catalog fields.'} Other values are in response.`,
1596
+ );
1597
+ }
1598
+ if (ending?.[2] === 'values') return source.values(binding.name);
1599
+ const list = source.itemList?.(binding.name);
1600
+ if (list) {
1601
+ throw new Error(
1602
+ `"${base}" is a field of each item on ${source.toolId}. Read it from result.rows('${list}').`,
1603
+ );
1604
+ }
1605
+ return oneValue(source.values(binding.name), source.toolId, base);
1606
+ }
1607
+
1608
+ /** One place in `fields`. Only the key it serializes as appears in JSON. */
1609
+ type FieldPlace = {
1610
+ parent: string[];
1611
+ value(): unknown;
1612
+ values(): unknown;
1613
+ serializes?: 'value' | 'values';
1614
+ };
1615
+
1616
+ function buildFieldTree(places: FieldPlace[]): Record<string, unknown> {
1617
+ const tree: Record<string, unknown> = {};
1618
+ for (const { parent, value, values, serializes } of places) {
1619
+ const node = parent.reduce(
1620
+ (at, key) => (at[key] ??= {}) as Record<string, unknown>,
1621
+ tree,
1622
+ );
1623
+ Object.defineProperties(node, {
1624
+ value: { get: value, enumerable: serializes === 'value' },
1625
+ values: { get: values, enumerable: serializes === 'values' },
1626
+ });
1627
+ }
1628
+ return tree;
1629
+ }
1630
+
1631
+ /** The places of a source's fields. Ambiguous ones throw and never serialize. */
1632
+ function fieldPlaces(
1633
+ source: FieldSource,
1634
+ serializes: (output: string) => FieldPlace['serializes'],
1635
+ lists: readonly string[] = [],
1636
+ ): FieldPlace[] {
1637
+ return fieldPaths(source.bindings, source.outputs, lists)
1638
+ .filter((entry) => !entry.list)
1639
+ .map((entry) => ({
1640
+ parent: entry.path.slice(0, -1),
1641
+ value: () => readField(source, entry.name),
1642
+ values: () =>
1643
+ entry.output === null
1644
+ ? readField(source, entry.name)
1645
+ : source.values(entry.output),
1646
+ serializes: entry.output === null ? undefined : serializes(entry.output),
1647
+ }));
1648
+ }
1649
+
1650
+ /**
1651
+ * Every value one extractor finds under `root`, from the first of its paths
1652
+ * that finds any. Later paths are fallbacks, not more values.
1653
+ */
1654
+ function extractorValues(
1655
+ root: unknown,
1656
+ name: string,
1657
+ descriptor: ToolResultExtractorDescriptor,
1658
+ paths: readonly string[],
1659
+ ): unknown[] {
1660
+ for (const path of paths) {
1661
+ for (const candidate of candidateResultPaths(path)) {
1662
+ const found = getValuesAtPath(root, candidate)
1663
+ .flat()
1664
+ .flatMap((value) => {
1665
+ const accepted = acceptedTargetValue(name, value);
1666
+ return accepted === undefined ? [] : [accepted];
1667
+ });
1668
+ if (found.length > 0) {
1669
+ return found.map((value) =>
1670
+ coerceToEnum(applyExtractorTransforms(value, descriptor), descriptor),
1671
+ );
1672
+ }
1673
+ }
1674
+ }
1675
+ return [];
1676
+ }
1677
+
1678
+ /** A value computed from several provider fields, so never more than one. */
1679
+ function isComputedValue(descriptor: ToolResultExtractorDescriptor): boolean {
1680
+ return Boolean(descriptor.emailStatus || descriptor.overrides?.length);
1681
+ }
1682
+
1683
+ function presentValues(value: unknown): unknown[] {
1684
+ return value === null || value === undefined ? [] : [value];
1685
+ }
1686
+
1687
+ function firstFound(readers: Array<() => unknown[]>): unknown[] {
1688
+ for (const read of readers) {
1689
+ const found = read();
1690
+ if (found.length > 0) return found;
1691
+ }
1692
+ return [];
1693
+ }
1694
+
1695
+ /**
1696
+ * The lists a tool declares, whatever this response holds. Paths ending in
1697
+ * the same name are one list, and a list is called by its alias when it has
1698
+ * one (`{ people: 'data' }`).
1699
+ */
1700
+ function declaredListNames(
1571
1701
  result: ToolExecuteResult | BasicToolExecuteResult,
1572
1702
  ): string[] {
1703
+ const aliasOf = new Map(
1704
+ Object.entries(result._metadata.listExtractorAliases ?? {}).map(
1705
+ ([alias, source]) => [source, alias],
1706
+ ),
1707
+ );
1573
1708
  return [
1574
- ...Object.keys(result._metadata.extractors ?? {}),
1575
- ...Object.keys(result.extractedLists),
1709
+ ...new Set(
1710
+ (result._metadata.listExtractorPaths ?? []).flatMap((path) => {
1711
+ const name = listNameFromDeclaredPath(path);
1712
+ return name ? [aliasOf.get(name) ?? name] : [];
1713
+ }),
1714
+ ),
1576
1715
  ];
1577
1716
  }
1578
1717
 
1579
- function readResultField(
1718
+ function resultFieldSource(
1580
1719
  result: ToolExecuteResult | BasicToolExecuteResult,
1581
- name: string,
1582
- ): unknown {
1583
- const bindings = resultFieldBindings(result);
1584
- const binding = resolveFieldName(name, bindings, resultOutputNames(result));
1585
- if (!binding) {
1586
- const ids = [...new Set(bindings.map((known) => known.id))];
1587
- throw new Error(
1588
- `${result._metadata.toolId} has no field "${name}". ${ids.length > 0 ? `Its fields: ${ids.join(', ')}.` : 'It declares no catalog fields.'} Other values are in result.response.`,
1720
+ ): FieldSource {
1721
+ const extractors = result._metadata.extractors ?? {};
1722
+ const root = resultRootOf(result);
1723
+ const getter = (output: string) =>
1724
+ presentValues(result.extractedValues[output]?.get());
1725
+ // A path with a wildcard may read a clipped preview, so rows answer first.
1726
+ const atRoot = (output: string) => {
1727
+ const descriptor = extractors[output]!;
1728
+ return extractorValues(
1729
+ root,
1730
+ output,
1731
+ descriptor,
1732
+ descriptor.paths.filter((path) => !path.includes('[*]')),
1589
1733
  );
1590
- }
1591
- return result.extractedValues[binding.name]!.get();
1734
+ };
1735
+ // Only a tool with one list can answer across its items; with several,
1736
+ // which list a value came from would be lost.
1737
+ const inRows = (output: string) => {
1738
+ if (declaredListNames(result).length !== 1) return [];
1739
+ const id = fieldOfName(output, extractors[output]!.means)!;
1740
+ return readResultRows(result).flatMap(
1741
+ (row) => row.field(id.replace(/\.value$/, '.values')) as unknown[],
1742
+ );
1743
+ };
1744
+ return {
1745
+ toolId: result._metadata.toolId,
1746
+ bindings: extractorBindings(extractors),
1747
+ outputs: [
1748
+ ...Object.keys(extractors),
1749
+ ...Object.keys(result.extractedLists),
1750
+ ],
1751
+ values(output) {
1752
+ const descriptor = extractors[output]!;
1753
+ return isComputedValue(descriptor)
1754
+ ? getter(output)
1755
+ : firstFound([
1756
+ () => atRoot(output),
1757
+ () => inRows(output),
1758
+ () => extractorValues(root, output, descriptor, descriptor.paths),
1759
+ () => getter(output),
1760
+ ]);
1761
+ },
1762
+ itemList(output) {
1763
+ const onResult =
1764
+ isComputedValue(extractors[output]!) || atRoot(output).length > 0;
1765
+ const named = declaredListNames(result).filter((name) =>
1766
+ (LIST_NAMES as readonly string[]).includes(name),
1767
+ );
1768
+ return onResult ? undefined : named[0];
1769
+ },
1770
+ };
1592
1771
  }
1593
1772
 
1594
- /** `result.fields`, laid out by `fieldPaths`; each value reads on access. */
1595
- function buildResultFields(
1773
+ function resultFields(
1596
1774
  result: ToolExecuteResult | BasicToolExecuteResult,
1597
1775
  ): Record<string, unknown> {
1598
- const tree: Record<string, unknown> = {};
1776
+ const source = resultFieldSource(result);
1599
1777
  const lists = Object.keys(result.extractedLists);
1600
- for (const entry of fieldPaths(
1601
- resultFieldBindings(result),
1602
- resultOutputNames(result),
1603
- lists,
1604
- )) {
1605
- let node = tree;
1606
- for (const key of entry.path.slice(0, -1)) {
1607
- node = (node[key] ??= {}) as Record<string, unknown>;
1608
- }
1609
- Object.defineProperty(node, entry.path[entry.path.length - 1]!, {
1610
- get: entry.list
1611
- ? () => result.extractedLists[entry.name]!.get()
1612
- : () => readResultField(result, entry.name),
1613
- enumerable: true,
1614
- });
1778
+ return buildFieldTree([
1779
+ ...fieldPlaces(source, () => 'value', lists),
1780
+ ...lists.map((name) => ({
1781
+ parent: [name],
1782
+ value: () => oneValue(resultListItems(result, name), source.toolId, name),
1783
+ values: () => result.extractedLists[name]!.get(),
1784
+ serializes: 'values' as const,
1785
+ })),
1786
+ ]);
1787
+ }
1788
+
1789
+ /** An extractor's paths as they read from one list item. */
1790
+ function rowExtractorPaths(paths: readonly string[]): string[] {
1791
+ return [
1792
+ ...new Set(
1793
+ paths.flatMap((path) => {
1794
+ const wildcard = path.indexOf('[*].');
1795
+ return wildcard < 0 ? [path] : [path, path.slice(wildcard + 4)];
1796
+ }),
1797
+ ),
1798
+ ];
1799
+ }
1800
+
1801
+ function createToolResultRow(
1802
+ toolId: string,
1803
+ item: unknown,
1804
+ extractors: Record<string, ToolResultExtractorDescriptor>,
1805
+ ): ToolResultRow {
1806
+ const descriptors = Object.fromEntries(
1807
+ Object.entries(extractors).map(([name, descriptor]) => [
1808
+ name,
1809
+ { ...descriptor, paths: rowExtractorPaths(descriptor.paths) },
1810
+ ]),
1811
+ );
1812
+ const root = { toolResponse: { raw: item } };
1813
+ const targets = buildTargets(root, descriptors);
1814
+ const values = (output: string) => {
1815
+ const descriptor = descriptors[output]!;
1816
+ const target = () => presentValues(targets[output]?.value);
1817
+ return isComputedValue(descriptor)
1818
+ ? target()
1819
+ : firstFound([
1820
+ () => extractorValues(root, output, descriptor, descriptor.paths),
1821
+ target,
1822
+ ]);
1823
+ };
1824
+ const source: FieldSource = {
1825
+ toolId,
1826
+ bindings: extractorBindings(extractors),
1827
+ outputs: Object.keys(extractors),
1828
+ values,
1829
+ };
1830
+ const row = {
1831
+ fields: buildFieldTree(
1832
+ fieldPlaces(source, (output) =>
1833
+ values(output).length > 1 ? 'values' : 'value',
1834
+ ),
1835
+ ),
1836
+ response: item,
1837
+ };
1838
+ Object.defineProperty(row, 'field', {
1839
+ value: (name: string) => readField(source, name),
1840
+ enumerable: false,
1841
+ });
1842
+ return row as unknown as ToolResultRow;
1843
+ }
1844
+
1845
+ function resultListItems(
1846
+ result: ToolExecuteResult | BasicToolExecuteResult,
1847
+ name: string,
1848
+ ): unknown[] {
1849
+ // The array view gives complete provider items, or throws for a lazy or
1850
+ // incomplete list, in every edition.
1851
+ return projectToolResultListGetters(
1852
+ result as ToolExecuteResult,
1853
+ 'array',
1854
+ ).extractedLists[name]!.get();
1855
+ }
1856
+
1857
+ /**
1858
+ * The one list `rows()` reads. A tool that declares several lists always needs
1859
+ * a name, whatever this response holds, and a name never reads two lists.
1860
+ */
1861
+ function selectResultList(
1862
+ result: ToolExecuteResult | BasicToolExecuteResult,
1863
+ list?: string,
1864
+ ): string {
1865
+ const toolId = result._metadata.toolId;
1866
+ const declared = declaredListNames(result);
1867
+ if (list === undefined && declared.length > 1) {
1868
+ throw new Error(
1869
+ `${toolId} has several lists: ${declared.join(', ')}. Name one with result.rows('${declared[0]}').`,
1870
+ );
1615
1871
  }
1616
- return tree;
1872
+ const wanted = list ?? declared[0];
1873
+ if (wanted !== undefined && result.extractedLists[wanted]) return wanted;
1874
+ if (wanted !== undefined && !declared.includes(wanted)) {
1875
+ throw new Error(
1876
+ `${toolId} has no list "${wanted}". Its lists: ${declared.join(', ') || 'none'}.`,
1877
+ );
1878
+ }
1879
+ throw new Error(
1880
+ `${toolId} returned no declared list; a missing list is not an empty one. Read result.response.`,
1881
+ );
1882
+ }
1883
+
1884
+ function readResultRows(
1885
+ result: ToolExecuteResult | BasicToolExecuteResult,
1886
+ list?: string,
1887
+ ): ToolResultRow[] {
1888
+ const { toolId, extractors = {} } = result._metadata;
1889
+ return resultListItems(result, selectResultList(result, list)).map((item) =>
1890
+ createToolResultRow(toolId, item, extractors),
1891
+ );
1617
1892
  }
1618
1893
 
1619
1894
  export function createToolExecuteResult<TResult = unknown>(input: {
@@ -1707,13 +1982,19 @@ export function createToolExecuteResult<TResult = unknown>(input: {
1707
1982
  },
1708
1983
  fields: {
1709
1984
  get(this: ToolExecuteResult) {
1710
- return buildResultFields(this);
1985
+ return resultFields(this);
1711
1986
  },
1712
1987
  enumerable: false,
1713
1988
  },
1714
1989
  field: {
1715
1990
  value(this: ToolExecuteResult, name: string) {
1716
- return readResultField(this, name);
1991
+ return readField(resultFieldSource(this), name);
1992
+ },
1993
+ enumerable: false,
1994
+ },
1995
+ rows: {
1996
+ value(this: ToolExecuteResult, list?: string) {
1997
+ return readResultRows(this, list);
1717
1998
  },
1718
1999
  enumerable: false,
1719
2000
  },
@@ -428,6 +428,21 @@ function isOtherThing(id: string): boolean {
428
428
  );
429
429
  }
430
430
 
431
+ /**
432
+ * What a list holds, the same on every tool. A tool's list has one of these
433
+ * names by its path or by `listExtractorAliases` (`{ people: 'data' }`). Its
434
+ * extractors then describe items, and its other declared paths are other
435
+ * places the same list can appear.
436
+ */
437
+ export const LIST_NAMES = [
438
+ 'people',
439
+ 'companies',
440
+ 'job_postings',
441
+ 'posts',
442
+ 'podcasts',
443
+ 'pages',
444
+ ] as const;
445
+
431
446
  /** One output of a tool, and the catalog field it is. */
432
447
  export type FieldBinding = { name: string; id: string };
433
448
 
@@ -5,8 +5,20 @@ import type { FIELD_CATALOG } from './field-catalog';
5
5
 
6
6
  type Catalog = typeof FIELD_CATALOG;
7
7
 
8
+ /** One place in `fields`: the one value or null, or every value. */
9
+ export type ToolResultFieldValue<T> = {
10
+ readonly value: T | null;
11
+ readonly values: T[];
12
+ };
13
+
8
14
  type GroupLeaves<TGroup> = TGroup extends { leaves: infer TLeaves }
9
- ? { readonly [Leaf in keyof TLeaves]?: unknown }
15
+ ? {
16
+ readonly [Leaf in keyof TLeaves]?: Leaf extends 'value'
17
+ ? unknown
18
+ : ToolResultFieldValue<unknown>;
19
+ } & (TLeaves extends { value: unknown }
20
+ ? { readonly values?: unknown[] }
21
+ : unknown)
10
22
  : unknown;
11
23
 
12
24
  type CatalogGroupFields<TGroup> = GroupLeaves<TGroup> &
@@ -35,7 +47,7 @@ export type CatalogFields = {
35
47
  * The values a result is known to have, by field catalog id and short name,
36
48
  * each ending in a leaf: `fields.person.email.value`,
37
49
  * `fields.person.email.work.value`, `fields.email.value`. Declared lists are
38
- * at `fields.<list>.value`, as their getter returns them. A value whose
50
+ * at `fields.<list>.values`, as their getter returns them. A value whose
39
51
  * meaning is not known is absent; read it from `response`. Reading a field
40
52
  * two outputs could be throws, naming both.
41
53
  */
@@ -43,8 +55,13 @@ export type ToolResultFields<
43
55
  TExtracted extends Record<string, unknown>,
44
56
  TListValues extends Record<string, unknown>,
45
57
  > = CatalogFields & {
46
- readonly [K in keyof TExtracted]?: { readonly value: TExtracted[K] | null };
47
- } & { readonly [K in keyof TListValues]?: { readonly value: TListValues[K] } };
58
+ readonly [K in keyof TExtracted]?: ToolResultFieldValue<TExtracted[K]>;
59
+ } & {
60
+ readonly [K in keyof TListValues]?: {
61
+ readonly value: unknown;
62
+ readonly values: TListValues[K];
63
+ };
64
+ };
48
65
 
49
66
  /**
50
67
  * Customer pricing reported by the execution API, never provider spend.
@@ -209,6 +226,20 @@ export type BasicToolExecuteResult<
209
226
  > = BasicToolExecuteResultBase<TResult, TMeta> &
210
227
  BasicToolExecuteResultAccessors<TExtracted, TLists>;
211
228
 
229
+ /**
230
+ * One list item in a format that does not depend on the provider. `fields`
231
+ * has every declared field, null when this item has no value. `response` is
232
+ * the provider's whole item, for anything else.
233
+ */
234
+ export type ToolResultRow<
235
+ TFields = CatalogFields &
236
+ Partial<Record<string, ToolResultFieldValue<unknown>>>,
237
+ > = {
238
+ readonly fields: TFields;
239
+ readonly response: unknown;
240
+ field(name: string): unknown;
241
+ };
242
+
212
243
  export type ToolResultMetadataInput = {
213
244
  toolId: string;
214
245
  extractors?: Record<string, ToolResultExtractorDescriptor>;
@@ -308,8 +339,18 @@ export type ToolExecuteResultBase<
308
339
  toolResponse: ToolResponseEnvelope<TResult, TMeta>;
309
340
  extractedValues: Record<string, ToolResultTargetAccessor>;
310
341
  extractedLists: Record<string, ToolResultListAccessor>;
311
- /** One field by id or short name, as `fields` resolves it. */
342
+ /**
343
+ * One field by id or short name, as `fields` resolves it.
344
+ * `field('person.email.value')` is the one value, or null, and throws when
345
+ * there are several. `field('person.email.values')` is always an array.
346
+ */
312
347
  field(name: string): unknown;
348
+ /**
349
+ * One consistent record per item of a list: `rows('people')`. A bare
350
+ * `rows()` works when the tool declares one list. Throws when the list is
351
+ * missing, lazy or incomplete; an absent list is not an empty one.
352
+ */
353
+ rows(list?: string): ToolResultRow[];
313
354
  /** The complete parsed provider response (`toolResponse.rawV2`). */
314
355
  readonly response: unknown;
315
356
  /** Convenience alias for play code. Serialized output uses toolResponse. */
@@ -359,10 +400,14 @@ export type ToolExecuteResultAccessors<
359
400
  * Known values: `fields.person.email.value`, `fields.email.value`, and a
360
401
  * declared list at `fields.<list>.value`; or `field('person.email.value')`.
361
402
  * A value whose meaning is not known is absent: read it from `response`, the
362
- * complete provider response. Metadata: `toolResponse.meta`. The older
403
+ * complete provider response (also `toolResponse.rawV2`). Metadata: `toolResponse.meta`. The older
363
404
  * getters `extractedValues.<name>.get()` and `extractedLists.<name>.get()`
364
405
  * return the same values and keep working.
365
406
  *
407
+ * One record per list item: `rows()`, each with `fields`, `field()` and
408
+ * `response`. `.value` is the one value or null, and throws when there are
409
+ * several rather than miss data. `.values` is always an array.
410
+ *
366
411
  * Edition 10 and later Plays receive ordinary arrays from
367
412
  * list `.get()` synchronously; persist explicitly with deepline.dataset. Lazy or
368
413
  * incomplete lists throw: use readList(result, path) for explicit dataset access.