deepline 0.2.73 → 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 (28) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +75 -4
  2. package/dist/bundling-sources/sdk/src/compat.ts +4 -0
  3. package/dist/bundling-sources/sdk/src/play.ts +9 -0
  4. package/dist/bundling-sources/sdk/src/release.ts +12 -1
  5. package/dist/bundling-sources/sdk/src/types.ts +7 -0
  6. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +159 -32
  7. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +3 -0
  8. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +3 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/secret-capability.ts +32 -7
  10. package/dist/bundling-sources/shared_libs/play-runtime/tool-response-contract.ts +89 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/tool-result-types.ts +29 -3
  12. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +203 -16
  13. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +3 -0
  14. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +6 -3
  15. package/dist/bundling-sources/shared_libs/plays/contracts.ts +14 -0
  16. package/dist/cli/index.js +83 -9
  17. package/dist/cli/index.mjs +83 -9
  18. package/dist/{compiler-manifest-DFBtSjB2.d.mts → compiler-manifest-BX85pKXW.d.mts} +11 -4
  19. package/dist/{compiler-manifest-DFBtSjB2.d.ts → compiler-manifest-BX85pKXW.d.ts} +11 -4
  20. package/dist/index.d.mts +14 -3
  21. package/dist/index.d.ts +14 -3
  22. package/dist/index.js +85 -4
  23. package/dist/index.mjs +85 -4
  24. package/dist/install-integrity.json +3 -2
  25. package/dist/plays/bundle-play-file.d.mts +12 -2
  26. package/dist/plays/bundle-play-file.d.ts +12 -2
  27. package/dist/plays/bundle-play-file.mjs +7 -2
  28. package/package.json +1 -1
@@ -99,12 +99,17 @@ import {
99
99
  TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
100
100
  } from '../../shared_libs/tool-execution-error.js';
101
101
  import { decodePlayRunPublicStatus } from '../../shared_libs/play-runtime/run-lifecycle-policy.js';
102
+ import {
103
+ legacyRawFromToolResponseRawV2,
104
+ providerMetaFromToolResponseRawV2,
105
+ RAW_V2_TOOL_RESPONSE_CONTRACT,
106
+ } from '../../shared_libs/play-runtime/tool-response-contract.js';
102
107
 
103
108
  const TERMINAL_PLAY_STATUSES = new Set(['completed', 'failed', 'cancelled']);
104
109
  const INCLUDE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
105
110
  const EXECUTE_RESPONSE_CONTRACT_HEADER = 'x-deepline-execute-response-contract';
106
111
  const EXECUTE_RESPONSE_INTENT_HEADER = 'x-deepline-execute-response-intent';
107
- const V2_EXECUTE_RESPONSE_CONTRACT = 'v2-tool-response';
112
+ const RAW_V2_EXECUTE_RESPONSE_CONTRACT = RAW_V2_TOOL_RESPONSE_CONTRACT;
108
113
  const COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1_000];
109
114
  const REGISTER_PLAY_ARTIFACTS_COMPILE_CONCURRENCY = 3;
110
115
  const REGISTER_PLAY_ARTIFACTS_MAX_BATCH_COUNT = 3;
@@ -452,7 +457,8 @@ function resolveToolExecuteTimeoutMs(
452
457
  /**
453
458
  * Standard provider/tool execution envelope returned by low-level SDK calls.
454
459
  *
455
- * `toolResponse.raw` contains the provider result. `extractedValues` and
460
+ * `toolResponse.rawV2` contains the complete scrubbed provider response;
461
+ * `toolResponse.raw` is derived locally as the legacy provider-result projection. `extractedValues` and
456
462
  * `extractedLists` contain Deepline-normalized getters when the tool exposes
457
463
  * them. Billing fields are Deepline-facing and must not expose provider spend.
458
464
  */
@@ -462,7 +468,10 @@ export type ToolExecution<TData = unknown, TMeta = Record<string, unknown>> = {
462
468
  meta?: Record<string, unknown>;
463
469
  toolResponse: {
464
470
  raw: TData;
471
+ rawV2?: unknown;
472
+ view?: 'data' | 'rawV2';
465
473
  meta?: TMeta;
474
+ responseMeta?: TMeta;
466
475
  };
467
476
  extractedLists?: Record<string, unknown>;
468
477
  extractedValues?: Record<string, unknown>;
@@ -1147,6 +1156,67 @@ function isRecord(value: unknown): value is Record<string, unknown> {
1147
1156
  return Boolean(value && typeof value === 'object' && !Array.isArray(value));
1148
1157
  }
1149
1158
 
1159
+ /** Materialize raw-v2's rawV2-only wire response into the stable SDK result view. */
1160
+ function materializeToolExecutionResponse<
1161
+ TData = unknown,
1162
+ TMeta = Record<string, unknown>,
1163
+ >(response: ToolExecution<TData, TMeta>): ToolExecution<TData, TMeta> {
1164
+ const toolResponse = response.toolResponse;
1165
+ // An older V2 backend does not understand raw-v2 yet and therefore falls
1166
+ // through to the historical `{ result: { data, meta? } }` response. New SDK
1167
+ // clients must keep working during that server rollout window.
1168
+ if (!toolResponse && isRecord(response.result)) {
1169
+ const legacyResult = response.result;
1170
+ if (Object.prototype.hasOwnProperty.call(legacyResult, 'data')) {
1171
+ const legacyMeta = isRecord(legacyResult.meta)
1172
+ ? (legacyResult.meta as TMeta)
1173
+ : undefined;
1174
+ return {
1175
+ ...response,
1176
+ toolResponse: {
1177
+ raw: legacyResult.data as TData,
1178
+ ...(legacyMeta ? { meta: legacyMeta } : {}),
1179
+ },
1180
+ };
1181
+ }
1182
+ }
1183
+ if (
1184
+ !toolResponse ||
1185
+ Object.prototype.hasOwnProperty.call(toolResponse, 'raw')
1186
+ ) {
1187
+ return response;
1188
+ }
1189
+ const rawV2 = toolResponse.rawV2;
1190
+ const view = toolResponse.view;
1191
+ const providerMeta = providerMetaFromToolResponseRawV2(
1192
+ rawV2,
1193
+ view ?? 'rawV2',
1194
+ ) as TMeta | undefined;
1195
+ const responseMeta = isRecord(toolResponse.responseMeta)
1196
+ ? (toolResponse.responseMeta as TMeta)
1197
+ : undefined;
1198
+ return {
1199
+ ...response,
1200
+ toolResponse: {
1201
+ ...toolResponse,
1202
+ raw: legacyRawFromToolResponseRawV2(
1203
+ rawV2,
1204
+ view ?? 'rawV2',
1205
+ responseMeta as Record<string, unknown> | undefined,
1206
+ ) as TData,
1207
+ ...(toolResponse.meta || providerMeta || responseMeta
1208
+ ? {
1209
+ meta: {
1210
+ ...(toolResponse.meta ?? {}),
1211
+ ...(providerMeta ?? {}),
1212
+ ...(responseMeta ?? {}),
1213
+ } as TMeta,
1214
+ }
1215
+ : {}),
1216
+ },
1217
+ };
1218
+ }
1219
+
1150
1220
  function isPrebuiltPlayDescription(
1151
1221
  play: Pick<PlayDescription, 'origin' | 'ownerType'>,
1152
1222
  ): boolean {
@@ -2019,7 +2089,7 @@ export class DeeplineClient {
2019
2089
  options?: ExecuteToolRawOptions,
2020
2090
  ): Promise<ToolExecution<TData, TMeta>> {
2021
2091
  const headers = {
2022
- [EXECUTE_RESPONSE_CONTRACT_HEADER]: V2_EXECUTE_RESPONSE_CONTRACT,
2092
+ [EXECUTE_RESPONSE_CONTRACT_HEADER]: RAW_V2_EXECUTE_RESPONSE_CONTRACT,
2023
2093
  [TOOL_EXECUTION_ERROR_SCHEMA_HEADER]: String(
2024
2094
  TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
2025
2095
  ),
@@ -2028,7 +2098,7 @@ export class DeeplineClient {
2028
2098
  : {}),
2029
2099
  [EXECUTE_RESPONSE_INTENT_HEADER]: options?.responseIntent ?? 'raw',
2030
2100
  };
2031
- return this.http.post<ToolExecution<TData, TMeta>>(
2101
+ const response = await this.http.post<ToolExecution<TData, TMeta>>(
2032
2102
  `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
2033
2103
  {
2034
2104
  payload: input,
@@ -2042,6 +2112,7 @@ export class DeeplineClient {
2042
2112
  toolId,
2043
2113
  },
2044
2114
  );
2115
+ return materializeToolExecutionResponse(response);
2045
2116
  }
2046
2117
 
2047
2118
  /**
@@ -22,6 +22,10 @@ export type SdkCompatibilityResponse = {
22
22
  update_required: boolean;
23
23
  message: string;
24
24
  update_command: string;
25
+ update_summary?: {
26
+ version: string;
27
+ summary: string;
28
+ };
25
29
  command?: string | null;
26
30
  /**
27
31
  * Present only when the queried command postdates this CLI. Advisory: the
@@ -149,6 +149,7 @@ import type {
149
149
  PlayLooseObject,
150
150
  PlayReturnObject as PlayAuthoringReturnObject,
151
151
  PlaySecretAuth,
152
+ PlaySecretAuthInput,
152
153
  PlaySecretAwareRequestInit,
153
154
  PlaySecretHandle,
154
155
  PlaySqlQuery,
@@ -249,6 +250,7 @@ export type SqlListenerEvent<T extends object = Record<string, unknown>> =
249
250
  export type SqlQuery = PlaySqlQuery;
250
251
  export type SecretHandle = PlaySecretHandle;
251
252
  export type SecretAuth = PlaySecretAuth;
253
+ export type SecretAuthInput = PlaySecretAuthInput;
252
254
  export type SecretAwareRequestInit = PlaySecretAwareRequestInit;
253
255
  export type LoosePlayObject = PlayLooseObject;
254
256
 
@@ -1469,6 +1471,8 @@ function toolExecutionEnvelopeToResult(
1469
1471
  },
1470
1472
  ): ToolExecuteResult {
1471
1473
  const raw = response.toolResponse?.raw ?? null;
1474
+ const rawV2 = response.toolResponse?.rawV2;
1475
+ const view = response.toolResponse?.view;
1472
1476
  const meta = response.toolResponse?.meta;
1473
1477
  const metadata = isRecord(response._metadata)
1474
1478
  ? response._metadata.tool
@@ -1485,6 +1489,11 @@ function toolExecutionEnvelopeToResult(
1485
1489
  data: raw,
1486
1490
  ...(isRecord(meta) ? { meta } : {}),
1487
1491
  },
1492
+ response: {
1493
+ ...(rawV2 !== undefined ? { rawV2 } : {}),
1494
+ ...(view === 'data' || view === 'rawV2' ? { view } : {}),
1495
+ ...(isRecord(meta) ? { meta } : {}),
1496
+ },
1488
1497
  metadata: {
1489
1498
  toolId:
1490
1499
  typeof toolMetadata.toolId === 'string'
@@ -136,6 +136,12 @@ export type SdkRelease = {
136
136
  * publish time (auto-bump); edit by hand only for minor/major releases.
137
137
  */
138
138
  version: string;
139
+ /**
140
+ * One concise, agent-readable explanation shown while this release is an
141
+ * available update. Keep it actionable; the compatibility route includes it
142
+ * in its existing update message so older installed CLIs can print it too.
143
+ */
144
+ updateSummary?: string;
139
145
  /** Named compatibility policies. This is the only authored contract policy. */
140
146
  contracts: DeeplineContractPolicy;
141
147
  /** Public support policy reported by `/api/v2/sdk/compat`. */
@@ -183,7 +189,12 @@ export const SDK_RELEASE = {
183
189
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
184
190
  // exposed storage-dependent synchronous access. This deliberate minor
185
191
  // release keeps lazy paging semantics independent of row residency.
186
- version: '0.2.73',
192
+ // 0.3.0 introduces raw-v2: complete scrubbed provider responses are
193
+ // available at toolResponse.rawV2 while toolResponse.raw and all declared
194
+ // getters keep their established compatibility behavior.
195
+ version: '0.3.0',
196
+ updateSummary:
197
+ 'New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.',
187
198
  contracts: {
188
199
  api: {
189
200
  name: 'sdk-http-api',
@@ -212,12 +212,19 @@ export interface ToolDefinition {
212
212
  expression?: string;
213
213
  meaning?: string;
214
214
  };
215
+ canonicalToolResponse?: {
216
+ expression?: string;
217
+ meaning?: string;
218
+ };
215
219
  invalidGetterHint?: string;
216
220
  };
217
221
  toolExecutionResult?: {
218
222
  type?: 'ToolExecutionResult';
219
223
  toolResponse?: {
220
224
  raw?: string;
225
+ rawV2?: string;
226
+ view?: string;
227
+ responseMeta?: string;
221
228
  meta?: string;
222
229
  };
223
230
  meta?: string;
@@ -94,6 +94,14 @@ import {
94
94
  type RuntimeResourceGovernor,
95
95
  } from './resource-governor';
96
96
  import { CTX_FETCH_EGRESS_TOOL_ID } from './builtin-pacing';
97
+ import {
98
+ legacyRawFromToolResponseRawV2,
99
+ normalizeToolResponseContract,
100
+ providerMetaFromToolResponseRawV2,
101
+ RAW_V2_TOOL_RESPONSE_CONTRACT,
102
+ type ToolResponseContract,
103
+ type ToolResponseView,
104
+ } from './tool-response-contract';
97
105
  import { ProviderExhaustedError } from './run-failure';
98
106
  import {
99
107
  buildPlayContractCompatibility,
@@ -255,10 +263,12 @@ import {
255
263
  createBearerSecretAuth,
256
264
  createHeaderSecretAuth,
257
265
  createSecretHandle,
258
- isSecretAuth,
266
+ isSecretAuthInput,
267
+ secretAuthEntries,
259
268
  secretAuthHeaderMarkers,
260
269
  valueContainsSecret,
261
270
  type SecretAuth,
271
+ type SecretAuthInput,
262
272
  type SecretAwareRequestInit,
263
273
  type SecretHandle,
264
274
  } from './secret-capability';
@@ -355,6 +365,8 @@ type InlineCompositionStore = {
355
365
  toolErrorSchemaVersion: ToolExecutionErrorSchemaVersion;
356
366
  /** Immutable authoring semantics pinned by the child play artifact. */
357
367
  authoringContractEdition: PlayAuthoringContractEdition;
368
+ /** Immutable execute response contract pinned by the child artifact. */
369
+ toolResponseContract: ToolResponseContract;
358
370
  };
359
371
  const inlineCompositionContext =
360
372
  new AsyncLocalStorage<InlineCompositionStore>();
@@ -1114,6 +1126,8 @@ function isUnsafeOutboundUrlError(error: unknown): boolean {
1114
1126
  function publicToolResponseEnvelope(value: unknown): {
1115
1127
  status: string;
1116
1128
  raw: unknown;
1129
+ rawV2?: unknown;
1130
+ view?: ToolResponseView;
1117
1131
  meta?: Record<string, unknown>;
1118
1132
  } | null {
1119
1133
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
@@ -1125,27 +1139,100 @@ function publicToolResponseEnvelope(value: unknown): {
1125
1139
  if (
1126
1140
  !toolResponse ||
1127
1141
  typeof toolResponse !== 'object' ||
1128
- Array.isArray(toolResponse) ||
1129
- !Object.prototype.hasOwnProperty.call(toolResponse, 'raw')
1142
+ Array.isArray(toolResponse)
1130
1143
  ) {
1131
1144
  return null;
1132
1145
  }
1133
1146
  const response = toolResponse as Record<string, unknown>;
1134
- return {
1135
- status: record.status,
1136
- raw: response.raw,
1137
- ...(response.meta &&
1147
+ const rawV2 = Object.prototype.hasOwnProperty.call(response, 'rawV2')
1148
+ ? response.rawV2
1149
+ : undefined;
1150
+ const view =
1151
+ response.view === 'data' || response.view === 'rawV2'
1152
+ ? response.view
1153
+ : undefined;
1154
+ if (
1155
+ !Object.prototype.hasOwnProperty.call(response, 'raw') &&
1156
+ rawV2 === undefined
1157
+ ) {
1158
+ return null;
1159
+ }
1160
+ const providerMeta = providerMetaFromToolResponseRawV2(
1161
+ rawV2,
1162
+ view ?? 'rawV2',
1163
+ );
1164
+ const toolResponseMeta =
1165
+ response.meta &&
1138
1166
  typeof response.meta === 'object' &&
1139
1167
  !Array.isArray(response.meta)
1140
- ? { meta: response.meta as Record<string, unknown> }
1168
+ ? (response.meta as Record<string, unknown>)
1169
+ : {};
1170
+ const responseMeta =
1171
+ response.responseMeta &&
1172
+ typeof response.responseMeta === 'object' &&
1173
+ !Array.isArray(response.responseMeta)
1174
+ ? (response.responseMeta as Record<string, unknown>)
1175
+ : {};
1176
+ return {
1177
+ status: record.status,
1178
+ raw: Object.prototype.hasOwnProperty.call(response, 'raw')
1179
+ ? response.raw
1180
+ : legacyRawFromToolResponseRawV2(rawV2, view ?? 'rawV2', responseMeta),
1181
+ ...(rawV2 !== undefined ? { rawV2 } : {}),
1182
+ ...(view ? { view } : {}),
1183
+ ...(Object.keys({ ...toolResponseMeta, ...providerMeta, ...responseMeta })
1184
+ .length > 0
1185
+ ? { meta: { ...toolResponseMeta, ...providerMeta, ...responseMeta } }
1141
1186
  : {}),
1142
1187
  };
1143
1188
  }
1144
1189
 
1190
+ /**
1191
+ * A batched provider request returns one envelope for several logical source
1192
+ * tool calls. Each source call persists only its own item-shaped canonical
1193
+ * response: retaining the aggregate envelope would make its legacy `raw`
1194
+ * projection change on receipt replay and duplicate every batch for every row.
1195
+ */
1196
+ function publicToolResponseForBatchedItem(
1197
+ execution: ParsedToolExecuteResponse,
1198
+ result: unknown,
1199
+ forceRawV2: boolean,
1200
+ ): ParsedToolExecuteResponse['toolResponse'] | undefined {
1201
+ const providerMeta = execution.toolResponse?.meta;
1202
+ if (
1203
+ !forceRawV2 &&
1204
+ (!execution.toolResponse ||
1205
+ !Object.prototype.hasOwnProperty.call(execution.toolResponse, 'rawV2'))
1206
+ ) {
1207
+ return undefined;
1208
+ }
1209
+ return {
1210
+ rawV2: { data: result },
1211
+ view: 'data',
1212
+ ...(providerMeta ? { meta: providerMeta } : {}),
1213
+ };
1214
+ }
1215
+
1216
+ /**
1217
+ * Batch splitters predate raw-v2 and receive the same legacy value they got
1218
+ * from callToolAPI. Keep that input contract stable; raw-v2 is attached to the
1219
+ * completed logical call separately by publicToolResponseForBatchedItem.
1220
+ */
1221
+ function legacyResultForBatchSplitter(execution: ParsedToolExecuteResponse): unknown {
1222
+ if (execution.toolResponse && 'raw' in execution.toolResponse) {
1223
+ return execution.toolResponse.raw;
1224
+ }
1225
+ return execution.result != null &&
1226
+ typeof execution.result === 'object' &&
1227
+ !Array.isArray(execution.result) &&
1228
+ 'data' in execution.result
1229
+ ? execution.result.data
1230
+ : execution.result;
1231
+ }
1232
+
1145
1233
  const EXECUTE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
1146
1234
  const EXECUTE_RESPONSE_CONTRACT_HEADER = 'x-deepline-execute-response-contract';
1147
1235
  const EXECUTE_RESPONSE_INTENT_HEADER = 'x-deepline-execute-response-intent';
1148
- const V2_EXECUTE_RESPONSE_CONTRACT = 'v2-tool-response';
1149
1236
  function recordOrNull(value: unknown): Record<string, unknown> | null {
1150
1237
  return value && typeof value === 'object' && !Array.isArray(value)
1151
1238
  ? (value as Record<string, unknown>)
@@ -2516,6 +2603,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
2516
2603
  return TOOL_EXECUTION_ERROR_SCHEMA_VERSION;
2517
2604
  }
2518
2605
 
2606
+ private get currentToolResponseContract(): ToolResponseContract {
2607
+ return normalizeToolResponseContract(
2608
+ this.activeInlineComposition?.toolResponseContract ??
2609
+ this.#options.toolResponseContract,
2610
+ );
2611
+ }
2612
+
2519
2613
  private get currentAuthoringContractEdition(): PlayAuthoringContractEdition {
2520
2614
  return (
2521
2615
  this.activeInlineComposition?.authoringContractEdition ??
@@ -2676,7 +2770,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
2676
2770
  }
2677
2771
  }
2678
2772
 
2679
- private async resolveSecretAuth(auth: SecretAuth | undefined) {
2773
+ private async resolveSecretAuth(auth: SecretAuthInput | undefined) {
2774
+ const headers: Record<string, string> = {};
2775
+ for (const entry of secretAuthEntries(auth)) {
2776
+ Object.assign(headers, await this.resolveSingleSecretAuth(entry));
2777
+ }
2778
+ return headers;
2779
+ }
2780
+
2781
+ private async resolveSingleSecretAuth(auth: SecretAuth) {
2680
2782
  if (!auth) return {};
2681
2783
  let value: string | null = null;
2682
2784
  if (this.#options.resolveSecret) {
@@ -4965,6 +5067,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
4965
5067
  result: unknown;
4966
5068
  metadata?: ToolResultMetadataInput | null;
4967
5069
  meta?: Record<string, unknown>;
5070
+ toolResponse?: {
5071
+ raw?: unknown;
5072
+ rawV2?: unknown;
5073
+ view?: 'data' | 'rawV2';
5074
+ meta?: Record<string, unknown>;
5075
+ };
4968
5076
  execution: ToolResultExecutionMetadata;
4969
5077
  requestInput?: Record<string, unknown>;
4970
5078
  }): Promise<ToolExecuteResult> {
@@ -4988,6 +5096,18 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
4988
5096
  ...(publicToolResult.meta ? { meta: publicToolResult.meta } : {}),
4989
5097
  }
4990
5098
  : input.result,
5099
+ response: publicToolResult
5100
+ ? {
5101
+ ...(Object.prototype.hasOwnProperty.call(
5102
+ publicToolResult,
5103
+ 'rawV2',
5104
+ )
5105
+ ? { rawV2: publicToolResult.rawV2 }
5106
+ : {}),
5107
+ ...(publicToolResult.view ? { view: publicToolResult.view } : {}),
5108
+ ...(publicToolResult.meta ? { meta: publicToolResult.meta } : {}),
5109
+ }
5110
+ : input.toolResponse,
4991
5111
  metadata:
4992
5112
  input.metadata ??
4993
5113
  (await this.resolveToolResultMetadata(input.toolId)),
@@ -5126,6 +5246,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
5126
5246
  metadata?: ToolResultMetadataInput | null,
5127
5247
  jobId?: string,
5128
5248
  meta?: Record<string, unknown>,
5249
+ toolResponse?: ParsedToolExecuteResponse['toolResponse'],
5129
5250
  ): Promise<unknown> {
5130
5251
  const cacheKey = request.cacheKey;
5131
5252
  const receiptKey = request.receiptKey?.trim() || null;
@@ -5136,6 +5257,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
5136
5257
  result,
5137
5258
  metadata,
5138
5259
  meta,
5260
+ toolResponse,
5139
5261
  execution: toolExecutionMetadataForOutcome({
5140
5262
  kind: 'live',
5141
5263
  cacheKey,
@@ -5186,6 +5308,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
5186
5308
  metadata?: ToolResultMetadataInput | null;
5187
5309
  jobId?: string;
5188
5310
  meta?: Record<string, unknown>;
5311
+ toolResponse?: ParsedToolExecuteResponse['toolResponse'];
5189
5312
  }>,
5190
5313
  ): Promise<unknown[]> {
5191
5314
  const wrappedEntries = await Promise.all(
@@ -5198,6 +5321,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
5198
5321
  result: entry.result,
5199
5322
  metadata: entry.metadata,
5200
5323
  meta: entry.meta,
5324
+ toolResponse: entry.toolResponse,
5201
5325
  execution: toolExecutionMetadataForOutcome({
5202
5326
  kind: 'live',
5203
5327
  cacheKey: entry.request.cacheKey,
@@ -8931,6 +9055,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
8931
9055
  status: execution.status,
8932
9056
  jobId: execution.jobId,
8933
9057
  result: execution.result,
9058
+ toolResponse: execution.toolResponse,
8934
9059
  metadata: execution.metadata,
8935
9060
  meta: execution.meta,
8936
9061
  requestInput: input,
@@ -9328,6 +9453,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
9328
9453
  );
9329
9454
  const childToolErrorSchemaVersion =
9330
9455
  childCompatibility.toolErrorSchemaVersion;
9456
+ const childToolResponseContract = childCompatibility.toolResponseContract;
9331
9457
  const childExecutionDecision = resolveChildExecutionStrategy({
9332
9458
  pipeline: resolvedPlay.staticPipeline,
9333
9459
  timeoutMs: options?.timeoutMs,
@@ -9400,6 +9526,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
9400
9526
  playName: resolvedName,
9401
9527
  staticPipeline: resolvedPlay.staticPipeline ?? null,
9402
9528
  toolErrorSchemaVersion: childToolErrorSchemaVersion,
9529
+ toolResponseContract: childToolResponseContract,
9403
9530
  authoringContractEdition:
9404
9531
  childCompatibility.authoringContractEdition,
9405
9532
  },
@@ -9648,7 +9775,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
9648
9775
  'ctx.fetch does not allow raw secret headers. Use ctx.secrets.bearer(...) or ctx.secrets.header(...).',
9649
9776
  );
9650
9777
  }
9651
- if (init.auth !== undefined && !isSecretAuth(init.auth)) {
9778
+ if (init.auth !== undefined && !isSecretAuthInput(init.auth)) {
9652
9779
  throw new Error('ctx.fetch auth must come from ctx.secrets.');
9653
9780
  }
9654
9781
  // Secret handles are deliberately resolved at the last possible moment, so
@@ -10648,6 +10775,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10648
10775
  execution?.metadata ?? null,
10649
10776
  execution?.jobId,
10650
10777
  execution?.meta,
10778
+ execution?.toolResponse,
10651
10779
  );
10652
10780
  if (result != null) {
10653
10781
  successfulLiveStepCallIds.add(owner.callId);
@@ -10974,7 +11102,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10974
11102
  });
10975
11103
  let releaseToolSlot: () => void = () => undefined;
10976
11104
  try {
10977
- const value = await this.callToolAPI(
11105
+ const execution = await this.callToolExecutionAPI(
10978
11106
  batch.batchOperation,
10979
11107
  batch.batchPayload,
10980
11108
  {
@@ -11033,7 +11161,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11033
11161
  },
11034
11162
  },
11035
11163
  );
11036
- return { value, releaseToolSlot };
11164
+ return { execution, releaseToolSlot };
11037
11165
  } catch (error) {
11038
11166
  releaseToolSlot();
11039
11167
  throw error;
@@ -11050,7 +11178,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11050
11178
  try {
11051
11179
  const splitResults =
11052
11180
  entry.result != null
11053
- ? entry.request.splitResults(entry.result.value)
11181
+ ? entry.request.splitResults(
11182
+ legacyResultForBatchSplitter(
11183
+ entry.result.execution,
11184
+ ),
11185
+ )
11054
11186
  : entry.request.memberRequests.map(() => null);
11055
11187
  const resolvedResults =
11056
11188
  await this.resolveToolCallBatchResults(
@@ -11059,6 +11191,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11059
11191
  (request, index) => ({
11060
11192
  request,
11061
11193
  result: splitResults[index] ?? null,
11194
+ toolResponse:
11195
+ entry.result == null
11196
+ ? undefined
11197
+ : publicToolResponseForBatchedItem(
11198
+ entry.result.execution,
11199
+ splitResults[index] ?? null,
11200
+ this.currentToolResponseContract ===
11201
+ RAW_V2_TOOL_RESPONSE_CONTRACT,
11202
+ ),
11062
11203
  }),
11063
11204
  ),
11064
11205
  );
@@ -11095,6 +11236,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11095
11236
  metadata?: ToolResultMetadataInput | null;
11096
11237
  jobId?: string;
11097
11238
  meta?: Record<string, unknown>;
11239
+ toolResponse?: ParsedToolExecuteResponse['toolResponse'];
11098
11240
  resolve: (value: unknown) => void;
11099
11241
  reject: (error: unknown) => void;
11100
11242
  }> = [];
@@ -11113,6 +11255,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11113
11255
  metadata: entry.metadata,
11114
11256
  jobId: entry.jobId,
11115
11257
  meta: entry.meta,
11258
+ toolResponse: entry.toolResponse,
11116
11259
  })),
11117
11260
  );
11118
11261
  for (let index = 0; index < entries.length; index += 1) {
@@ -11145,6 +11288,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11145
11288
  metadata: execution.metadata ?? null,
11146
11289
  jobId: execution.jobId,
11147
11290
  meta: execution.meta,
11291
+ toolResponse: execution.toolResponse,
11148
11292
  resolve,
11149
11293
  reject,
11150
11294
  });
@@ -11359,23 +11503,6 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11359
11503
  )(ctx, input);
11360
11504
  }
11361
11505
 
11362
- private async callToolAPI(
11363
- toolId: string,
11364
- input: Record<string, unknown>,
11365
- options?: ToolExecutionApiOptions,
11366
- ): Promise<unknown> {
11367
- const execution = await this.callToolExecutionAPI(toolId, input, options);
11368
- if (execution.toolResponse && 'raw' in execution.toolResponse) {
11369
- return execution.toolResponse.raw;
11370
- }
11371
- return execution.result != null &&
11372
- typeof execution.result === 'object' &&
11373
- !Array.isArray(execution.result) &&
11374
- 'data' in execution.result
11375
- ? execution.result.data
11376
- : execution.result;
11377
- }
11378
-
11379
11506
  private providerIdempotencyKeyForToolCall(input: {
11380
11507
  cacheKey: string;
11381
11508
  force?: boolean;
@@ -11863,7 +11990,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11863
11990
  'Content-Type': 'application/json',
11864
11991
  Authorization: `Bearer ${this.#options.executorToken}`,
11865
11992
  [EXECUTE_RESPONSE_CONTRACT_HEADER]:
11866
- V2_EXECUTE_RESPONSE_CONTRACT,
11993
+ this.currentToolResponseContract,
11867
11994
  [EXECUTE_RESPONSE_INTENT_HEADER]: 'dataset',
11868
11995
  [EXECUTE_TOOL_METADATA_HEADER]: 'true',
11869
11996
  [TOOL_EXECUTION_ERROR_SCHEMA_HEADER]: String(
@@ -51,6 +51,7 @@ import type {
51
51
  ToolExecutionFailureV1,
52
52
  } from '../tool-execution-error';
53
53
  import type { FixtureBehavior } from './fixture-behavior';
54
+ import type { ToolResponseContract } from './tool-response-contract';
54
55
 
55
56
  export interface RowState {
56
57
  results: Map<string, unknown>;
@@ -572,6 +573,8 @@ export interface ContextOptions {
572
573
  authoringContractEdition?: PlayAuthoringContractEdition;
573
574
  /** Error shape pinned by the immutable play artifact; missing preserves legacy schema 0. */
574
575
  toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion;
576
+ /** Execute-result contract pinned by the immutable Play artifact. */
577
+ toolResponseContract?: ToolResponseContract;
575
578
  /** Short-lived HMAC-signed internal token for tool callbacks. Required for cloud execution. */
576
579
  executorToken?: string;
577
580
  baseUrl?: string;
@@ -15,6 +15,7 @@ import type { PlaySandboxRuntimeLimits } from './sandbox-runtime-limits';
15
15
  import type { PlayRunInputPayload } from './play-input';
16
16
  import type { PlayRunFailureDetails } from './run-failure';
17
17
  import type { ToolExecutionErrorSchemaVersion } from '../tool-execution-error';
18
+ import type { ToolResponseContract } from './tool-response-contract';
18
19
  import type { FixtureBehavior } from './fixture-behavior';
19
20
 
20
21
  export type PlayRunnerRateStateBackendConfig =
@@ -138,6 +139,8 @@ export interface PlayRunnerContextConfig {
138
139
  maxConcurrentRows?: number | null;
139
140
  /** Immutable tool-error payload schema copied from the run contract. */
140
141
  toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion;
142
+ /** Immutable execute response contract copied from the Play artifact. */
143
+ toolResponseContract?: ToolResponseContract;
141
144
  orgId?: string;
142
145
  workflowId?: string;
143
146
  playId?: string;