deepline 0.3.16 → 0.3.17
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/bundling-sources/sdk/src/client.ts +7 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/integrations/bettercontact-execution-policy.ts +29 -0
- package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +19 -3
- package/dist/bundling-sources/shared_libs/play-runtime/bettercontact-batching.ts +726 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +13 -6
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +2 -0
- package/dist/cli/index.js +14 -1
- package/dist/cli/index.mjs +14 -1
- package/dist/index.js +14 -1
- package/dist/index.mjs +14 -1
- package/dist/install-integrity.json +2 -0
- package/package.json +1 -1
|
@@ -88,6 +88,10 @@ import type { PlayCompilerManifest } from '../../shared_libs/plays/compiler-mani
|
|
|
88
88
|
import type { EnrichCompiledConfig } from './cli/enrich-play-compiler.js';
|
|
89
89
|
import { RUNTIME_ENVIRONMENT_TOKEN_HEADER } from '../../shared_libs/play-runtime/coordinator-headers.js';
|
|
90
90
|
import { resolveTheirstackClientTimeoutMs } from '../../shared_libs/integrations/theirstack-execution-policy.js';
|
|
91
|
+
import {
|
|
92
|
+
BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS,
|
|
93
|
+
usesExtendedBetterContactLauncherBudget,
|
|
94
|
+
} from '../../shared_libs/integrations/bettercontact-execution-policy.js';
|
|
91
95
|
import {
|
|
92
96
|
normalizePlayRuntimeEnvironment,
|
|
93
97
|
normalizePlayRuntimeNamespace,
|
|
@@ -460,6 +464,9 @@ function resolveToolExecuteTimeoutMs(
|
|
|
460
464
|
input,
|
|
461
465
|
);
|
|
462
466
|
if (theirstackTimeoutMs !== null) return theirstackTimeoutMs;
|
|
467
|
+
if (usesExtendedBetterContactLauncherBudget(normalized)) {
|
|
468
|
+
return BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS;
|
|
469
|
+
}
|
|
463
470
|
return normalized === 'deeplineagent' ||
|
|
464
471
|
normalized === 'deeplineagent_deeplineagent' ||
|
|
465
472
|
normalized === 'ai_inference' ||
|
|
@@ -192,7 +192,7 @@ export const SDK_RELEASE = {
|
|
|
192
192
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
193
193
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
194
194
|
// getters keep their established compatibility behavior.
|
|
195
|
-
version: '0.3.
|
|
195
|
+
version: '0.3.17',
|
|
196
196
|
updateSummary:
|
|
197
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.',
|
|
198
198
|
contracts: {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BetterContact launchers are upstream-async but Deepline gives callers a
|
|
3
|
+
* bounded synchronous convenience wait before returning a pollable request
|
|
4
|
+
* id. Keep the provider and SDK budgets together so the client cannot abandon
|
|
5
|
+
* a launched job while the server is still inside that documented path.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Per-request upstream timeout used by BetterContact launch and poll calls. */
|
|
9
|
+
export const BETTERCONTACT_UPSTREAM_REQUEST_TIMEOUT_MS = 30_000;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Client budget for a BetterContact launcher response.
|
|
13
|
+
*
|
|
14
|
+
* Covers route cold start, the bounded synchronous wait, one in-flight
|
|
15
|
+
* upstream request, and response serialization without widening unrelated
|
|
16
|
+
* tool requests.
|
|
17
|
+
*/
|
|
18
|
+
export const BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS = 120_000;
|
|
19
|
+
|
|
20
|
+
const BETTERCONTACT_LAUNCHER_TOOL_IDS = new Set([
|
|
21
|
+
'bettercontact_enrich',
|
|
22
|
+
'bettercontact_bulk_enrich',
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
export function usesExtendedBetterContactLauncherBudget(
|
|
26
|
+
toolId: string,
|
|
27
|
+
): boolean {
|
|
28
|
+
return BETTERCONTACT_LAUNCHER_TOOL_IDS.has(toolId.trim().toLowerCase());
|
|
29
|
+
}
|
|
@@ -102,16 +102,31 @@ export async function executeChunkedRequests<TRequest, TResult>(input: {
|
|
|
102
102
|
input.weightOf,
|
|
103
103
|
);
|
|
104
104
|
for (const chunk of chunks) {
|
|
105
|
+
// notifyChain serializes onChunkComplete calls (the caller's callback can
|
|
106
|
+
// touch shared, non-concurrency-safe state) without letting one entry's
|
|
107
|
+
// callback failure skip a sibling's callback. A naive `.then()` chain
|
|
108
|
+
// would propagate a rejection forward and silently drop every queued
|
|
109
|
+
// notify() behind it -- for BetterContact/native-batch callers that
|
|
110
|
+
// means a correlation-mismatch throw on one row would leak the tool
|
|
111
|
+
// slots of every other row in the same chunk, since their
|
|
112
|
+
// onChunkComplete (and its `finally { releaseToolSlot() }`) would never
|
|
113
|
+
// run. Each callback failure is caught and re-thrown only after every
|
|
114
|
+
// queued entry in the chunk has had its callback invoked.
|
|
105
115
|
let notifyChain: Promise<void> = Promise.resolve();
|
|
116
|
+
let firstCallbackFailure: unknown;
|
|
106
117
|
const notify = async (
|
|
107
118
|
entry: ChunkExecutionResult<TRequest, TResult>,
|
|
108
119
|
): Promise<void> => {
|
|
109
120
|
if (input.retainResults !== false) {
|
|
110
121
|
results.push(entry);
|
|
111
122
|
}
|
|
112
|
-
notifyChain = notifyChain.then(
|
|
113
|
-
|
|
114
|
-
|
|
123
|
+
notifyChain = notifyChain.then(async () => {
|
|
124
|
+
try {
|
|
125
|
+
await input.onChunkComplete?.([entry]);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
firstCallbackFailure ??= error;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
115
130
|
await notifyChain;
|
|
116
131
|
};
|
|
117
132
|
|
|
@@ -135,6 +150,7 @@ export async function executeChunkedRequests<TRequest, TResult>(input: {
|
|
|
135
150
|
}),
|
|
136
151
|
);
|
|
137
152
|
await notifyChain;
|
|
153
|
+
if (firstCallbackFailure !== undefined) throw firstCallbackFailure;
|
|
138
154
|
}
|
|
139
155
|
|
|
140
156
|
return results;
|
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineBatchStrategyMap,
|
|
3
|
+
type BatchOperationStrategy,
|
|
4
|
+
} from './batching-types';
|
|
5
|
+
|
|
6
|
+
const BETTERCONTACT_BATCH_ITEM_KEY = 'deepline_batch_item_key';
|
|
7
|
+
const BETTERCONTACT_BATCH_SIZE = 100;
|
|
8
|
+
const BETTERCONTACT_SCOPED_REQUEST_PREFIX = 'deepline-bc-batch-v1:';
|
|
9
|
+
const BETTERCONTACT_DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
10
|
+
const BETTERCONTACT_MAX_SYNC_WAIT_MS = 30_000;
|
|
11
|
+
const BETTERCONTACT_SINGLE_PAYLOAD_FIELDS = new Set([
|
|
12
|
+
'first_name',
|
|
13
|
+
'last_name',
|
|
14
|
+
'company_domain',
|
|
15
|
+
'company',
|
|
16
|
+
'linkedin_url',
|
|
17
|
+
'custom_fields',
|
|
18
|
+
'enrich_email_address',
|
|
19
|
+
'enrich_phone_number',
|
|
20
|
+
'wait_for_completion',
|
|
21
|
+
'poll_interval_ms',
|
|
22
|
+
'max_wait_ms',
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
type BetterContactCustomFields = Record<string, unknown>;
|
|
26
|
+
|
|
27
|
+
type BetterContactContact = {
|
|
28
|
+
first_name: string;
|
|
29
|
+
last_name: string;
|
|
30
|
+
company_domain?: string;
|
|
31
|
+
company?: string;
|
|
32
|
+
linkedin_url?: string;
|
|
33
|
+
custom_fields?: BetterContactCustomFields;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type BetterContactControls = {
|
|
37
|
+
enrich_email_address?: boolean;
|
|
38
|
+
enrich_phone_number?: boolean;
|
|
39
|
+
wait_for_completion?: boolean;
|
|
40
|
+
poll_interval_ms?: number;
|
|
41
|
+
max_wait_ms?: number;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type BetterContactSinglePayload = BetterContactContact & BetterContactControls;
|
|
45
|
+
|
|
46
|
+
type BetterContactBulkPayload = BetterContactControls & {
|
|
47
|
+
contacts: BetterContactContact[];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type BetterContactResult = Record<string, unknown> & {
|
|
51
|
+
data?: unknown[];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type BetterContactBatchResult = BetterContactResult | unknown[];
|
|
55
|
+
|
|
56
|
+
export type BetterContactScopedRequest = {
|
|
57
|
+
requestId: string;
|
|
58
|
+
correlationField: string;
|
|
59
|
+
correlationValue: string;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Fields BetterContact's AsyncGetResponse reports once for the whole batch
|
|
63
|
+
// request, never per contact: `summary` (a batch-wide breakdown),
|
|
64
|
+
// `credits_consumed`/`credits_left` (the request's total spend/remaining
|
|
65
|
+
// balance). Billing settlement reads these from BetterContact's own async
|
|
66
|
+
// status endpoint independently (see billing.ts's fetchTruth), never from
|
|
67
|
+
// this per-row public shape, so stripping them here only affects what a
|
|
68
|
+
// caller sees on each row -- it cannot desync actual charging. Left
|
|
69
|
+
// unstripped, a 20-contact batch that spent 20 credits total would report
|
|
70
|
+
// `credits_consumed: 20` on every one of the 20 rows, misrepresenting a
|
|
71
|
+
// per-request total as a per-row cost.
|
|
72
|
+
const BETTERCONTACT_BATCH_LEVEL_ONLY_FIELDS = [
|
|
73
|
+
'summary',
|
|
74
|
+
'credits_consumed',
|
|
75
|
+
'credits_left',
|
|
76
|
+
] as const;
|
|
77
|
+
|
|
78
|
+
function withoutBatchLevelSummary(
|
|
79
|
+
result: BetterContactResult,
|
|
80
|
+
logicalItemCount: number,
|
|
81
|
+
): BetterContactResult {
|
|
82
|
+
if (logicalItemCount <= 1) {
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
const perItemResult = { ...result };
|
|
86
|
+
for (const field of BETTERCONTACT_BATCH_LEVEL_ONLY_FIELDS) {
|
|
87
|
+
delete perItemResult[field];
|
|
88
|
+
}
|
|
89
|
+
return perItemResult;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function stableValue(value: unknown): unknown {
|
|
93
|
+
if (Array.isArray(value)) {
|
|
94
|
+
return value.map(stableValue);
|
|
95
|
+
}
|
|
96
|
+
if (value && typeof value === 'object') {
|
|
97
|
+
return Object.fromEntries(
|
|
98
|
+
Object.entries(value as Record<string, unknown>)
|
|
99
|
+
.filter(([, entry]) => entry !== undefined)
|
|
100
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
101
|
+
.map(([key, entry]) => [key, stableValue(entry)]),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function stableStringify(value: unknown): string {
|
|
108
|
+
return JSON.stringify(stableValue(value));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function shortStableHash(value: string): string {
|
|
112
|
+
let hash = 0x811c9dc5;
|
|
113
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
114
|
+
hash ^= value.charCodeAt(index);
|
|
115
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
116
|
+
}
|
|
117
|
+
return hash.toString(36);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function normalizedControls(payload: BetterContactControls) {
|
|
121
|
+
const waitForCompletion = payload.wait_for_completion !== false;
|
|
122
|
+
return {
|
|
123
|
+
enrich_phone_number: payload.enrich_phone_number === true,
|
|
124
|
+
wait_for_completion: waitForCompletion,
|
|
125
|
+
...(waitForCompletion
|
|
126
|
+
? {
|
|
127
|
+
poll_interval_ms:
|
|
128
|
+
payload.poll_interval_ms ?? BETTERCONTACT_DEFAULT_POLL_INTERVAL_MS,
|
|
129
|
+
max_wait_ms: Math.min(
|
|
130
|
+
payload.max_wait_ms ?? BETTERCONTACT_MAX_SYNC_WAIT_MS,
|
|
131
|
+
BETTERCONTACT_MAX_SYNC_WAIT_MS,
|
|
132
|
+
),
|
|
133
|
+
}
|
|
134
|
+
: {}),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function controlsKey(payload: BetterContactControls): string {
|
|
139
|
+
return stableStringify(normalizedControls(payload));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function copyControls(payload: BetterContactControls): BetterContactControls {
|
|
143
|
+
return {
|
|
144
|
+
...(payload.enrich_email_address !== undefined
|
|
145
|
+
? { enrich_email_address: payload.enrich_email_address }
|
|
146
|
+
: {}),
|
|
147
|
+
...(payload.enrich_phone_number !== undefined
|
|
148
|
+
? { enrich_phone_number: payload.enrich_phone_number }
|
|
149
|
+
: {}),
|
|
150
|
+
...(payload.wait_for_completion !== undefined
|
|
151
|
+
? { wait_for_completion: payload.wait_for_completion }
|
|
152
|
+
: {}),
|
|
153
|
+
...(payload.poll_interval_ms !== undefined
|
|
154
|
+
? { poll_interval_ms: payload.poll_interval_ms }
|
|
155
|
+
: {}),
|
|
156
|
+
...(payload.max_wait_ms !== undefined
|
|
157
|
+
? { max_wait_ms: payload.max_wait_ms }
|
|
158
|
+
: {}),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
163
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function hasValidOptionalBoolean(
|
|
167
|
+
payload: Record<string, unknown>,
|
|
168
|
+
field: string,
|
|
169
|
+
): boolean {
|
|
170
|
+
return payload[field] === undefined || typeof payload[field] === 'boolean';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function hasValidOptionalInteger(
|
|
174
|
+
payload: Record<string, unknown>,
|
|
175
|
+
field: string,
|
|
176
|
+
minimum: number,
|
|
177
|
+
maximum: number,
|
|
178
|
+
): boolean {
|
|
179
|
+
const value = payload[field];
|
|
180
|
+
return (
|
|
181
|
+
value === undefined ||
|
|
182
|
+
(typeof value === 'number' &&
|
|
183
|
+
Number.isInteger(value) &&
|
|
184
|
+
value >= minimum &&
|
|
185
|
+
value <= maximum)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function hasValidControls(payload: BetterContactControls): boolean {
|
|
190
|
+
const record = payload as Record<string, unknown>;
|
|
191
|
+
return (
|
|
192
|
+
hasValidOptionalBoolean(record, 'enrich_email_address') &&
|
|
193
|
+
hasValidOptionalBoolean(record, 'enrich_phone_number') &&
|
|
194
|
+
hasValidOptionalBoolean(record, 'wait_for_completion') &&
|
|
195
|
+
hasValidOptionalInteger(record, 'poll_interval_ms', 250, 10_000) &&
|
|
196
|
+
hasValidOptionalInteger(record, 'max_wait_ms', 1_000, 120_000)
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function hasValidCustomFields(contact: BetterContactContact): boolean {
|
|
201
|
+
const customFields = (contact as Record<string, unknown>).custom_fields;
|
|
202
|
+
return customFields === undefined || isPlainRecord(customFields);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function isBatchableContact(contact: BetterContactContact): boolean {
|
|
206
|
+
return hasRequiredContactFields(contact) && hasValidCustomFields(contact);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function hasOnlyKnownSinglePayloadFields(
|
|
210
|
+
payload: BetterContactSinglePayload,
|
|
211
|
+
): boolean {
|
|
212
|
+
return Object.keys(payload).every((field) =>
|
|
213
|
+
BETTERCONTACT_SINGLE_PAYLOAD_FIELDS.has(field),
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function isBatchableSinglePayload(
|
|
218
|
+
payload: BetterContactSinglePayload,
|
|
219
|
+
): boolean {
|
|
220
|
+
return (
|
|
221
|
+
isBatchableContact(payload) &&
|
|
222
|
+
hasValidControls(payload) &&
|
|
223
|
+
hasOnlyKnownSinglePayloadFields(payload)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function contactIdentity(contact: BetterContactContact): string {
|
|
228
|
+
return stableStringify({
|
|
229
|
+
first_name: contact.first_name,
|
|
230
|
+
last_name: contact.last_name,
|
|
231
|
+
company_domain: contact.company_domain ?? null,
|
|
232
|
+
company: contact.company ?? null,
|
|
233
|
+
linkedin_url: contact.linkedin_url ?? null,
|
|
234
|
+
custom_fields: contact.custom_fields ?? null,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function buildItemKey(contact: BetterContactContact, index = 0): string {
|
|
239
|
+
return `dl_bc_${index}_${shortStableHash(contactIdentity(contact))}`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function withItemKey(
|
|
243
|
+
contact: BetterContactContact,
|
|
244
|
+
itemKey: string,
|
|
245
|
+
): BetterContactContact {
|
|
246
|
+
const customFields = contact.custom_fields ?? {};
|
|
247
|
+
const correlationFieldBase = `${BETTERCONTACT_BATCH_ITEM_KEY}_${shortStableHash(
|
|
248
|
+
itemKey,
|
|
249
|
+
)}`;
|
|
250
|
+
let correlationField = correlationFieldBase;
|
|
251
|
+
let suffix = 1;
|
|
252
|
+
while (Object.prototype.hasOwnProperty.call(customFields, correlationField)) {
|
|
253
|
+
correlationField = `${correlationFieldBase}_${suffix}`;
|
|
254
|
+
suffix += 1;
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
...contact,
|
|
258
|
+
custom_fields: {
|
|
259
|
+
...customFields,
|
|
260
|
+
[correlationField]: itemKey,
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function resultCustomFieldRecords(
|
|
266
|
+
row: unknown,
|
|
267
|
+
): Array<Record<string, unknown>> {
|
|
268
|
+
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
const customFields = (row as Record<string, unknown>).custom_fields;
|
|
272
|
+
return (Array.isArray(customFields) ? customFields : [customFields]).filter(
|
|
273
|
+
(candidate): candidate is Record<string, unknown> =>
|
|
274
|
+
Boolean(candidate) &&
|
|
275
|
+
typeof candidate === 'object' &&
|
|
276
|
+
!Array.isArray(candidate),
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function encodeScopedRequest(scope: BetterContactScopedRequest): string {
|
|
281
|
+
return `${BETTERCONTACT_SCOPED_REQUEST_PREFIX}${encodeURIComponent(
|
|
282
|
+
JSON.stringify([
|
|
283
|
+
scope.requestId,
|
|
284
|
+
scope.correlationField,
|
|
285
|
+
scope.correlationValue,
|
|
286
|
+
]),
|
|
287
|
+
)}`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function parseBetterContactScopedRequest(
|
|
291
|
+
requestId: string,
|
|
292
|
+
): BetterContactScopedRequest | null {
|
|
293
|
+
if (!requestId.startsWith(BETTERCONTACT_SCOPED_REQUEST_PREFIX)) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
const parsed = JSON.parse(
|
|
298
|
+
decodeURIComponent(
|
|
299
|
+
requestId.slice(BETTERCONTACT_SCOPED_REQUEST_PREFIX.length),
|
|
300
|
+
),
|
|
301
|
+
) as unknown;
|
|
302
|
+
if (
|
|
303
|
+
!Array.isArray(parsed) ||
|
|
304
|
+
parsed.length !== 3 ||
|
|
305
|
+
parsed.some((value) => typeof value !== 'string' || value.length === 0)
|
|
306
|
+
) {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
requestId: parsed[0] as string,
|
|
311
|
+
correlationField: parsed[1] as string,
|
|
312
|
+
correlationValue: parsed[2] as string,
|
|
313
|
+
};
|
|
314
|
+
} catch {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function rowMatchesCorrelation(
|
|
320
|
+
row: unknown,
|
|
321
|
+
correlationField: string,
|
|
322
|
+
correlationValue: string,
|
|
323
|
+
): boolean {
|
|
324
|
+
return resultCustomFieldRecords(row).some(
|
|
325
|
+
(fields) => fields[correlationField] === correlationValue,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function withoutCorrelationSelector(
|
|
330
|
+
row: unknown,
|
|
331
|
+
correlationField: string,
|
|
332
|
+
): unknown {
|
|
333
|
+
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
334
|
+
return row;
|
|
335
|
+
}
|
|
336
|
+
const record = row as Record<string, unknown>;
|
|
337
|
+
const customFields = record.custom_fields;
|
|
338
|
+
const cleanRecord = (fields: Record<string, unknown>) => {
|
|
339
|
+
const cleaned = { ...fields };
|
|
340
|
+
delete cleaned[correlationField];
|
|
341
|
+
return cleaned;
|
|
342
|
+
};
|
|
343
|
+
return {
|
|
344
|
+
...record,
|
|
345
|
+
custom_fields: Array.isArray(customFields)
|
|
346
|
+
? customFields
|
|
347
|
+
.filter(isPlainRecord)
|
|
348
|
+
.map(cleanRecord)
|
|
349
|
+
.filter((fields) => Object.keys(fields).length > 0)
|
|
350
|
+
: isPlainRecord(customFields)
|
|
351
|
+
? cleanRecord(customFields)
|
|
352
|
+
: customFields,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function isolateBetterContactScopedResult(
|
|
357
|
+
result: unknown,
|
|
358
|
+
scopedRequestId: string,
|
|
359
|
+
): unknown {
|
|
360
|
+
const scope = parseBetterContactScopedRequest(scopedRequestId);
|
|
361
|
+
if (!scope || !isPlainRecord(result)) {
|
|
362
|
+
return result;
|
|
363
|
+
}
|
|
364
|
+
const scopedResult: BetterContactResult = { ...result, id: scopedRequestId };
|
|
365
|
+
delete scopedResult.summary;
|
|
366
|
+
if (!Array.isArray(result.data)) {
|
|
367
|
+
return scopedResult;
|
|
368
|
+
}
|
|
369
|
+
if (result.data.length === 0) {
|
|
370
|
+
return { ...scopedResult, data: [] };
|
|
371
|
+
}
|
|
372
|
+
const matches = result.data.filter((row) =>
|
|
373
|
+
rowMatchesCorrelation(row, scope.correlationField, scope.correlationValue),
|
|
374
|
+
);
|
|
375
|
+
if (matches.length !== 1) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
matches.length === 0
|
|
378
|
+
? 'BetterContact scoped result has missing or unmatched correlation identity.'
|
|
379
|
+
: 'BetterContact scoped result has ambiguous correlation identity.',
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
...scopedResult,
|
|
384
|
+
data: [withoutCorrelationSelector(matches[0], scope.correlationField)],
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function correlationEntryForItem(item: {
|
|
389
|
+
itemKey: string;
|
|
390
|
+
payload: BetterContactContact;
|
|
391
|
+
}): [string, unknown] | null {
|
|
392
|
+
const keyedContact = withItemKey(item.payload, item.itemKey);
|
|
393
|
+
const originalFields = item.payload.custom_fields ?? {};
|
|
394
|
+
return (
|
|
395
|
+
Object.entries(keyedContact.custom_fields ?? {}).find(
|
|
396
|
+
([field]) => !Object.prototype.hasOwnProperty.call(originalFields, field),
|
|
397
|
+
) ?? null
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function resultMatchesItem(
|
|
402
|
+
row: unknown,
|
|
403
|
+
item: { itemKey: string; payload: BetterContactContact },
|
|
404
|
+
): boolean {
|
|
405
|
+
const correlationEntry = correlationEntryForItem(item);
|
|
406
|
+
if (!correlationEntry) {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
const [correlationField, correlationValue] = correlationEntry;
|
|
410
|
+
return rowMatchesCorrelation(row, correlationField, String(correlationValue));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function withoutCorrelationField(
|
|
414
|
+
row: unknown,
|
|
415
|
+
item: { itemKey: string; payload: BetterContactContact },
|
|
416
|
+
): unknown {
|
|
417
|
+
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
418
|
+
return row;
|
|
419
|
+
}
|
|
420
|
+
const correlationField = correlationEntryForItem(item)?.[0];
|
|
421
|
+
if (!correlationField) {
|
|
422
|
+
return row;
|
|
423
|
+
}
|
|
424
|
+
return withoutCorrelationSelector(row, correlationField);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function scopedRequestIdForItem(
|
|
428
|
+
requestId: string,
|
|
429
|
+
item: { itemKey: string; payload: BetterContactContact },
|
|
430
|
+
): string {
|
|
431
|
+
const correlationEntry = correlationEntryForItem(item);
|
|
432
|
+
if (!correlationEntry) {
|
|
433
|
+
throw new Error(
|
|
434
|
+
'BetterContact batch item is missing correlation identity.',
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
return encodeScopedRequest({
|
|
438
|
+
requestId,
|
|
439
|
+
correlationField: correlationEntry[0],
|
|
440
|
+
correlationValue: String(correlationEntry[1]),
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function withScopedRequestId<TPayload extends Record<string, unknown>>(
|
|
445
|
+
result: BetterContactResult,
|
|
446
|
+
item: { itemKey: string; payload: TPayload },
|
|
447
|
+
logicalItemCount: number,
|
|
448
|
+
): BetterContactResult {
|
|
449
|
+
const requestId = typeof result.id === 'string' ? result.id.trim() : '';
|
|
450
|
+
if (!requestId || logicalItemCount <= 1) {
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
...result,
|
|
455
|
+
id: scopedRequestIdForItem(requestId, {
|
|
456
|
+
itemKey: item.itemKey,
|
|
457
|
+
payload: item.payload as unknown as BetterContactContact,
|
|
458
|
+
}),
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function hasRequiredContactFields(contact: BetterContactContact): boolean {
|
|
463
|
+
const companyDomain =
|
|
464
|
+
typeof contact.company_domain === 'string' &&
|
|
465
|
+
contact.company_domain.trim().length > 0
|
|
466
|
+
? contact.company_domain
|
|
467
|
+
: contact.company;
|
|
468
|
+
return (
|
|
469
|
+
typeof contact.first_name === 'string' &&
|
|
470
|
+
contact.first_name.trim().length > 0 &&
|
|
471
|
+
typeof contact.last_name === 'string' &&
|
|
472
|
+
contact.last_name.trim().length > 0 &&
|
|
473
|
+
typeof companyDomain === 'string' &&
|
|
474
|
+
companyDomain.trim().length > 0
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function splitBetterContactResult<TPayload extends Record<string, unknown>>(
|
|
479
|
+
fullResult: BetterContactBatchResult,
|
|
480
|
+
compiled: {
|
|
481
|
+
items: Array<{ itemKey: string; payload: TPayload }>;
|
|
482
|
+
},
|
|
483
|
+
) {
|
|
484
|
+
const resultRows = Array.isArray(fullResult)
|
|
485
|
+
? fullResult
|
|
486
|
+
: Array.isArray(fullResult.data)
|
|
487
|
+
? fullResult.data
|
|
488
|
+
: null;
|
|
489
|
+
if (!resultRows) {
|
|
490
|
+
return compiled.items.map((item) => ({
|
|
491
|
+
itemKey: item.itemKey,
|
|
492
|
+
result: {
|
|
493
|
+
data: Array.isArray(fullResult)
|
|
494
|
+
? fullResult
|
|
495
|
+
: withScopedRequestId(
|
|
496
|
+
withoutBatchLevelSummary(fullResult, compiled.items.length),
|
|
497
|
+
item,
|
|
498
|
+
compiled.items.length,
|
|
499
|
+
),
|
|
500
|
+
},
|
|
501
|
+
rawResult: fullResult,
|
|
502
|
+
}));
|
|
503
|
+
}
|
|
504
|
+
if (resultRows.length === 0) {
|
|
505
|
+
return compiled.items.map((item) => ({
|
|
506
|
+
itemKey: item.itemKey,
|
|
507
|
+
result: {
|
|
508
|
+
data: Array.isArray(fullResult)
|
|
509
|
+
? []
|
|
510
|
+
: withScopedRequestId(
|
|
511
|
+
{
|
|
512
|
+
...withoutBatchLevelSummary(fullResult, compiled.items.length),
|
|
513
|
+
data: [],
|
|
514
|
+
},
|
|
515
|
+
item,
|
|
516
|
+
compiled.items.length,
|
|
517
|
+
),
|
|
518
|
+
},
|
|
519
|
+
rawResult: null,
|
|
520
|
+
}));
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const remainingItems = new Map(
|
|
524
|
+
compiled.items.map((item) => [item.itemKey, item] as const),
|
|
525
|
+
);
|
|
526
|
+
const rowsByItemKey = new Map<string, unknown>();
|
|
527
|
+
for (const row of resultRows) {
|
|
528
|
+
const matches = [...remainingItems.values()].filter((item) =>
|
|
529
|
+
resultMatchesItem(row, {
|
|
530
|
+
itemKey: item.itemKey,
|
|
531
|
+
payload: item.payload as unknown as BetterContactContact,
|
|
532
|
+
}),
|
|
533
|
+
);
|
|
534
|
+
if (matches.length !== 1) {
|
|
535
|
+
throw new Error(
|
|
536
|
+
matches.length === 0
|
|
537
|
+
? 'BetterContact bulk result has missing or unmatched correlation identity.'
|
|
538
|
+
: 'BetterContact bulk result has ambiguous correlation identity.',
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
const matchedItem = matches[0]!;
|
|
542
|
+
remainingItems.delete(matchedItem.itemKey);
|
|
543
|
+
rowsByItemKey.set(matchedItem.itemKey, row);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return compiled.items.map((item) => {
|
|
547
|
+
const matchedRow = rowsByItemKey.get(item.itemKey);
|
|
548
|
+
if (!matchedRow) {
|
|
549
|
+
throw new Error(
|
|
550
|
+
`BetterContact bulk result is missing result identity ${item.itemKey}.`,
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
const publicRow = withoutCorrelationField(matchedRow, {
|
|
554
|
+
itemKey: item.itemKey,
|
|
555
|
+
payload: item.payload as unknown as BetterContactContact,
|
|
556
|
+
});
|
|
557
|
+
const resultData = Array.isArray(fullResult)
|
|
558
|
+
? [publicRow]
|
|
559
|
+
: withScopedRequestId(
|
|
560
|
+
{
|
|
561
|
+
...withoutBatchLevelSummary(fullResult, compiled.items.length),
|
|
562
|
+
data: [publicRow],
|
|
563
|
+
},
|
|
564
|
+
item,
|
|
565
|
+
compiled.items.length,
|
|
566
|
+
);
|
|
567
|
+
return {
|
|
568
|
+
itemKey: item.itemKey,
|
|
569
|
+
result: { data: resultData },
|
|
570
|
+
rawResult: publicRow,
|
|
571
|
+
};
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const sharedStrategyFields = {
|
|
576
|
+
batchOperation: 'bettercontact_bulk_enrich' as const,
|
|
577
|
+
kind: 'identifier_batch' as const,
|
|
578
|
+
maxBatchSize: BETTERCONTACT_BATCH_SIZE,
|
|
579
|
+
bucketKeyPayloadFields: ['enrich_phone_number'],
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
const bettercontactSingleBatchStrategy: BatchOperationStrategy<
|
|
583
|
+
BetterContactSinglePayload,
|
|
584
|
+
BetterContactBulkPayload,
|
|
585
|
+
BetterContactBatchResult,
|
|
586
|
+
{ data: BetterContactBatchResult },
|
|
587
|
+
unknown
|
|
588
|
+
> = {
|
|
589
|
+
...sharedStrategyFields,
|
|
590
|
+
sourceOperation: 'bettercontact_enrich',
|
|
591
|
+
canBatchWith(left, right) {
|
|
592
|
+
return (
|
|
593
|
+
isBatchableSinglePayload(left) &&
|
|
594
|
+
isBatchableSinglePayload(right) &&
|
|
595
|
+
controlsKey(left) === controlsKey(right)
|
|
596
|
+
);
|
|
597
|
+
},
|
|
598
|
+
toBucketKey(payload) {
|
|
599
|
+
if (!isBatchableSinglePayload(payload)) {
|
|
600
|
+
return `bettercontact_enrich:invalid:${stableStringify(payload)}`;
|
|
601
|
+
}
|
|
602
|
+
return `bettercontact_bulk_enrich:${controlsKey(payload)}`;
|
|
603
|
+
},
|
|
604
|
+
toItemKey(payload) {
|
|
605
|
+
return buildItemKey(payload);
|
|
606
|
+
},
|
|
607
|
+
compile(payloads) {
|
|
608
|
+
const first = payloads[0] ?? ({} as BetterContactSinglePayload);
|
|
609
|
+
const items = payloads.map((payload, index) => ({
|
|
610
|
+
itemKey: buildItemKey(payload, index),
|
|
611
|
+
payload,
|
|
612
|
+
}));
|
|
613
|
+
return {
|
|
614
|
+
batchOperation: 'bettercontact_bulk_enrich',
|
|
615
|
+
batchPayload: {
|
|
616
|
+
...copyControls(first),
|
|
617
|
+
contacts: items.map((item) =>
|
|
618
|
+
isBatchableSinglePayload(item.payload)
|
|
619
|
+
? withItemKey(item.payload, item.itemKey)
|
|
620
|
+
: item.payload,
|
|
621
|
+
),
|
|
622
|
+
},
|
|
623
|
+
items,
|
|
624
|
+
};
|
|
625
|
+
},
|
|
626
|
+
splitResult: splitBetterContactResult,
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
function readBulkContacts(
|
|
630
|
+
payload: BetterContactBulkPayload,
|
|
631
|
+
): BetterContactContact[] {
|
|
632
|
+
return Array.isArray(payload.contacts) ? payload.contacts : [];
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function isOneContactBulkPayload(payload: BetterContactBulkPayload): boolean {
|
|
636
|
+
return readBulkContacts(payload).length === 1;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const bettercontactBulkSelfBatchStrategy: BatchOperationStrategy<
|
|
640
|
+
BetterContactBulkPayload,
|
|
641
|
+
BetterContactBulkPayload,
|
|
642
|
+
BetterContactBatchResult,
|
|
643
|
+
{ data: BetterContactBatchResult },
|
|
644
|
+
unknown
|
|
645
|
+
> = {
|
|
646
|
+
...sharedStrategyFields,
|
|
647
|
+
sourceOperation: 'bettercontact_bulk_enrich',
|
|
648
|
+
canBatchWith(left, right) {
|
|
649
|
+
return (
|
|
650
|
+
isOneContactBulkPayload(left) &&
|
|
651
|
+
isOneContactBulkPayload(right) &&
|
|
652
|
+
isBatchableContact(readBulkContacts(left)[0]!) &&
|
|
653
|
+
isBatchableContact(readBulkContacts(right)[0]!) &&
|
|
654
|
+
hasValidControls(left) &&
|
|
655
|
+
hasValidControls(right) &&
|
|
656
|
+
controlsKey(left) === controlsKey(right)
|
|
657
|
+
);
|
|
658
|
+
},
|
|
659
|
+
toBucketKey(payload) {
|
|
660
|
+
if (!isOneContactBulkPayload(payload)) {
|
|
661
|
+
return `bettercontact_bulk_enrich:passthrough:${stableStringify(payload)}`;
|
|
662
|
+
}
|
|
663
|
+
if (
|
|
664
|
+
!isBatchableContact(readBulkContacts(payload)[0]!) ||
|
|
665
|
+
!hasValidControls(payload)
|
|
666
|
+
) {
|
|
667
|
+
return `bettercontact_bulk_enrich:invalid:${stableStringify(payload)}`;
|
|
668
|
+
}
|
|
669
|
+
return `bettercontact_bulk_enrich:${controlsKey(payload)}`;
|
|
670
|
+
},
|
|
671
|
+
toItemKey(payload) {
|
|
672
|
+
return buildItemKey(
|
|
673
|
+
readBulkContacts(payload)[0] ?? ({} as BetterContactContact),
|
|
674
|
+
);
|
|
675
|
+
},
|
|
676
|
+
compile(payloads) {
|
|
677
|
+
const first = payloads[0] ?? ({ contacts: [] } as BetterContactBulkPayload);
|
|
678
|
+
if (payloads.length === 1 && !isOneContactBulkPayload(first)) {
|
|
679
|
+
return {
|
|
680
|
+
batchOperation: 'bettercontact_bulk_enrich',
|
|
681
|
+
batchPayload: first,
|
|
682
|
+
items: [{ itemKey: this.toItemKey(first), payload: first }],
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const items = payloads.map((payload, index) => ({
|
|
687
|
+
itemKey: buildItemKey(readBulkContacts(payload)[0]!, index),
|
|
688
|
+
payload,
|
|
689
|
+
}));
|
|
690
|
+
return {
|
|
691
|
+
batchOperation: 'bettercontact_bulk_enrich',
|
|
692
|
+
batchPayload: {
|
|
693
|
+
...copyControls(first),
|
|
694
|
+
contacts: items.map((item) => {
|
|
695
|
+
const contact = readBulkContacts(item.payload)[0]!;
|
|
696
|
+
return isBatchableContact(contact) && hasValidControls(item.payload)
|
|
697
|
+
? withItemKey(contact, item.itemKey)
|
|
698
|
+
: contact;
|
|
699
|
+
}),
|
|
700
|
+
},
|
|
701
|
+
items,
|
|
702
|
+
};
|
|
703
|
+
},
|
|
704
|
+
splitResult(fullResult, compiled) {
|
|
705
|
+
const first = compiled.items[0];
|
|
706
|
+
if (
|
|
707
|
+
compiled.items.length === 1 &&
|
|
708
|
+
first &&
|
|
709
|
+
!isOneContactBulkPayload(first.payload)
|
|
710
|
+
) {
|
|
711
|
+
return [
|
|
712
|
+
{
|
|
713
|
+
itemKey: first.itemKey,
|
|
714
|
+
result: { data: fullResult },
|
|
715
|
+
rawResult: fullResult,
|
|
716
|
+
},
|
|
717
|
+
];
|
|
718
|
+
}
|
|
719
|
+
return splitBetterContactResult(fullResult, compiled);
|
|
720
|
+
},
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
export const bettercontactBatchStrategies = defineBatchStrategyMap({
|
|
724
|
+
bettercontact_enrich: bettercontactSingleBatchStrategy,
|
|
725
|
+
bettercontact_bulk_enrich: bettercontactBulkSelfBatchStrategy,
|
|
726
|
+
});
|
|
@@ -5369,6 +5369,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
5369
5369
|
entries: Array<{
|
|
5370
5370
|
request: ToolCallRequest;
|
|
5371
5371
|
result: unknown | null;
|
|
5372
|
+
status?: string;
|
|
5372
5373
|
metadata?: ToolResultMetadataInput | null;
|
|
5373
5374
|
jobId?: string;
|
|
5374
5375
|
meta?: Record<string, unknown>;
|
|
@@ -5380,7 +5381,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
5380
5381
|
...entry,
|
|
5381
5382
|
wrapped: await this.wrapToolExecutionResult({
|
|
5382
5383
|
toolId,
|
|
5383
|
-
status:
|
|
5384
|
+
status:
|
|
5385
|
+
entry.status ?? (entry.result == null ? 'no_result' : 'completed'),
|
|
5384
5386
|
jobId: entry.jobId,
|
|
5385
5387
|
result: entry.result,
|
|
5386
5388
|
metadata: entry.metadata,
|
|
@@ -11282,12 +11284,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11282
11284
|
}
|
|
11283
11285
|
continue;
|
|
11284
11286
|
}
|
|
11287
|
+
const batchExecution = entry.result;
|
|
11285
11288
|
try {
|
|
11286
11289
|
const splitResults =
|
|
11287
|
-
|
|
11290
|
+
batchExecution != null
|
|
11288
11291
|
? entry.request.splitResults(
|
|
11289
11292
|
legacyResultForBatchSplitter(
|
|
11290
|
-
|
|
11293
|
+
batchExecution.execution,
|
|
11291
11294
|
),
|
|
11292
11295
|
)
|
|
11293
11296
|
: entry.request.memberRequests.map(() => null);
|
|
@@ -11298,11 +11301,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11298
11301
|
(request, index) => ({
|
|
11299
11302
|
request,
|
|
11300
11303
|
result: splitResults[index] ?? null,
|
|
11304
|
+
status: batchExecution?.execution.status,
|
|
11301
11305
|
toolResponse:
|
|
11302
|
-
|
|
11306
|
+
batchExecution == null
|
|
11303
11307
|
? undefined
|
|
11304
11308
|
: publicToolResponseForBatchedItem(
|
|
11305
|
-
|
|
11309
|
+
batchExecution.execution,
|
|
11306
11310
|
splitResults[index] ?? null,
|
|
11307
11311
|
this.currentToolResponseContract ===
|
|
11308
11312
|
RAW_V2_TOOL_RESPONSE_CONTRACT,
|
|
@@ -11323,7 +11327,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11323
11327
|
resolveLiveFollowers(request, resolvedResults[index]);
|
|
11324
11328
|
}
|
|
11325
11329
|
} finally {
|
|
11326
|
-
|
|
11330
|
+
batchExecution?.releaseToolSlot();
|
|
11327
11331
|
}
|
|
11328
11332
|
}
|
|
11329
11333
|
|
|
@@ -11340,6 +11344,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11340
11344
|
const completionBuffer: Array<{
|
|
11341
11345
|
request: ToolCallRequest;
|
|
11342
11346
|
result: unknown | null;
|
|
11347
|
+
status?: string;
|
|
11343
11348
|
metadata?: ToolResultMetadataInput | null;
|
|
11344
11349
|
jobId?: string;
|
|
11345
11350
|
meta?: Record<string, unknown>;
|
|
@@ -11359,6 +11364,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11359
11364
|
entries.map((entry) => ({
|
|
11360
11365
|
request: entry.request,
|
|
11361
11366
|
result: entry.result,
|
|
11367
|
+
status: entry.status,
|
|
11362
11368
|
metadata: entry.metadata,
|
|
11363
11369
|
jobId: entry.jobId,
|
|
11364
11370
|
meta: entry.meta,
|
|
@@ -11392,6 +11398,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11392
11398
|
completionBuffer.push({
|
|
11393
11399
|
request,
|
|
11394
11400
|
result: execution.result ?? null,
|
|
11401
|
+
status: execution.status,
|
|
11395
11402
|
metadata: execution.metadata ?? null,
|
|
11396
11403
|
jobId: execution.jobId,
|
|
11397
11404
|
meta: execution.meta,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AnyBatchOperationStrategy } from './batching-types';
|
|
2
|
+
import { bettercontactBatchStrategies } from './bettercontact-batching';
|
|
2
3
|
import { DEFAULT_PLAY_RUNTIME_BATCH_STRATEGIES } from './default-batch-strategies';
|
|
3
4
|
import { fullenrichBatchStrategies } from './fullenrich-batching';
|
|
4
5
|
import { opensosdataBatchStrategies } from './opensosdata-batching';
|
|
@@ -8,6 +9,7 @@ export const PLAY_RUNTIME_BATCH_OPERATION_REGISTRY: Record<
|
|
|
8
9
|
AnyBatchOperationStrategy
|
|
9
10
|
> = {
|
|
10
11
|
...DEFAULT_PLAY_RUNTIME_BATCH_STRATEGIES,
|
|
12
|
+
...bettercontactBatchStrategies,
|
|
11
13
|
...fullenrichBatchStrategies,
|
|
12
14
|
...opensosdataBatchStrategies,
|
|
13
15
|
};
|
package/dist/cli/index.js
CHANGED
|
@@ -1047,7 +1047,7 @@ var SDK_RELEASE = {
|
|
|
1047
1047
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
1048
1048
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
1049
1049
|
// getters keep their established compatibility behavior.
|
|
1050
|
-
version: "0.3.
|
|
1050
|
+
version: "0.3.17",
|
|
1051
1051
|
updateSummary: "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.",
|
|
1052
1052
|
contracts: {
|
|
1053
1053
|
api: {
|
|
@@ -3772,6 +3772,16 @@ function resolveTheirstackClientTimeoutMs(endpointId, payload) {
|
|
|
3772
3772
|
return usesExtendedTheirstackJobSearchBudget(endpointId, payload) ? THEIRSTACK_SLOW_JOB_SEARCH_CLIENT_TIMEOUT_MS : null;
|
|
3773
3773
|
}
|
|
3774
3774
|
|
|
3775
|
+
// ../shared_libs/integrations/bettercontact-execution-policy.ts
|
|
3776
|
+
var BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS = 12e4;
|
|
3777
|
+
var BETTERCONTACT_LAUNCHER_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
3778
|
+
"bettercontact_enrich",
|
|
3779
|
+
"bettercontact_bulk_enrich"
|
|
3780
|
+
]);
|
|
3781
|
+
function usesExtendedBetterContactLauncherBudget(toolId) {
|
|
3782
|
+
return BETTERCONTACT_LAUNCHER_TOOL_IDS.has(toolId.trim().toLowerCase());
|
|
3783
|
+
}
|
|
3784
|
+
|
|
3775
3785
|
// ../shared_libs/play-runtime/backend.ts
|
|
3776
3786
|
var PLAY_RUNTIME_BACKENDS = {
|
|
3777
3787
|
localProcess: "local_process",
|
|
@@ -4052,6 +4062,9 @@ function resolveToolExecuteTimeoutMs(toolId, input2) {
|
|
|
4052
4062
|
input2
|
|
4053
4063
|
);
|
|
4054
4064
|
if (theirstackTimeoutMs !== null) return theirstackTimeoutMs;
|
|
4065
|
+
if (usesExtendedBetterContactLauncherBudget(normalized)) {
|
|
4066
|
+
return BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS;
|
|
4067
|
+
}
|
|
4055
4068
|
return normalized === "deeplineagent" || normalized === "deeplineagent_deeplineagent" || normalized === "ai_inference" || normalized === "deeplineagent_ai_inference" || normalized === "aiinference" ? DEEPLINEAGENT_EXECUTE_TIMEOUT_MS : void 0;
|
|
4056
4069
|
}
|
|
4057
4070
|
var RUNS_FAILED_LOG_LIMIT = 20;
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1033,7 +1033,7 @@ var SDK_RELEASE = {
|
|
|
1033
1033
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
1034
1034
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
1035
1035
|
// getters keep their established compatibility behavior.
|
|
1036
|
-
version: "0.3.
|
|
1036
|
+
version: "0.3.17",
|
|
1037
1037
|
updateSummary: "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.",
|
|
1038
1038
|
contracts: {
|
|
1039
1039
|
api: {
|
|
@@ -3758,6 +3758,16 @@ function resolveTheirstackClientTimeoutMs(endpointId, payload) {
|
|
|
3758
3758
|
return usesExtendedTheirstackJobSearchBudget(endpointId, payload) ? THEIRSTACK_SLOW_JOB_SEARCH_CLIENT_TIMEOUT_MS : null;
|
|
3759
3759
|
}
|
|
3760
3760
|
|
|
3761
|
+
// ../shared_libs/integrations/bettercontact-execution-policy.ts
|
|
3762
|
+
var BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS = 12e4;
|
|
3763
|
+
var BETTERCONTACT_LAUNCHER_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
3764
|
+
"bettercontact_enrich",
|
|
3765
|
+
"bettercontact_bulk_enrich"
|
|
3766
|
+
]);
|
|
3767
|
+
function usesExtendedBetterContactLauncherBudget(toolId) {
|
|
3768
|
+
return BETTERCONTACT_LAUNCHER_TOOL_IDS.has(toolId.trim().toLowerCase());
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3761
3771
|
// ../shared_libs/play-runtime/backend.ts
|
|
3762
3772
|
var PLAY_RUNTIME_BACKENDS = {
|
|
3763
3773
|
localProcess: "local_process",
|
|
@@ -4038,6 +4048,9 @@ function resolveToolExecuteTimeoutMs(toolId, input2) {
|
|
|
4038
4048
|
input2
|
|
4039
4049
|
);
|
|
4040
4050
|
if (theirstackTimeoutMs !== null) return theirstackTimeoutMs;
|
|
4051
|
+
if (usesExtendedBetterContactLauncherBudget(normalized)) {
|
|
4052
|
+
return BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS;
|
|
4053
|
+
}
|
|
4041
4054
|
return normalized === "deeplineagent" || normalized === "deeplineagent_deeplineagent" || normalized === "ai_inference" || normalized === "deeplineagent_ai_inference" || normalized === "aiinference" ? DEEPLINEAGENT_EXECUTE_TIMEOUT_MS : void 0;
|
|
4042
4055
|
}
|
|
4043
4056
|
var RUNS_FAILED_LOG_LIMIT = 20;
|
package/dist/index.js
CHANGED
|
@@ -783,7 +783,7 @@ var SDK_RELEASE = {
|
|
|
783
783
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
784
784
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
785
785
|
// getters keep their established compatibility behavior.
|
|
786
|
-
version: "0.3.
|
|
786
|
+
version: "0.3.17",
|
|
787
787
|
updateSummary: "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.",
|
|
788
788
|
contracts: {
|
|
789
789
|
api: {
|
|
@@ -3483,6 +3483,16 @@ function resolveTheirstackClientTimeoutMs(endpointId, payload) {
|
|
|
3483
3483
|
return usesExtendedTheirstackJobSearchBudget(endpointId, payload) ? THEIRSTACK_SLOW_JOB_SEARCH_CLIENT_TIMEOUT_MS : null;
|
|
3484
3484
|
}
|
|
3485
3485
|
|
|
3486
|
+
// ../shared_libs/integrations/bettercontact-execution-policy.ts
|
|
3487
|
+
var BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS = 12e4;
|
|
3488
|
+
var BETTERCONTACT_LAUNCHER_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
3489
|
+
"bettercontact_enrich",
|
|
3490
|
+
"bettercontact_bulk_enrich"
|
|
3491
|
+
]);
|
|
3492
|
+
function usesExtendedBetterContactLauncherBudget(toolId) {
|
|
3493
|
+
return BETTERCONTACT_LAUNCHER_TOOL_IDS.has(toolId.trim().toLowerCase());
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3486
3496
|
// ../shared_libs/play-runtime/backend.ts
|
|
3487
3497
|
var PLAY_RUNTIME_BACKENDS = {
|
|
3488
3498
|
localProcess: "local_process",
|
|
@@ -3763,6 +3773,9 @@ function resolveToolExecuteTimeoutMs(toolId, input) {
|
|
|
3763
3773
|
input
|
|
3764
3774
|
);
|
|
3765
3775
|
if (theirstackTimeoutMs !== null) return theirstackTimeoutMs;
|
|
3776
|
+
if (usesExtendedBetterContactLauncherBudget(normalized)) {
|
|
3777
|
+
return BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS;
|
|
3778
|
+
}
|
|
3766
3779
|
return normalized === "deeplineagent" || normalized === "deeplineagent_deeplineagent" || normalized === "ai_inference" || normalized === "deeplineagent_ai_inference" || normalized === "aiinference" ? DEEPLINEAGENT_EXECUTE_TIMEOUT_MS : void 0;
|
|
3767
3780
|
}
|
|
3768
3781
|
var RUNS_FAILED_LOG_LIMIT = 20;
|
package/dist/index.mjs
CHANGED
|
@@ -706,7 +706,7 @@ var SDK_RELEASE = {
|
|
|
706
706
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
707
707
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
708
708
|
// getters keep their established compatibility behavior.
|
|
709
|
-
version: "0.3.
|
|
709
|
+
version: "0.3.17",
|
|
710
710
|
updateSummary: "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.",
|
|
711
711
|
contracts: {
|
|
712
712
|
api: {
|
|
@@ -3406,6 +3406,16 @@ function resolveTheirstackClientTimeoutMs(endpointId, payload) {
|
|
|
3406
3406
|
return usesExtendedTheirstackJobSearchBudget(endpointId, payload) ? THEIRSTACK_SLOW_JOB_SEARCH_CLIENT_TIMEOUT_MS : null;
|
|
3407
3407
|
}
|
|
3408
3408
|
|
|
3409
|
+
// ../shared_libs/integrations/bettercontact-execution-policy.ts
|
|
3410
|
+
var BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS = 12e4;
|
|
3411
|
+
var BETTERCONTACT_LAUNCHER_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
3412
|
+
"bettercontact_enrich",
|
|
3413
|
+
"bettercontact_bulk_enrich"
|
|
3414
|
+
]);
|
|
3415
|
+
function usesExtendedBetterContactLauncherBudget(toolId) {
|
|
3416
|
+
return BETTERCONTACT_LAUNCHER_TOOL_IDS.has(toolId.trim().toLowerCase());
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3409
3419
|
// ../shared_libs/play-runtime/backend.ts
|
|
3410
3420
|
var PLAY_RUNTIME_BACKENDS = {
|
|
3411
3421
|
localProcess: "local_process",
|
|
@@ -3686,6 +3696,9 @@ function resolveToolExecuteTimeoutMs(toolId, input) {
|
|
|
3686
3696
|
input
|
|
3687
3697
|
);
|
|
3688
3698
|
if (theirstackTimeoutMs !== null) return theirstackTimeoutMs;
|
|
3699
|
+
if (usesExtendedBetterContactLauncherBudget(normalized)) {
|
|
3700
|
+
return BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS;
|
|
3701
|
+
}
|
|
3689
3702
|
return normalized === "deeplineagent" || normalized === "deeplineagent_deeplineagent" || normalized === "ai_inference" || normalized === "deeplineagent_ai_inference" || normalized === "aiinference" ? DEEPLINEAGENT_EXECUTE_TIMEOUT_MS : void 0;
|
|
3690
3703
|
}
|
|
3691
3704
|
var RUNS_FAILED_LOG_LIMIT = 20;
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"dist/bundling-sources/sdk/src/tool-output.ts",
|
|
21
21
|
"dist/bundling-sources/sdk/src/types.ts",
|
|
22
22
|
"dist/bundling-sources/sdk/src/version.ts",
|
|
23
|
+
"dist/bundling-sources/shared_libs/integrations/bettercontact-execution-policy.ts",
|
|
23
24
|
"dist/bundling-sources/shared_libs/integrations/theirstack-execution-policy.ts",
|
|
24
25
|
"dist/bundling-sources/shared_libs/observability/node-tracing.ts",
|
|
25
26
|
"dist/bundling-sources/shared_libs/observability/redaction.ts",
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
"dist/bundling-sources/shared_libs/play-runtime/backend.ts",
|
|
37
38
|
"dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts",
|
|
38
39
|
"dist/bundling-sources/shared_libs/play-runtime/batching-types.ts",
|
|
40
|
+
"dist/bundling-sources/shared_libs/play-runtime/bettercontact-batching.ts",
|
|
39
41
|
"dist/bundling-sources/shared_libs/play-runtime/bounded-dispatch.ts",
|
|
40
42
|
"dist/bundling-sources/shared_libs/play-runtime/builtin-pacing.ts",
|
|
41
43
|
"dist/bundling-sources/shared_libs/play-runtime/cell-provenance.ts",
|