deepline 0.3.434 → 0.3.435

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 (102) hide show
  1. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  2. package/dist/bundling-sources/shared_libs/monitors/billing/state.ts +32 -3
  3. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-dead-letter.ts +7 -0
  4. package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +18 -0
  5. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +9 -119
  6. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-tool-hooks.ts +155 -0
  7. package/dist/bundling-sources/shared_libs/product-notifications/slack-delivery.ts +32 -3
  8. package/dist/bundling-sources/shared_libs/product-notifications/slack-transport.ts +2 -0
  9. package/dist/cli/{auth-ZWASM3JJ.mjs → auth-EQ3MPGNF.mjs} +4 -4
  10. package/dist/cli/auth-N3273IA5.js +40 -0
  11. package/dist/cli/{billing-SQI72SQI.js → billing-23HTLBTU.js} +73 -73
  12. package/dist/cli/{billing-BICK6R5J.mjs → billing-AZXP6FQ6.mjs} +4 -4
  13. package/dist/cli/{chunk-E5HBBE7F.js → chunk-26X3B5SV.js} +16 -16
  14. package/dist/cli/{chunk-6AOEQVTN.js → chunk-2CM22MQI.js} +50 -50
  15. package/dist/cli/{chunk-NIRI3ILU.mjs → chunk-5WJ4K7ZJ.mjs} +1 -1
  16. package/dist/cli/{chunk-K324EUI2.mjs → chunk-6R6BUVOX.mjs} +8 -8
  17. package/dist/cli/{chunk-ISU67CJA.mjs → chunk-7JZD5E5E.mjs} +3 -3
  18. package/dist/cli/{chunk-KPPCM62O.js → chunk-B6ZCP3Z5.js} +6 -6
  19. package/dist/cli/{chunk-NP5HEQAW.mjs → chunk-BFYSJUB7.mjs} +2 -2
  20. package/dist/cli/{chunk-6ZGPHO5R.js → chunk-CF4T46L6.js} +1 -1
  21. package/dist/cli/{chunk-Q7IWP5UM.js → chunk-CTDN4FZR.js} +2 -2
  22. package/dist/cli/{chunk-KXB5UAFH.js → chunk-DCSRQRUL.js} +2 -2
  23. package/dist/cli/{chunk-ISUKMN5B.mjs → chunk-DOWK7NOY.mjs} +1 -1
  24. package/dist/cli/{chunk-QY2DYCUJ.mjs → chunk-EW5TD772.mjs} +2 -2
  25. package/dist/cli/{chunk-GDIZKSKB.mjs → chunk-HBZWT2US.mjs} +2 -2
  26. package/dist/cli/{chunk-KHR6V4CE.mjs → chunk-HHJYD3PC.mjs} +1 -1
  27. package/dist/cli/{chunk-GVMQS6G7.js → chunk-HN6WJC4Y.js} +10 -10
  28. package/dist/cli/{chunk-A4RM6SQK.mjs → chunk-HR7TOTM7.mjs} +1 -1
  29. package/dist/cli/{chunk-VFNLUKRT.mjs → chunk-IIW7JOKQ.mjs} +4 -4
  30. package/dist/cli/{chunk-EAZGVI2H.js → chunk-KDTYSHTE.js} +8 -8
  31. package/dist/cli/{chunk-NLD7TUJ6.js → chunk-KSXYGT6J.js} +8 -8
  32. package/dist/cli/{chunk-WB7HZOGR.mjs → chunk-LQTNFYOL.mjs} +2 -2
  33. package/dist/cli/{chunk-NBZGQRBQ.mjs → chunk-PBK2HVL5.mjs} +3 -3
  34. package/dist/cli/{chunk-FECJ5LBK.js → chunk-PKHERMMF.js} +52 -52
  35. package/dist/cli/{chunk-OLWNESMO.js → chunk-PSOCBKRK.js} +35 -35
  36. package/dist/cli/{chunk-EEQEQRTO.js → chunk-R2DKLSIE.js} +4 -4
  37. package/dist/cli/{chunk-6P3SCJZA.mjs → chunk-R4KGIU4P.mjs} +1 -1
  38. package/dist/cli/{chunk-S57PLAGM.mjs → chunk-TXG7K6OK.mjs} +1 -1
  39. package/dist/cli/{chunk-XVDYAJ4B.js → chunk-UDWRO43L.js} +79 -79
  40. package/dist/cli/{chunk-CYJJUUIT.mjs → chunk-UFXJB5VM.mjs} +5 -5
  41. package/dist/cli/{chunk-TFOUACKF.js → chunk-V5S5JGXE.js} +20 -20
  42. package/dist/cli/{chunk-SVRL42S7.js → chunk-VZOIX2SU.js} +26 -26
  43. package/dist/cli/{chunk-XZBBTLIL.js → chunk-X3GM2FO2.js} +6 -6
  44. package/dist/cli/{chunk-HMJS4EP4.mjs → chunk-X7L2ICG7.mjs} +3 -3
  45. package/dist/cli/{csv-JDI6IB23.js → csv-CHD4AORD.js} +12 -12
  46. package/dist/cli/{csv-4VNH4CNZ.mjs → csv-RBYDE2VF.mjs} +3 -3
  47. package/dist/cli/{db-RIXQ4WX5.js → db-G66I35IX.js} +25 -25
  48. package/dist/cli/{db-2BT7CMVU.mjs → db-PEAA46HL.mjs} +4 -4
  49. package/dist/cli/{describe-ODFGJI7Q.js → describe-N3WGB4RU.js} +16 -16
  50. package/dist/cli/{describe-SF4TKH2Y.mjs → describe-YAAQ22BK.mjs} +12 -12
  51. package/dist/cli/{executions-DNPJRBW2.mjs → executions-NFYYKOQ4.mjs} +4 -4
  52. package/dist/cli/{executions-HZTV7BRI.js → executions-XL3VMVUS.js} +6 -6
  53. package/dist/cli/{feedback-OBODUERI.js → feedback-KZ6SBLG6.js} +7 -7
  54. package/dist/cli/{feedback-SK5AKR7W.mjs → feedback-PUTCVMTD.mjs} +3 -3
  55. package/dist/cli/git-QTHF7KRA.js +24 -0
  56. package/dist/cli/{git-7IE5J53C.mjs → git-UU3XVCT4.mjs} +4 -4
  57. package/dist/cli/index.js +131 -131
  58. package/dist/cli/index.mjs +36 -36
  59. package/dist/cli/{legacy-noops-UIQWCOGF.mjs → legacy-noops-BOGHHXVF.mjs} +4 -4
  60. package/dist/cli/{legacy-noops-CVHQS2Z4.js → legacy-noops-FZSGPSPI.js} +7 -7
  61. package/dist/cli/{monitors-37W655H5.js → monitors-BDUIUKJJ.js} +84 -84
  62. package/dist/cli/{monitors-47U23SEH.mjs → monitors-XUSIJAQS.mjs} +4 -4
  63. package/dist/cli/{org-AS7BDCF2.js → org-4M4LF366.js} +64 -64
  64. package/dist/cli/{org-Z3WRCPPN.mjs → org-YKDMSVRT.mjs} +4 -4
  65. package/dist/cli/{play-CQNGRFJX.mjs → play-CLGVNXQW.mjs} +10 -10
  66. package/dist/cli/{play-IDRYO2XG.js → play-XYAA5UC5.js} +283 -283
  67. package/dist/cli/{providers-LXH4JT4W.js → providers-HS2JM5MQ.js} +7 -7
  68. package/dist/cli/{providers-XFGJTDA2.mjs → providers-MFI6MK72.mjs} +4 -4
  69. package/dist/cli/{quickstart-AFPLY6AL.js → quickstart-BHNM5TQN.js} +5 -5
  70. package/dist/cli/{quickstart-I34EZG6K.mjs → quickstart-CWUBSTJ2.mjs} +3 -3
  71. package/dist/cli/search-MN7DAPYR.js +30 -0
  72. package/dist/cli/{search-GV3H73R7.mjs → search-VK6EN3BR.mjs} +5 -5
  73. package/dist/cli/{secrets-Z2RLL7MD.mjs → secrets-GPRZ4XF5.mjs} +4 -4
  74. package/dist/cli/{secrets-ADPINHAV.js → secrets-HJ3HW5TQ.js} +13 -13
  75. package/dist/cli/{sessions-H7QVYCTY.mjs → sessions-NN4AJLGF.mjs} +4 -4
  76. package/dist/cli/{sessions-A635YVAT.js → sessions-XJYXZKFA.js} +5 -5
  77. package/dist/cli/{settings-G6FM442W.js → settings-3Y4FZRE6.js} +75 -75
  78. package/dist/cli/{settings-LUYTVWTR.mjs → settings-ADLQGWTJ.mjs} +8 -8
  79. package/dist/cli/{setup-7Y3JAMBT.js → setup-DR2F5EHD.js} +42 -42
  80. package/dist/cli/{setup-EPRBDJXZ.mjs → setup-PJUA2JPA.mjs} +7 -7
  81. package/dist/cli/skills-AF3OYN25.js +29 -0
  82. package/dist/cli/{skills-2SNUHQC7.mjs → skills-YYLZ6ZCQ.mjs} +5 -5
  83. package/dist/cli/tools-3XW4TJBV.js +44 -0
  84. package/dist/cli/{tools-PJPLF63I.mjs → tools-GIVNDAF4.mjs} +12 -12
  85. package/dist/cli/update-2WLAPOXT.js +31 -0
  86. package/dist/cli/{update-JP7BOOIZ.mjs → update-IZO6EDLX.mjs} +7 -7
  87. package/dist/cli/{workflow-42ISZKK5.mjs → workflow-FO67TXGC.mjs} +5 -5
  88. package/dist/cli/{workflow-L33RGJ4A.js → workflow-O4TG6Q3C.js} +15 -15
  89. package/dist/index.js +6 -6
  90. package/dist/index.mjs +3 -3
  91. package/dist/install-integrity.json +79 -78
  92. package/dist/release.d.mts +1 -1
  93. package/dist/release.d.ts +1 -1
  94. package/dist/release.js +2 -2
  95. package/dist/release.mjs +1 -1
  96. package/package.json +1 -1
  97. package/dist/cli/auth-KFYDF5TS.js +0 -40
  98. package/dist/cli/git-WMBBN7S7.js +0 -24
  99. package/dist/cli/search-HBEAM36R.js +0 -30
  100. package/dist/cli/skills-GV2L7QWT.js +0 -29
  101. package/dist/cli/tools-PXECTR3C.js +0 -44
  102. package/dist/cli/update-LKVWKWIV.js +0 -31
@@ -205,7 +205,7 @@ export const SDK_RELEASE = {
205
205
  // getters keep their established compatibility behavior.
206
206
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
207
207
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
208
- version: '0.3.434',
208
+ version: '0.3.435',
209
209
  updateSummary: {
210
210
  introducedIn: '0.3.90',
211
211
  summary:
@@ -457,6 +457,35 @@ export function monitorBillingStateForSuspension(input: {
457
457
  };
458
458
  }
459
459
 
460
+ /**
461
+ * Deepline's org-wide billing pause of a direct monitor whose slot-month was
462
+ * already paid (an exhausted wallet, a cap, an archive). Like
463
+ * `monitorBillingStateForDisabled`, the paid-through anchor survives: the
464
+ * revival re-create reads it through `readPreviousMonitorBillingState`, and
465
+ * without it the month was charged a second time. Renewals still stop,
466
+ * because they range on active rows and the caller clears the index fields.
467
+ * A renewal whose own charge failed collected nothing and keeps using
468
+ * `monitorBillingStateForSuspension`.
469
+ */
470
+ export function monitorBillingStateForDirectBillingSuspension(input: {
471
+ previous?: StoredMonitorBillingState | null;
472
+ reason: string;
473
+ error?: string;
474
+ disablePending?: boolean;
475
+ }): StoredMonitorBillingState {
476
+ return {
477
+ ...monitorBillingStateForSuspension(input),
478
+ next_renewal_at: input.previous?.next_renewal_at ?? null,
479
+ provider_next_charge_at: input.previous?.provider_next_charge_at ?? null,
480
+ cancel_before_at: input.previous?.cancel_before_at ?? null,
481
+ };
482
+ }
483
+
484
+ /**
485
+ * Billing cleanup's upstream delete failed. The paid-through dates are the
486
+ * ones the suspension that started cleanup already chose: a direct billing
487
+ * pause keeps them, a failed renewal or settle has nulled them.
488
+ */
460
489
  export function monitorBillingStateForUpstreamDisableFailed(input: {
461
490
  previous?: StoredMonitorBillingState | null;
462
491
  error: string;
@@ -468,9 +497,9 @@ export function monitorBillingStateForUpstreamDisableFailed(input: {
468
497
  reactivation_required: true,
469
498
  suspension_reason: 'billing_failure_upstream_disable_failed',
470
499
  last_billing_error: input.error,
471
- next_renewal_at: null,
472
- provider_next_charge_at: null,
473
- cancel_before_at: null,
500
+ next_renewal_at: input.previous?.next_renewal_at ?? null,
501
+ provider_next_charge_at: input.previous?.provider_next_charge_at ?? null,
502
+ cancel_before_at: input.previous?.cancel_before_at ?? null,
474
503
  };
475
504
  }
476
505
 
@@ -64,6 +64,9 @@ export function classifyInboundFailure(error: unknown): InboundFailureClass {
64
64
  '08003', // connection does not exist
65
65
  '08006', // connection failure
66
66
  '53300', // too many connections
67
+ '40001', // serialization failure
68
+ '40P01', // deadlock detected
69
+ '55P03', // lock not available (a lock_timeout expired)
67
70
  ]);
68
71
  if (code && transientCodes.has(code)) return 'transient';
69
72
 
@@ -81,6 +84,10 @@ export function classifyInboundFailure(error: unknown): InboundFailureClass {
81
84
  /convex.*(?:unavailable|timeout|network)/i,
82
85
  /fetch failed/i,
83
86
  /socket hang up/i,
87
+ // A concurrent catalog change on the tenant table (grant, column add,
88
+ // dynamic promotion). Raised as XX000, so it is matched by message, as in
89
+ // connection-uris.ts and runtime-sheet-data-plane-readiness.ts.
90
+ /tuple concurrently updated|could not serialize access|deadlock detected/i,
84
91
  ];
85
92
  if (transientPatterns.some((pattern) => pattern.test(message))) {
86
93
  return 'transient';
@@ -747,6 +747,24 @@
747
747
  "failureAlert": "vercel_route_wrapper",
748
748
  "domainReportRequired": true
749
749
  },
750
+ {
751
+ "id": "vercel.monitor-delivery-canary",
752
+ "name": "TamRadar monitor delivery canary",
753
+ "scheduler": "vercel_cron",
754
+ "cadence": "*/30 * * * *",
755
+ "timezone": "UTC",
756
+ "source": "apps/deepline-api/vercel.json",
757
+ "locator": "/api/v2/cron/monitor-delivery-canary",
758
+ "owner": "monitors",
759
+ "criticality": "critical",
760
+ "purpose": "Send one synthetic TamRadar job-opening finding to the internal canary monitor's own callback route so the deepline-monitor-delivery-heartbeat Play can prove receiver, queue, typed write and Play dispatch end to end.",
761
+ "successDefinition": "The canary monitor's binding is read from Convex and its callback route acknowledges this slot's synthetic finding with a 2xx.",
762
+ "failureDefinition": "The canary monitor is missing, inactive or mis-bound, or its callback route returns a non-2xx response or times out.",
763
+ "startGraceMs": 300000,
764
+ "runTimeoutMs": 60000,
765
+ "failureAlert": "vercel_route_wrapper",
766
+ "domainReportRequired": true
767
+ },
750
768
  {
751
769
  "id": "vercel.public-tool-execution-blob-cleanup",
752
770
  "name": "Public tool execution blob cleanup",
@@ -25,7 +25,6 @@ import {
25
25
  iteratePlayDatasetInputPages,
26
26
  type PlayDataset,
27
27
  } from '../../plays/dataset';
28
- import type { PlayQueueHint } from '../governor/rate-state-backend';
29
28
  import { AppRuntimeRateStateBackend } from '../governor/app-runtime-rate-state-backend';
30
29
  import { AppRuntimeBudgetStateBackend } from '../governor/app-runtime-budget-state-backend';
31
30
  import {
@@ -90,8 +89,10 @@ import {
90
89
  type PlayRunnerTerminalTransportEvent,
91
90
  } from './runtime-terminal-push';
92
91
  import { vercelProtectionBypassHeader } from '../vercel-protection';
93
- import { getPlayRuntimeBatchStrategy } from '../play-runtime-batching-registry';
94
- import type { ToolResultMetadataInput } from '../tool-result';
92
+ import {
93
+ createRuntimeToolHooks,
94
+ type RuntimeToolMetadata,
95
+ } from './runtime-tool-hooks';
95
96
  import type {
96
97
  ContextOptions,
97
98
  RuntimeSheetDatasetPageReaderScope,
@@ -227,7 +228,6 @@ import {
227
228
  sqlSafePlayColumnName,
228
229
  type PlaySheetContract,
229
230
  } from '../../plays/static-pipeline';
230
- import { isIntegrationEventWaitToolId } from '../event-wait-tools';
231
231
  import { type PlayBundleArtifact } from '../../plays/artifact-types';
232
232
  import {
233
233
  RuntimeReceiptStoreAdapter,
@@ -356,32 +356,6 @@ export function playRunnerReceiptStoreAdapterOptions() {
356
356
  // co-located receipt gateway. There is no host-injected or app-runtime path.
357
357
  type RuntimeStepReceiptHandlers = RuntimeReceiptStoreHandlers;
358
358
 
359
- type RuntimeToolMetadata = {
360
- reusableResult?: { supported: boolean; unavailableReason: string | null };
361
- provider?: string | null;
362
- operation?: string | null;
363
- extractors?: ToolResultMetadataInput['extractors'];
364
- actionCacheVersion: string;
365
- actionCacheReadVersions?: string[];
366
- queueHints?: unknown;
367
- retry?: {
368
- idempotencyScope?: 'invocation';
369
- retrySafeTransientHttp?: boolean;
370
- requiresExecutionFence?: boolean;
371
- };
372
- targetGetters?: Record<string, string[]>;
373
- listExtractorPaths?: string[];
374
- listExtractorAliases?: ToolResultMetadataInput['listExtractorAliases'];
375
- listIdentityGetters?: Record<string, string[]>;
376
- eventWait?: boolean;
377
- asyncOperation?:
378
- | import('../async-operation').AsyncOperationContractWire
379
- | null;
380
- asyncOperationPoll?:
381
- | import('../async-operation').AsyncOperationPollExecution
382
- | null;
383
- };
384
-
385
359
  type RuntimeToolAuthScope = {
386
360
  digest?: unknown;
387
361
  managedAsyncExecutionRecovery?: unknown;
@@ -2489,101 +2463,17 @@ async function run(
2489
2463
  options,
2490
2464
  );
2491
2465
  },
2492
- getToolQueueHints: async (toolId) => {
2493
- const metadata = await getRuntimeToolMetadata(toolId);
2494
- return Array.isArray(metadata.queueHints)
2495
- ? (metadata.queueHints as PlayQueueHint[])
2496
- : [];
2497
- },
2498
- getToolProvider: async (toolId) => {
2499
- const provider = (await getRuntimeToolMetadata(toolId)).provider;
2500
- return typeof provider === 'string' && provider.trim()
2501
- ? provider.trim()
2502
- : null;
2503
- },
2504
- getToolOperation: async (toolId) => {
2505
- const operation = (await getRuntimeToolMetadata(toolId)).operation;
2506
- return typeof operation === 'string' && operation.trim()
2507
- ? operation.trim()
2508
- : null;
2509
- },
2510
- getToolIdempotencyScope: async (toolId) => {
2511
- const metadata = await getRuntimeToolMetadata(toolId);
2512
- return metadata.retry?.idempotencyScope === 'invocation'
2513
- ? 'invocation'
2514
- : null;
2515
- },
2516
- getToolReusableResultSupport: async (toolId) => {
2517
- const support = (await getRuntimeToolMetadata(toolId)).reusableResult;
2518
- if (!support) {
2519
- throw new Error(
2520
- `Tool ${toolId} has no reusable-result capability metadata. Deploy the runtime metadata reader before admitting explicit-freshness artifacts.`,
2521
- );
2522
- }
2523
- return support;
2524
- },
2525
- getToolRetryPolicy: async (toolId) => {
2526
- const metadata = await getRuntimeToolMetadata(toolId);
2527
- return {
2528
- retrySafeTransientHttp: metadata.retry?.retrySafeTransientHttp === true,
2529
- requiresExecutionFence: metadata.retry?.requiresExecutionFence === true,
2530
- };
2531
- },
2532
- getToolActionCacheVersion: async (toolId) => {
2533
- const metadata = await getRuntimeToolMetadata(toolId);
2534
- const version = metadata.actionCacheVersion.trim();
2535
- if (!version) {
2536
- throw new Error(
2537
- `Runtime metadata for ${toolId} is missing actionCacheVersion.`,
2538
- );
2539
- }
2540
- return version;
2541
- },
2542
- getToolActionCacheReadVersions: async (toolId) => {
2543
- const metadata = await getRuntimeToolMetadata(toolId);
2544
- return [
2545
- ...new Set(
2546
- (metadata.actionCacheReadVersions ?? [])
2547
- .map((version) => String(version || '').trim())
2548
- .filter(Boolean),
2549
- ),
2550
- ];
2551
- },
2466
+ ...createRuntimeToolHooks({
2467
+ getRuntimeToolMetadata,
2468
+ createEventWaitHandler: (toolId) =>
2469
+ createRuntimeEventWaitHandler(config, toolId),
2470
+ }),
2552
2471
  getToolAuthScopeDigest: getRuntimeToolAuthScopeDigest,
2553
2472
  getToolAsyncExecutionRecovery:
2554
2473
  getRuntimeToolAuthScopeDigest.asyncExecutionRecovery,
2555
2474
  getToolRateScope: getRuntimeToolAuthScopeDigest.rateScope,
2556
2475
  invalidateToolAuthScopeDigest: (toolId) =>
2557
2476
  getRuntimeToolAuthScopeDigest.invalidate?.(toolId),
2558
- getToolTargetGetters: async (toolId, output) => {
2559
- const metadata = await getRuntimeToolMetadata(toolId);
2560
- return metadata.targetGetters?.[output] ?? [];
2561
- },
2562
- getToolResultMetadata: async (toolId) => {
2563
- const metadata = await getRuntimeToolMetadata(toolId);
2564
- return {
2565
- toolId,
2566
- extractors: metadata.extractors ?? {},
2567
- targetGetters: metadata.targetGetters ?? {},
2568
- listExtractorPaths: metadata.listExtractorPaths ?? [],
2569
- listExtractorAliases: metadata.listExtractorAliases,
2570
- listIdentityGetters: metadata.listIdentityGetters ?? {},
2571
- };
2572
- },
2573
- getToolAsyncOperation: async (toolId) =>
2574
- (await getRuntimeToolMetadata(toolId)).asyncOperation ?? null,
2575
- getToolAsyncOperationPoll: async (toolId) =>
2576
- (await getRuntimeToolMetadata(toolId)).asyncOperationPoll ?? null,
2577
- getBatchOperationStrategy: (toolId) => getPlayRuntimeBatchStrategy(toolId),
2578
- getIntegrationEventWaitHandler: async (toolId) => {
2579
- if (isIntegrationEventWaitToolId(toolId)) {
2580
- return createRuntimeEventWaitHandler(config, toolId);
2581
- }
2582
- const metadata = await getRuntimeToolMetadata(toolId);
2583
- return metadata.eventWait
2584
- ? createRuntimeEventWaitHandler(config, toolId)
2585
- : null;
2586
- },
2587
2477
  onRowUpdate: (() => {
2588
2478
  let rowUpdateCount = 0;
2589
2479
  let lastEmitAt = 0;
@@ -0,0 +1,155 @@
1
+ import type { ContextOptions, IntegrationEventWaitHandler } from '../ctx-types';
2
+ import { isIntegrationEventWaitToolId } from '../event-wait-tools';
3
+ import type { PlayQueueHint } from '../governor/rate-state-backend';
4
+ import { getPlayRuntimeBatchStrategy } from '../play-runtime-batching-registry';
5
+ import type { ToolResultMetadataInput } from '../tool-result';
6
+
7
+ /** The app's runtime tool metadata as the runner receives it over HTTP. */
8
+ export type RuntimeToolMetadata = {
9
+ reusableResult?: { supported: boolean; unavailableReason: string | null };
10
+ provider?: string | null;
11
+ operation?: string | null;
12
+ extractors?: ToolResultMetadataInput['extractors'];
13
+ actionCacheVersion: string;
14
+ actionCacheReadVersions?: string[];
15
+ queueHints?: unknown;
16
+ retry?: {
17
+ idempotencyScope?: 'invocation';
18
+ retrySafeTransientHttp?: boolean;
19
+ requiresExecutionFence?: boolean;
20
+ };
21
+ targetGetters?: Record<string, string[]>;
22
+ listExtractorPaths?: string[];
23
+ listExtractorAliases?: ToolResultMetadataInput['listExtractorAliases'];
24
+ listIdentityGetters?: Record<string, string[]>;
25
+ eventWait?: boolean;
26
+ asyncOperation?:
27
+ | import('../async-operation').AsyncOperationContractWire
28
+ | null;
29
+ asyncOperationPoll?:
30
+ | import('../async-operation').AsyncOperationPollExecution
31
+ | null;
32
+ };
33
+
34
+ export type RuntimeToolHooks = Required<
35
+ Pick<
36
+ ContextOptions,
37
+ | 'getToolQueueHints'
38
+ | 'getToolProvider'
39
+ | 'getToolOperation'
40
+ | 'getToolIdempotencyScope'
41
+ | 'getToolReusableResultSupport'
42
+ | 'getToolRetryPolicy'
43
+ | 'getToolActionCacheVersion'
44
+ | 'getToolActionCacheReadVersions'
45
+ | 'getToolTargetGetters'
46
+ | 'getToolResultMetadata'
47
+ | 'getToolAsyncOperation'
48
+ | 'getToolAsyncOperationPoll'
49
+ | 'getBatchOperationStrategy'
50
+ | 'getIntegrationEventWaitHandler'
51
+ >
52
+ >;
53
+
54
+ /**
55
+ * Every tool-policy decision the Play runner makes from tool metadata. The
56
+ * runner and in-process tests both build their context from this, so a test
57
+ * swaps only where metadata and event-wait effects come from, never a policy.
58
+ */
59
+ export function createRuntimeToolHooks(input: {
60
+ getRuntimeToolMetadata: (toolId: string) => Promise<RuntimeToolMetadata>;
61
+ createEventWaitHandler: (
62
+ toolId: string,
63
+ ) => IntegrationEventWaitHandler | null;
64
+ }): RuntimeToolHooks {
65
+ const { getRuntimeToolMetadata, createEventWaitHandler } = input;
66
+ return {
67
+ getToolQueueHints: async (toolId) => {
68
+ const metadata = await getRuntimeToolMetadata(toolId);
69
+ return Array.isArray(metadata.queueHints)
70
+ ? (metadata.queueHints as PlayQueueHint[])
71
+ : [];
72
+ },
73
+ getToolProvider: async (toolId) => {
74
+ const provider = (await getRuntimeToolMetadata(toolId)).provider;
75
+ return typeof provider === 'string' && provider.trim()
76
+ ? provider.trim()
77
+ : null;
78
+ },
79
+ getToolOperation: async (toolId) => {
80
+ const operation = (await getRuntimeToolMetadata(toolId)).operation;
81
+ return typeof operation === 'string' && operation.trim()
82
+ ? operation.trim()
83
+ : null;
84
+ },
85
+ getToolIdempotencyScope: async (toolId) => {
86
+ const metadata = await getRuntimeToolMetadata(toolId);
87
+ return metadata.retry?.idempotencyScope === 'invocation'
88
+ ? 'invocation'
89
+ : null;
90
+ },
91
+ getToolReusableResultSupport: async (toolId) => {
92
+ const support = (await getRuntimeToolMetadata(toolId)).reusableResult;
93
+ if (!support) {
94
+ throw new Error(
95
+ `Tool ${toolId} has no reusable-result capability metadata. Deploy the runtime metadata reader before admitting explicit-freshness artifacts.`,
96
+ );
97
+ }
98
+ return support;
99
+ },
100
+ getToolRetryPolicy: async (toolId) => {
101
+ const metadata = await getRuntimeToolMetadata(toolId);
102
+ return {
103
+ retrySafeTransientHttp: metadata.retry?.retrySafeTransientHttp === true,
104
+ requiresExecutionFence: metadata.retry?.requiresExecutionFence === true,
105
+ };
106
+ },
107
+ getToolActionCacheVersion: async (toolId) => {
108
+ const metadata = await getRuntimeToolMetadata(toolId);
109
+ const version = metadata.actionCacheVersion.trim();
110
+ if (!version) {
111
+ throw new Error(
112
+ `Runtime metadata for ${toolId} is missing actionCacheVersion.`,
113
+ );
114
+ }
115
+ return version;
116
+ },
117
+ getToolActionCacheReadVersions: async (toolId) => {
118
+ const metadata = await getRuntimeToolMetadata(toolId);
119
+ return [
120
+ ...new Set(
121
+ (metadata.actionCacheReadVersions ?? [])
122
+ .map((version) => String(version || '').trim())
123
+ .filter(Boolean),
124
+ ),
125
+ ];
126
+ },
127
+ getToolTargetGetters: async (toolId, output) => {
128
+ const metadata = await getRuntimeToolMetadata(toolId);
129
+ return metadata.targetGetters?.[output] ?? [];
130
+ },
131
+ getToolResultMetadata: async (toolId) => {
132
+ const metadata = await getRuntimeToolMetadata(toolId);
133
+ return {
134
+ toolId,
135
+ extractors: metadata.extractors ?? {},
136
+ targetGetters: metadata.targetGetters ?? {},
137
+ listExtractorPaths: metadata.listExtractorPaths ?? [],
138
+ listExtractorAliases: metadata.listExtractorAliases,
139
+ listIdentityGetters: metadata.listIdentityGetters ?? {},
140
+ };
141
+ },
142
+ getToolAsyncOperation: async (toolId) =>
143
+ (await getRuntimeToolMetadata(toolId)).asyncOperation ?? null,
144
+ getToolAsyncOperationPoll: async (toolId) =>
145
+ (await getRuntimeToolMetadata(toolId)).asyncOperationPoll ?? null,
146
+ getBatchOperationStrategy: (toolId) => getPlayRuntimeBatchStrategy(toolId),
147
+ getIntegrationEventWaitHandler: async (toolId) => {
148
+ if (isIntegrationEventWaitToolId(toolId)) {
149
+ return createEventWaitHandler(toolId);
150
+ }
151
+ const metadata = await getRuntimeToolMetadata(toolId);
152
+ return metadata.eventWait ? createEventWaitHandler(toolId) : null;
153
+ },
154
+ };
155
+ }
@@ -25,6 +25,7 @@ export type SlackChannelKey =
25
25
  | 'python_cli_session'
26
26
  | 'monitor_activity'
27
27
  | 'monitor_error_activity'
28
+ | 'monitor_cron_failures'
28
29
  | 'plain_thread_activity'
29
30
  | 'inbound_leads';
30
31
 
@@ -85,7 +86,10 @@ const SLACK_MONITOR_ERROR_ACTIVITY_CHANNEL_ID_ENV =
85
86
  const DEFAULT_PROVIDER_ALERTS_CHANNEL_ID = 'C0B4QRC8NEM';
86
87
  const DEFAULT_USER_SIGNUP_CHANNEL_ID = 'C0AGWVD2TNK';
87
88
  const DEFAULT_MONITOR_ACTIVITY_CHANNEL_ID = 'C0BDRFY7NE7';
88
- const DEFAULT_MONITOR_ERROR_ACTIVITY_CHANNEL_ID = 'C0BDGCEMLBZ';
89
+ // #deepline-monitor-error-activity. Replaces the archived C0BDGCEMLBZ (KAR-49).
90
+ const DEFAULT_MONITOR_ERROR_ACTIVITY_CHANNEL_ID = 'C0CD0JQTU20';
91
+ // #deepline-monitor-cron-failures: failures of scheduled jobs owned by monitors.
92
+ export const MONITOR_CRON_FAILURES_SLACK_CHANNEL_ID = 'C0CCZCKETEV';
89
93
 
90
94
  export const SDK_CLI_ACTIVITY_SLACK_CHANNEL_ID = 'C0B7D7DB40Y';
91
95
  export const SDK_CLI_ERRORS_SLACK_CHANNEL_ID = 'C0B6JUJ9QJW';
@@ -124,6 +128,7 @@ const CHANNEL_ALERT_LEVEL: Record<
124
128
  python_cli_session: 'info',
125
129
  monitor_activity: 'info',
126
130
  monitor_error_activity: 'error',
131
+ monitor_cron_failures: 'error',
127
132
  plain_thread_activity: 'info',
128
133
  inbound_leads: 'info',
129
134
  };
@@ -144,6 +149,8 @@ function getSlackChannelEnvironmentRequirement(
144
149
  case 'user_signup':
145
150
  case 'sdk_cli_activity':
146
151
  case 'sdk_cli_errors':
152
+ // Vercel crons run only in production.
153
+ case 'monitor_cron_failures':
147
154
  // Preview and local deploys must never page GTM with test submissions.
148
155
  case 'inbound_leads':
149
156
  return 'prod';
@@ -200,6 +207,7 @@ function isBotOnlySlackChannel(channel: SlackChannelKey): boolean {
200
207
  channel === 'user_signup' ||
201
208
  channel === 'sdk_cli_activity' ||
202
209
  channel === 'sdk_cli_errors' ||
210
+ channel === 'monitor_cron_failures' ||
203
211
  channel === 'sdk_cli_feedback' ||
204
212
  channel === 'dashboard_feedback' ||
205
213
  channel === 'python_cli_feedback' ||
@@ -264,6 +272,8 @@ function resolveBotChannel(
264
272
  return { token, channelId: SDK_CLI_ACTIVITY_SLACK_CHANNEL_ID };
265
273
  case 'sdk_cli_errors':
266
274
  return { token, channelId: SDK_CLI_ERRORS_SLACK_CHANNEL_ID };
275
+ case 'monitor_cron_failures':
276
+ return { token, channelId: MONITOR_CRON_FAILURES_SLACK_CHANNEL_ID };
267
277
  case 'sdk_cli_feedback':
268
278
  return { token, channelId: SDK_CLI_FEEDBACK_SLACK_CHANNEL_ID };
269
279
  case 'dashboard_feedback':
@@ -277,6 +287,14 @@ function resolveBotChannel(
277
287
  }
278
288
  }
279
289
 
290
+ /**
291
+ * The Axiom email alert counts failed posts to these channels, so their
292
+ * failures must always produce a `Slack event send failed.` line.
293
+ */
294
+ function isMonitorSlackChannel(channel: SlackChannelKey): boolean {
295
+ return channel.startsWith('monitor_');
296
+ }
297
+
280
298
  function isSlackChannelUnavailableError(error: unknown): boolean {
281
299
  const message = error instanceof Error ? error.message : String(error);
282
300
  return (
@@ -582,12 +600,20 @@ export async function sendSlackEvent(
582
600
 
583
601
  const botChannel = resolveBotChannel(channel);
584
602
  if (botChannel) {
603
+ // Monitor cron alerts await this post, so bound it like a webhook post.
604
+ const controller = isMonitorSlackChannel(channel)
605
+ ? new AbortController()
606
+ : null;
607
+ const timeout = controller
608
+ ? setTimeout(() => controller.abort(), resolveTimeoutMs())
609
+ : null;
585
610
  try {
611
+ const botOptions = messageOptions(sanitizedNormalized);
586
612
  await postMessage(
587
613
  botChannel.token,
588
614
  botChannel.channelId,
589
615
  sanitizedNormalized.text,
590
- messageOptions(sanitizedNormalized),
616
+ controller ? { ...botOptions, signal: controller.signal } : botOptions,
591
617
  );
592
618
  logSlackEventDispatch(channel, sanitizedNormalized, 'bot');
593
619
  return;
@@ -611,6 +637,8 @@ export async function sendSlackEvent(
611
637
  await postSlackWebhook(webhookUrl, sanitizedNormalized);
612
638
  logSlackEventDispatch(channel, sanitizedNormalized, 'webhook');
613
639
  return;
640
+ } finally {
641
+ if (timeout) clearTimeout(timeout);
614
642
  }
615
643
  }
616
644
 
@@ -631,7 +659,8 @@ export async function sendSlackEventSafe(
631
659
  return true;
632
660
  } catch (error) {
633
661
  const message = error instanceof Error ? error.message : String(error);
634
- if (message.startsWith('Missing SLACK_')) return false;
662
+ if (message.startsWith('Missing SLACK_') && !isMonitorSlackChannel(channel))
663
+ return false;
635
664
  console.warn('Slack event send failed.', {
636
665
  channel,
637
666
  source: payload.source,
@@ -13,6 +13,7 @@ export type SlackMessageOptions = {
13
13
  unfurlLinks?: boolean;
14
14
  unfurlMedia?: boolean;
15
15
  username?: string;
16
+ signal?: AbortSignal;
16
17
  };
17
18
 
18
19
  async function postSlackMessage(
@@ -29,6 +30,7 @@ async function postSlackMessage(
29
30
  : undefined;
30
31
  const res = await fetch('https://slack.com/api/chat.postMessage', {
31
32
  method: 'POST',
33
+ signal: options?.signal,
32
34
  headers: {
33
35
  Authorization: `Bearer ${token}`,
34
36
  'Content-Type': 'application/json',
@@ -13,13 +13,13 @@ import {
13
13
  reportSetupCompleted,
14
14
  resolvePendingAuthClaimForResume,
15
15
  statusBannerLine
16
- } from "./chunk-WB7HZOGR.mjs";
17
- import "./chunk-KHR6V4CE.mjs";
18
- import "./chunk-ISUKMN5B.mjs";
16
+ } from "./chunk-LQTNFYOL.mjs";
17
+ import "./chunk-HHJYD3PC.mjs";
18
+ import "./chunk-DOWK7NOY.mjs";
19
19
  import "./chunk-MAMVGOSC.mjs";
20
20
  import "./chunk-DLIRSHV6.mjs";
21
21
  import "./chunk-E4IXDAZI.mjs";
22
- import "./chunk-6P3SCJZA.mjs";
22
+ import "./chunk-R4KGIU4P.mjs";
23
23
  import "./chunk-WZYKRD7T.mjs";
24
24
  import "./chunk-KRD6H754.mjs";
25
25
  export {
@@ -0,0 +1,40 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+ var _chunk2CM22MQIjs = require('./chunk-2CM22MQI.js');
17
+ require('./chunk-X3GM2FO2.js');
18
+ require('./chunk-R2DKLSIE.js');
19
+ require('./chunk-NPBJI7RE.js');
20
+ require('./chunk-XNYIZMNQ.js');
21
+ require('./chunk-PPDBRJRW.js');
22
+ require('./chunk-CF4T46L6.js');
23
+ require('./chunk-OG64RBAC.js');
24
+ require('./chunk-COGTRVWW.js');
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+ exports.EXIT_WORKSPACE_MISMATCH = _chunk2CM22MQIjs.EXIT_WORKSPACE_MISMATCH; exports.authStatusLine = _chunk2CM22MQIjs.authStatusLine; exports.clearPendingClaim = _chunk2CM22MQIjs.clearPendingClaim; exports.clearSatisfiedPendingClaim = _chunk2CM22MQIjs.clearSatisfiedPendingClaim; exports.handleRegister = _chunk2CM22MQIjs.handleRegister; exports.handleStatus = _chunk2CM22MQIjs.handleStatus; exports.handleWait = _chunk2CM22MQIjs.handleWait; exports.httpJson = _chunk2CM22MQIjs.httpJson; exports.readPendingAuthClaim = _chunk2CM22MQIjs.readPendingAuthClaim; exports.readStatusBanner = _chunk2CM22MQIjs.readStatusBanner; exports.registerAuthCommands = _chunk2CM22MQIjs.registerAuthCommands; exports.reportSetupCompleted = _chunk2CM22MQIjs.reportSetupCompleted; exports.resolvePendingAuthClaimForResume = _chunk2CM22MQIjs.resolvePendingAuthClaimForResume; exports.statusBannerLine = _chunk2CM22MQIjs.statusBannerLine;