deepline 0.3.141 → 0.3.142
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/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +65 -2
- package/dist/bundling-sources/shared_libs/observability/dlq.ts +11 -0
- package/dist/bundling-sources/shared_libs/observability/queue-health.ts +111 -0
- package/dist/bundling-sources/shared_libs/play-runtime/bettercontact-batching.ts +34 -8
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +271 -44
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +25 -6
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +132 -8
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +3 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +49 -54
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +14 -7
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +118 -29
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-queue-health.ts +183 -36
- package/dist/bundling-sources/shared_libs/product-notifications/events.ts +4 -1
- package/dist/cli/index.js +242 -12
- package/dist/cli/index.mjs +242 -12
- package/dist/index.d.mts +54 -2
- package/dist/index.d.ts +54 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
|
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
202
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
203
|
-
version: '0.3.
|
|
203
|
+
version: '0.3.142',
|
|
204
204
|
updateSummary:
|
|
205
205
|
'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
|
|
206
206
|
packageCapabilities: {
|
|
@@ -193,9 +193,9 @@ export type {
|
|
|
193
193
|
*
|
|
194
194
|
* Returned by {@link DeeplineClient.listTools} and ranked tool search. Use
|
|
195
195
|
* `getTool(toolId)` or the matching HTTP describe route for provider-specific
|
|
196
|
-
* schema, examples, pricing,
|
|
196
|
+
* schema, examples, pricing, extraction guidance, and execution metadata.
|
|
197
197
|
*/
|
|
198
|
-
export interface ToolDefinition {
|
|
198
|
+
export interface ToolDefinition extends ToolExecutionMetadataFields {
|
|
199
199
|
/** Unique tool identifier used in API calls (e.g. `"dropleads_search_people"`). */
|
|
200
200
|
toolId: string;
|
|
201
201
|
/** Provider that backs this tool (e.g. `"hunter"`, `"dropleads"`, `"test"`). */
|
|
@@ -345,6 +345,45 @@ export interface ToolDefinition {
|
|
|
345
345
|
connectionMessage?: string;
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
export type ToolBatchCapability = {
|
|
349
|
+
status: 'compiled' | 'exposed_uncompiled' | 'upstream_only';
|
|
350
|
+
surface: 'registry' | 'local' | 'docs';
|
|
351
|
+
batchKind: 'identifier_batch' | 'query_share' | 'async_dataset_job';
|
|
352
|
+
batchOperation: string | null;
|
|
353
|
+
maxBatchSize: number | null;
|
|
354
|
+
notes: string;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
/** Optional batching, pacing, and configured rate metadata for tool catalog entries. */
|
|
358
|
+
export interface ToolExecutionMetadataFields {
|
|
359
|
+
batchCapability?: ToolBatchCapability;
|
|
360
|
+
batch_capability?: ToolBatchCapability;
|
|
361
|
+
executionMetadata?: ToolExecutionMetadata;
|
|
362
|
+
execution_metadata?: ToolExecutionMetadata;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export type ToolExecutionMetadata = {
|
|
366
|
+
provider: string;
|
|
367
|
+
sourceOperation: string;
|
|
368
|
+
effectiveOperation: string;
|
|
369
|
+
batch: {
|
|
370
|
+
batchOperation: string | null;
|
|
371
|
+
maxBatchSize: number;
|
|
372
|
+
};
|
|
373
|
+
configuredRateLimits: Array<{
|
|
374
|
+
source: 'deepline_configuration';
|
|
375
|
+
scope: string;
|
|
376
|
+
requestsPerWindow: number;
|
|
377
|
+
windowMs: number;
|
|
378
|
+
usage: 'queue_hint' | 'enforced' | 'queue_hint_and_enforced' | 'disabled';
|
|
379
|
+
explicitMaxConcurrency: number | null;
|
|
380
|
+
}>;
|
|
381
|
+
queueHint: {
|
|
382
|
+
requestsPerSecondHint: number;
|
|
383
|
+
derivedConcurrency: number;
|
|
384
|
+
};
|
|
385
|
+
};
|
|
386
|
+
|
|
348
387
|
export interface ModelProviderOptionField {
|
|
349
388
|
name: string;
|
|
350
389
|
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
@@ -697,6 +736,11 @@ export interface PlayRunPackage {
|
|
|
697
736
|
startedAt?: number | null;
|
|
698
737
|
finishedAt?: number | null;
|
|
699
738
|
durationMs?: number | null;
|
|
739
|
+
outcome?: PlayRunOutcome;
|
|
740
|
+
recovery?: {
|
|
741
|
+
mode: 'replayed' | 'forced' | 'recovered' | 'joined';
|
|
742
|
+
sourceRunId?: string;
|
|
743
|
+
};
|
|
700
744
|
error?: string;
|
|
701
745
|
/** Canonical explanation of what this run is doing or waiting on. */
|
|
702
746
|
activity?: PlayRunActivityProjection | null;
|
|
@@ -742,6 +786,19 @@ export interface PlayRunPackage {
|
|
|
742
786
|
};
|
|
743
787
|
}
|
|
744
788
|
|
|
789
|
+
/**
|
|
790
|
+
* Stable classification of whether this run executed work or reused/recovered
|
|
791
|
+
* an existing durable run outcome.
|
|
792
|
+
*/
|
|
793
|
+
export type PlayRunOutcome =
|
|
794
|
+
| 'executed'
|
|
795
|
+
| 'reused'
|
|
796
|
+
| 'joined'
|
|
797
|
+
| 'recovered'
|
|
798
|
+
| 'forced'
|
|
799
|
+
| 'waiting'
|
|
800
|
+
| 'failed';
|
|
801
|
+
|
|
745
802
|
/**
|
|
746
803
|
* Current status of a play execution, returned by {@link DeeplineClient.getPlayStatus}.
|
|
747
804
|
*
|
|
@@ -779,6 +836,12 @@ export interface PlayStatus {
|
|
|
779
836
|
| 'completed'
|
|
780
837
|
| 'failed'
|
|
781
838
|
| 'cancelled';
|
|
839
|
+
/** How this run was admitted or recovered, when the server can prove it. */
|
|
840
|
+
outcome?: PlayRunOutcome;
|
|
841
|
+
recovery?: {
|
|
842
|
+
mode: 'replayed' | 'forced' | 'recovered' | 'joined';
|
|
843
|
+
sourceRunId?: string;
|
|
844
|
+
};
|
|
782
845
|
/** Execution progress with logs and error details. */
|
|
783
846
|
progress?: PlayProgressStatus;
|
|
784
847
|
/** Partial or final result. Available once the play returns. */
|
|
@@ -277,6 +277,8 @@ export type QueueItem = {
|
|
|
277
277
|
readonly editableFields: readonly FieldDefinition[];
|
|
278
278
|
readonly createdAt?: string;
|
|
279
279
|
readonly updatedAt?: string;
|
|
280
|
+
/** Native failure timestamp when the owner can provide it. */
|
|
281
|
+
readonly failedAt?: string;
|
|
280
282
|
readonly summary?: string;
|
|
281
283
|
readonly retiredAt?: string;
|
|
282
284
|
readonly retirementReason?: string;
|
|
@@ -445,6 +447,14 @@ export type QueueItemConnector = {
|
|
|
445
447
|
readonly source: string;
|
|
446
448
|
readonly mapNativeState: (nativeState: string) => QueueItemState;
|
|
447
449
|
readonly list: (context: QueueItemConnectorContext) => Promise<QueueItemList>;
|
|
450
|
+
/** Optional source-indexed failure-window listing for safe, bounded
|
|
451
|
+
* retirement planning. The lower bound is inclusive and the upper bound is
|
|
452
|
+
* exclusive. Connectors must not emulate this by scanning their ordinary
|
|
453
|
+
* item listing; unsupported sources leave the capability absent. */
|
|
454
|
+
readonly listFailedInRange?: (
|
|
455
|
+
context: QueueItemConnectorContext,
|
|
456
|
+
range: { after?: string; before: string },
|
|
457
|
+
) => Promise<QueueItemList>;
|
|
448
458
|
readonly get: (
|
|
449
459
|
context: QueueItemConnectorContext,
|
|
450
460
|
itemId: string,
|
|
@@ -470,6 +480,7 @@ export function normalizeQueueItemState(nativeState: string): QueueItemState {
|
|
|
470
480
|
return 'running';
|
|
471
481
|
}
|
|
472
482
|
if (
|
|
483
|
+
state === 'failed' ||
|
|
473
484
|
state === 'dead_lettered' ||
|
|
474
485
|
state === 'dead-lettered' ||
|
|
475
486
|
state === 'dlq'
|
|
@@ -363,6 +363,117 @@ export type QueueObservation = {
|
|
|
363
363
|
readonly adapter: string;
|
|
364
364
|
};
|
|
365
365
|
|
|
366
|
+
export type QueueRetirementCandidate = {
|
|
367
|
+
readonly itemId: string;
|
|
368
|
+
readonly bucket: 'blocked' | 'deadLettered';
|
|
369
|
+
readonly ageMs: number;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Remove operator-retired identities from a bounded source sample. The source
|
|
374
|
+
* still owns every row; the disposition overlay only changes what operators
|
|
375
|
+
* count as unresolved. Truncated samples remain lower bounds after filtering.
|
|
376
|
+
*/
|
|
377
|
+
export function excludeRetiredQueueCandidates(input: {
|
|
378
|
+
observation: QueueObservation;
|
|
379
|
+
candidates: readonly QueueRetirementCandidate[];
|
|
380
|
+
retiredItemIds: ReadonlySet<string>;
|
|
381
|
+
truncatedBuckets?: ReadonlySet<QueueRetirementCandidate['bucket']>;
|
|
382
|
+
}): QueueObservation {
|
|
383
|
+
const {
|
|
384
|
+
observation,
|
|
385
|
+
candidates,
|
|
386
|
+
retiredItemIds,
|
|
387
|
+
truncatedBuckets = new Set(),
|
|
388
|
+
} = input;
|
|
389
|
+
const blocked = candidates.filter(
|
|
390
|
+
(candidate) => candidate.bucket === 'blocked',
|
|
391
|
+
);
|
|
392
|
+
const deadLettered = candidates.filter(
|
|
393
|
+
(candidate) => candidate.bucket === 'deadLettered',
|
|
394
|
+
);
|
|
395
|
+
const adjustDepth = (
|
|
396
|
+
bucket: 'blocked' | 'deadLettered',
|
|
397
|
+
rows: readonly QueueRetirementCandidate[],
|
|
398
|
+
): Measured<number> => {
|
|
399
|
+
const measured = observation.depth[bucket];
|
|
400
|
+
if (measured.kind !== 'exact' && measured.kind !== 'lower_bound') {
|
|
401
|
+
return measured;
|
|
402
|
+
}
|
|
403
|
+
const activeSampleCount = rows.reduce(
|
|
404
|
+
(count, row) => count + Number(!retiredItemIds.has(row.itemId)),
|
|
405
|
+
0,
|
|
406
|
+
);
|
|
407
|
+
if (
|
|
408
|
+
measured.kind === 'lower_bound' ||
|
|
409
|
+
truncatedBuckets.has(bucket) ||
|
|
410
|
+
rows.length !== measured.value
|
|
411
|
+
) {
|
|
412
|
+
// For a partial source sample, only surviving sampled identities are a
|
|
413
|
+
// proven lower bound. Subtracting sampled retirements from the source's
|
|
414
|
+
// lower bound could overstate active work if unseen rows are retired too.
|
|
415
|
+
return { kind: 'lower_bound', value: activeSampleCount };
|
|
416
|
+
}
|
|
417
|
+
return { kind: 'exact', value: activeSampleCount };
|
|
418
|
+
};
|
|
419
|
+
const adjustAge = (
|
|
420
|
+
bucket: 'blocked' | 'deadLettered',
|
|
421
|
+
rows: readonly QueueRetirementCandidate[],
|
|
422
|
+
depth: Measured<number>,
|
|
423
|
+
): Measured<number> => {
|
|
424
|
+
const measured = observation.oldestAgeMs[bucket];
|
|
425
|
+
if (measured.kind !== 'exact' && measured.kind !== 'lower_bound') {
|
|
426
|
+
return measured;
|
|
427
|
+
}
|
|
428
|
+
if (depth.kind === 'unavailable') {
|
|
429
|
+
return {
|
|
430
|
+
kind: 'unavailable',
|
|
431
|
+
reason: `retirement candidate coverage is incomplete for ${bucket}`,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
const remaining = rows.filter((row) => !retiredItemIds.has(row.itemId));
|
|
435
|
+
if (remaining.length > 0) {
|
|
436
|
+
const value = Math.max(...remaining.map((row) => row.ageMs));
|
|
437
|
+
return measured.kind === 'lower_bound' ||
|
|
438
|
+
truncatedBuckets.has(bucket) ||
|
|
439
|
+
depth.kind === 'lower_bound'
|
|
440
|
+
? { kind: 'lower_bound', value }
|
|
441
|
+
: { kind: 'exact', value };
|
|
442
|
+
}
|
|
443
|
+
if (
|
|
444
|
+
measured.kind === 'exact' &&
|
|
445
|
+
depth.kind === 'exact' &&
|
|
446
|
+
depth.value === 0
|
|
447
|
+
) {
|
|
448
|
+
return { kind: 'not_applicable', reason: 'no work in this bucket' };
|
|
449
|
+
}
|
|
450
|
+
if (
|
|
451
|
+
measured.kind === 'lower_bound' ||
|
|
452
|
+
truncatedBuckets.has(bucket) ||
|
|
453
|
+
depth.kind === 'lower_bound'
|
|
454
|
+
) {
|
|
455
|
+
// The bounded source page may have unseen, non-retired rows.
|
|
456
|
+
return { kind: 'lower_bound', value: 0 };
|
|
457
|
+
}
|
|
458
|
+
return measured;
|
|
459
|
+
};
|
|
460
|
+
const blockedDepth = adjustDepth('blocked', blocked);
|
|
461
|
+
const deadLetteredDepth = adjustDepth('deadLettered', deadLettered);
|
|
462
|
+
return {
|
|
463
|
+
...observation,
|
|
464
|
+
depth: {
|
|
465
|
+
...observation.depth,
|
|
466
|
+
blocked: blockedDepth,
|
|
467
|
+
deadLettered: deadLetteredDepth,
|
|
468
|
+
},
|
|
469
|
+
oldestAgeMs: {
|
|
470
|
+
...observation.oldestAgeMs,
|
|
471
|
+
blocked: adjustAge('blocked', blocked, blockedDepth),
|
|
472
|
+
deadLettered: adjustAge('deadLettered', deadLettered, deadLetteredDepth),
|
|
473
|
+
},
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
366
477
|
function combineMeasurements(
|
|
367
478
|
observations: readonly QueueObservation[],
|
|
368
479
|
select: (observation: QueueObservation) => Measured<number>,
|
|
@@ -321,14 +321,23 @@ function rowMatchesCorrelation(
|
|
|
321
321
|
correlationField: string,
|
|
322
322
|
correlationValue: string,
|
|
323
323
|
): boolean {
|
|
324
|
-
|
|
325
|
-
(fields) =>
|
|
326
|
-
|
|
324
|
+
const matches = resultCustomFieldRecords(row).filter(
|
|
325
|
+
(fields) =>
|
|
326
|
+
fields[correlationField] === correlationValue ||
|
|
327
|
+
(fields.name === correlationField && fields.value === correlationValue),
|
|
328
|
+
).length;
|
|
329
|
+
if (matches > 1) {
|
|
330
|
+
throw new Error(
|
|
331
|
+
'BetterContact scoped result has ambiguous correlation identity.',
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return matches === 1;
|
|
327
335
|
}
|
|
328
336
|
|
|
329
337
|
function withoutCorrelationSelector(
|
|
330
338
|
row: unknown,
|
|
331
339
|
correlationField: string,
|
|
340
|
+
correlationValue: string,
|
|
332
341
|
): unknown {
|
|
333
342
|
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
334
343
|
return row;
|
|
@@ -337,7 +346,9 @@ function withoutCorrelationSelector(
|
|
|
337
346
|
const customFields = record.custom_fields;
|
|
338
347
|
const cleanRecord = (fields: Record<string, unknown>) => {
|
|
339
348
|
const cleaned = { ...fields };
|
|
340
|
-
|
|
349
|
+
if (cleaned[correlationField] === correlationValue) {
|
|
350
|
+
delete cleaned[correlationField];
|
|
351
|
+
}
|
|
341
352
|
return cleaned;
|
|
342
353
|
};
|
|
343
354
|
return {
|
|
@@ -345,6 +356,11 @@ function withoutCorrelationSelector(
|
|
|
345
356
|
custom_fields: Array.isArray(customFields)
|
|
346
357
|
? customFields
|
|
347
358
|
.filter(isPlainRecord)
|
|
359
|
+
.filter(
|
|
360
|
+
(fields) =>
|
|
361
|
+
fields.name !== correlationField ||
|
|
362
|
+
fields.value !== correlationValue,
|
|
363
|
+
)
|
|
348
364
|
.map(cleanRecord)
|
|
349
365
|
.filter((fields) => Object.keys(fields).length > 0)
|
|
350
366
|
: isPlainRecord(customFields)
|
|
@@ -381,7 +397,13 @@ export function isolateBetterContactScopedResult(
|
|
|
381
397
|
}
|
|
382
398
|
return {
|
|
383
399
|
...scopedResult,
|
|
384
|
-
data: [
|
|
400
|
+
data: [
|
|
401
|
+
withoutCorrelationSelector(
|
|
402
|
+
matches[0],
|
|
403
|
+
scope.correlationField,
|
|
404
|
+
scope.correlationValue,
|
|
405
|
+
),
|
|
406
|
+
],
|
|
385
407
|
};
|
|
386
408
|
}
|
|
387
409
|
|
|
@@ -417,11 +439,15 @@ function withoutCorrelationField(
|
|
|
417
439
|
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
418
440
|
return row;
|
|
419
441
|
}
|
|
420
|
-
const
|
|
421
|
-
if (!
|
|
442
|
+
const correlationEntry = correlationEntryForItem(item);
|
|
443
|
+
if (!correlationEntry) {
|
|
422
444
|
return row;
|
|
423
445
|
}
|
|
424
|
-
return withoutCorrelationSelector(
|
|
446
|
+
return withoutCorrelationSelector(
|
|
447
|
+
row,
|
|
448
|
+
correlationEntry[0],
|
|
449
|
+
String(correlationEntry[1]),
|
|
450
|
+
);
|
|
425
451
|
}
|
|
426
452
|
|
|
427
453
|
function scopedRequestIdForItem(
|