deepline 0.1.227 → 0.1.228

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.
@@ -140,6 +140,11 @@ import type {
140
140
  PlayRunLedgerStepProgress,
141
141
  PlayRunLedgerStepStatus,
142
142
  } from '../../../shared_libs/play-runtime/run-ledger';
143
+ import {
144
+ MAX_LEDGER_LOG_LINE_LENGTH,
145
+ MAX_LEDGER_LOG_LINES_PER_EVENT,
146
+ } from '../../../shared_libs/play-runtime/ledger-safe-payload';
147
+ import { partitionWorkerLedgerEvents } from './runtime/ledger-event-batches';
143
148
  import type { PlayHarnessRpc } from '../../play-harness-worker/src/rpc-types';
144
149
  import {
145
150
  createCsvDatasetHandle,
@@ -7778,7 +7783,10 @@ async function executeRunRequest(
7778
7783
  let ledgerFlushQueueDepth = 0;
7779
7784
 
7780
7785
  const appendRunLogLine = (line: string) => {
7781
- const trimmed = redactSecretsFromLogString(line.trim());
7786
+ const trimmed = redactSecretsFromLogString(line.trim()).slice(
7787
+ 0,
7788
+ MAX_LEDGER_LOG_LINE_LENGTH,
7789
+ );
7782
7790
  if (!trimmed) return;
7783
7791
  workBudgetMeter.count('log');
7784
7792
  totalEmittedLogLines += 1;
@@ -7861,17 +7869,27 @@ async function executeRunRequest(
7861
7869
  pendingLedgerEvents = [];
7862
7870
 
7863
7871
  if (pendingRunLogLines.length > 0) {
7864
- events.push({
7865
- type: 'log.appended',
7866
- runId: req.runId,
7867
- source: 'worker',
7868
- occurredAt,
7869
- lines: pendingRunLogLines,
7870
- // Positional cursor: pendingRunLogLines always holds the LAST
7871
- // pending lines emitted on this channel, so the offset of its first
7872
- // line is total-emitted minus pending length.
7873
- channelOffset: totalEmittedLogLines - pendingRunLogLines.length,
7874
- });
7872
+ const channelOffset = totalEmittedLogLines - pendingRunLogLines.length;
7873
+ for (
7874
+ let start = 0;
7875
+ start < pendingRunLogLines.length;
7876
+ start += MAX_LEDGER_LOG_LINES_PER_EVENT
7877
+ ) {
7878
+ events.push({
7879
+ type: 'log.appended',
7880
+ runId: req.runId,
7881
+ source: 'worker',
7882
+ occurredAt,
7883
+ lines: pendingRunLogLines.slice(
7884
+ start,
7885
+ start + MAX_LEDGER_LOG_LINES_PER_EVENT,
7886
+ ),
7887
+ // Positional cursor: pendingRunLogLines always holds the LAST
7888
+ // pending lines emitted on this channel, so the offset of its first
7889
+ // line is total-emitted minus pending length.
7890
+ channelOffset: channelOffset + start,
7891
+ });
7892
+ }
7875
7893
  pendingRunLogLines = [];
7876
7894
  }
7877
7895
 
@@ -7956,19 +7974,26 @@ async function executeRunRequest(
7956
7974
  ledgerFlushInFlight = ledgerFlushInFlight
7957
7975
  .catch(() => undefined)
7958
7976
  .then(async () => {
7959
- try {
7960
- await postRuntimeApi(req.baseUrl, req.executorToken, {
7961
- action: 'append_run_events',
7962
- playId: req.runId,
7963
- events,
7964
- });
7965
- } catch {
7966
- pendingLedgerEvents = [...events, ...pendingLedgerEvents];
7967
- throw new Error('runtime run-ledger append failed');
7968
- } finally {
7969
- ledgerFlushQueueDepth = Math.max(0, ledgerFlushQueueDepth - 1);
7977
+ const batches = partitionWorkerLedgerEvents(events);
7978
+ for (let index = 0; index < batches.length; index += 1) {
7979
+ try {
7980
+ await postRuntimeApi(req.baseUrl, req.executorToken, {
7981
+ action: 'append_run_events',
7982
+ playId: req.runId,
7983
+ events: batches[index]!,
7984
+ });
7985
+ } catch {
7986
+ pendingLedgerEvents = [
7987
+ ...batches.slice(index).flat(),
7988
+ ...pendingLedgerEvents,
7989
+ ];
7990
+ throw new Error('runtime run-ledger append failed');
7991
+ }
7970
7992
  }
7971
7993
  })
7994
+ .finally(() => {
7995
+ ledgerFlushQueueDepth = Math.max(0, ledgerFlushQueueDepth - 1);
7996
+ })
7972
7997
  .catch(() => undefined);
7973
7998
  return force ? ledgerFlushInFlight : Promise.resolve();
7974
7999
  };
@@ -7988,15 +8013,21 @@ async function executeRunRequest(
7988
8013
  ]);
7989
8014
  const events = [...drainPendingLedgerEvents(now), terminalEvent];
7990
8015
  if (events.length === 0) return;
7991
- try {
7992
- await postRuntimeApi(req.baseUrl, req.executorToken, {
7993
- action: 'append_run_events',
7994
- playId: req.runId,
7995
- events,
7996
- });
7997
- } catch (error) {
7998
- pendingLedgerEvents = [...events, ...pendingLedgerEvents];
7999
- throw error;
8016
+ const batches = partitionWorkerLedgerEvents(events);
8017
+ for (let index = 0; index < batches.length; index += 1) {
8018
+ try {
8019
+ await postRuntimeApi(req.baseUrl, req.executorToken, {
8020
+ action: 'append_run_events',
8021
+ playId: req.runId,
8022
+ events: batches[index]!,
8023
+ });
8024
+ } catch (error) {
8025
+ pendingLedgerEvents = [
8026
+ ...batches.slice(index).flat(),
8027
+ ...pendingLedgerEvents,
8028
+ ];
8029
+ throw error;
8030
+ }
8000
8031
  }
8001
8032
  };
8002
8033
 
@@ -0,0 +1,81 @@
1
+ import {
2
+ MAX_LEDGER_LOG_LINE_LENGTH,
3
+ MAX_LEDGER_LOG_LINES_PER_EVENT,
4
+ } from '../../../../shared_libs/play-runtime/ledger-safe-payload';
5
+ import type { PlayRunLedgerEvent } from '../../../../shared_libs/play-runtime/run-ledger';
6
+
7
+ /** Keep a Worker-to-runtime append comfortably below the API and Convex limits. */
8
+ export const WORKER_LEDGER_EVENT_BATCH_MAX_BYTES = 128 * 1024;
9
+ export const WORKER_LEDGER_EVENTS_PER_APPEND_MAX = 100;
10
+
11
+ const encoder = new TextEncoder();
12
+
13
+ function encodedJsonBytes(value: unknown): number {
14
+ return encoder.encode(JSON.stringify(value)).length;
15
+ }
16
+
17
+ function splitLogEvent(
18
+ event: Extract<PlayRunLedgerEvent, { type: 'log.appended' }>,
19
+ ): PlayRunLedgerEvent[] {
20
+ const events: PlayRunLedgerEvent[] = [];
21
+ const lines = event.lines.map((line) =>
22
+ line.slice(0, MAX_LEDGER_LOG_LINE_LENGTH),
23
+ );
24
+ let start = 0;
25
+ while (start < lines.length) {
26
+ let end = start;
27
+ let bytes = encodedJsonBytes({ ...event, lines: [] });
28
+ while (end < lines.length && end - start < MAX_LEDGER_LOG_LINES_PER_EVENT) {
29
+ const lineBytes = encodedJsonBytes(lines[end]!);
30
+ if (
31
+ end > start &&
32
+ bytes + lineBytes + 1 > WORKER_LEDGER_EVENT_BATCH_MAX_BYTES
33
+ ) {
34
+ break;
35
+ }
36
+ bytes += lineBytes + 1;
37
+ end += 1;
38
+ }
39
+ events.push({
40
+ ...event,
41
+ lines: lines.slice(start, end),
42
+ ...(typeof event.channelOffset === 'number'
43
+ ? { channelOffset: event.channelOffset + start }
44
+ : {}),
45
+ });
46
+ start = end;
47
+ }
48
+ return events;
49
+ }
50
+
51
+ /**
52
+ * Partition ledger delivery without dropping positional log lines. The worker
53
+ * retries only unacknowledged batches, so each batch must be independently
54
+ * valid for the app-runtime and Convex boundaries.
55
+ */
56
+ export function partitionWorkerLedgerEvents(
57
+ events: readonly PlayRunLedgerEvent[],
58
+ ): PlayRunLedgerEvent[][] {
59
+ const batches: PlayRunLedgerEvent[][] = [];
60
+ let batch: PlayRunLedgerEvent[] = [];
61
+ let batchBytes = 2;
62
+
63
+ for (const event of events.flatMap((event) =>
64
+ event.type === 'log.appended' ? splitLogEvent(event) : [event],
65
+ )) {
66
+ const eventBytes = encodedJsonBytes(event);
67
+ if (
68
+ batch.length > 0 &&
69
+ (batch.length >= WORKER_LEDGER_EVENTS_PER_APPEND_MAX ||
70
+ batchBytes + eventBytes + 1 > WORKER_LEDGER_EVENT_BATCH_MAX_BYTES)
71
+ ) {
72
+ batches.push(batch);
73
+ batch = [];
74
+ batchBytes = 2;
75
+ }
76
+ batch.push(event);
77
+ batchBytes += eventBytes + 1;
78
+ }
79
+ if (batch.length > 0) batches.push(batch);
80
+ return batches;
81
+ }
@@ -243,8 +243,30 @@ type ExecuteToolRawOptions = {
243
243
  maxRetries?: number;
244
244
  };
245
245
 
246
- function resolveToolExecuteTimeoutMs(toolId: string): number | undefined {
246
+ /**
247
+ * The server launches the Apify run, polls it for `payload.timeoutMs`, then
248
+ * reads its dataset before replying. Keep the client request alive through
249
+ * that final hand-off so a pollable run id is never lost to the SDK default
250
+ * timeout.
251
+ */
252
+ const APIFY_SYNC_DEFAULT_TIMEOUT_MS = 300_000;
253
+ const APIFY_SYNC_RESPONSE_GRACE_MS = 90_000;
254
+
255
+ function resolveToolExecuteTimeoutMs(
256
+ toolId: string,
257
+ input: Record<string, unknown>,
258
+ ): number | undefined {
247
259
  const normalized = toolId.trim().toLowerCase();
260
+ if (normalized === 'apify_run_actor_sync') {
261
+ const requestedTimeoutMs = input.timeoutMs ?? APIFY_SYNC_DEFAULT_TIMEOUT_MS;
262
+ if (
263
+ typeof requestedTimeoutMs === 'number' &&
264
+ Number.isFinite(requestedTimeoutMs) &&
265
+ requestedTimeoutMs > 0
266
+ ) {
267
+ return Math.floor(requestedTimeoutMs) + APIFY_SYNC_RESPONSE_GRACE_MS;
268
+ }
269
+ }
248
270
  return normalized === 'deeplineagent' ||
249
271
  normalized === 'deeplineagent_deeplineagent' ||
250
272
  normalized === 'ai_inference' ||
@@ -1417,7 +1439,7 @@ export class DeeplineClient {
1417
1439
  headers,
1418
1440
  {
1419
1441
  forbiddenAsApiError: true,
1420
- timeout: options?.timeout ?? resolveToolExecuteTimeoutMs(toolId),
1442
+ timeout: options?.timeout ?? resolveToolExecuteTimeoutMs(toolId, input),
1421
1443
  maxRetries: options?.maxRetries ?? 0,
1422
1444
  exactUrlOnly: true,
1423
1445
  },
@@ -161,7 +161,7 @@ export interface PlayCallOptions {
161
161
  * sqlListeners: [
162
162
  * {
163
163
  * id: 'jobs',
164
- * tool: 'tamradar.company_radar',
164
+ * tool: 'deepline_native.company_radar',
165
165
  * stream: 'company_job_openings',
166
166
  * operations: ['INSERT'],
167
167
  * },
@@ -237,7 +237,7 @@ export type SqlListenerWhere = {
237
237
  export type SqlListenerDeclaration = {
238
238
  /** Short id unique inside this play. Deepline stores it as playName.id. */
239
239
  id: string;
240
- /** Modeled monitor tool id, for example "tamradar.company_radar". */
240
+ /** Modeled monitor tool id, for example "deepline_native.company_radar". */
241
241
  tool: string;
242
242
  /** Stream key exposed by the modeled monitor tool, for example "company_job_openings". */
243
243
  stream: string;
@@ -108,10 +108,10 @@ export const SDK_RELEASE = {
108
108
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
109
109
  // 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
110
110
  // automatically without blocking their current command.
111
- version: '0.1.227',
111
+ version: '0.1.228',
112
112
  apiContract: '2026-06-dataset-handle-results-hard-cutover',
113
113
  supportPolicy: {
114
- latest: '0.1.227',
114
+ latest: '0.1.228',
115
115
  minimumSupported: '0.1.53',
116
116
  deprecatedBelow: '0.1.219',
117
117
  commandMinimumSupported: [