deepline 0.3.311 → 0.3.313

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 (114) hide show
  1. package/dist/bundling-sources/sdk/src/play.ts +7 -1
  2. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  3. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +159 -61
  4. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +11 -6
  5. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +23 -1
  6. package/dist/bundling-sources/shared_libs/plays/core.ts +7 -1
  7. package/dist/bundling-sources/shared_libs/plays/settings-contract.ts +29 -3
  8. package/dist/cli/auth-56NYRTQK.js +31 -0
  9. package/dist/cli/{auth-2A7OYHFJ.mjs → auth-LNA6WCQA.mjs} +4 -4
  10. package/dist/cli/{billing-P7GIAP3U.mjs → billing-D37QNIE3.mjs} +5 -5
  11. package/dist/cli/{billing-2PQ4VDRR.js → billing-GJJUUCOZ.js} +71 -71
  12. package/dist/cli/{chunk-DUEHAEPM.mjs → chunk-3ZC4PHJ6.mjs} +1 -1
  13. package/dist/cli/{chunk-LRI2FQF2.js → chunk-4UXNEC5C.js} +26 -26
  14. package/dist/cli/{tools-WN7T25LW.js → chunk-6G7ZYA6E.js} +231 -87
  15. package/dist/cli/chunk-76VKBPOC.mjs +406 -0
  16. package/dist/cli/{chunk-HOCPOTDX.mjs → chunk-BLMO3JMZ.mjs} +2 -2
  17. package/dist/cli/{play-SOG5Q2GE.mjs → chunk-BS4GAUMH.mjs} +166 -90
  18. package/dist/cli/{chunk-SPUTBCUR.mjs → chunk-CSH4PKPJ.mjs} +1 -1
  19. package/dist/cli/{chunk-Q3LYQTSO.mjs → chunk-DOI3UBGV.mjs} +1 -1
  20. package/dist/cli/{chunk-GCX3343L.js → chunk-FJUIXWZF.js} +48 -48
  21. package/dist/cli/{chunk-RNJ74JDH.js → chunk-FSZTWQFK.js} +4 -4
  22. package/dist/cli/{chunk-XDUQQTIS.js → chunk-GQXEHX5X.js} +6 -6
  23. package/dist/cli/{chunk-SGTSJCOA.mjs → chunk-HM2TSTCT.mjs} +1 -1
  24. package/dist/cli/{chunk-FYPZ43Y4.js → chunk-HY2SSFRI.js} +1 -1
  25. package/dist/cli/{chunk-7TT4LZIR.js → chunk-I4FHFGMA.js} +60 -60
  26. package/dist/cli/{chunk-W7INSYJS.js → chunk-IALPYID6.js} +3 -3
  27. package/dist/cli/{chunk-VODJJUKE.js → chunk-JG2ZTDZV.js} +15 -15
  28. package/dist/cli/{chunk-5IBHISKA.js → chunk-KNGTN7NE.js} +6 -6
  29. package/dist/cli/{chunk-QO3GH6UP.js → chunk-MHETTUWR.js} +2 -2
  30. package/dist/cli/{chunk-TBX7AA7Y.mjs → chunk-N2DXWS6W.mjs} +2 -2
  31. package/dist/cli/{chunk-WB6Z2Q44.mjs → chunk-N5V2SBB2.mjs} +19 -1
  32. package/dist/cli/{chunk-ZDO3TO3M.js → chunk-OJ243ZA5.js} +19 -1
  33. package/dist/cli/{chunk-QJA6SJHA.mjs → chunk-OTA5C7DD.mjs} +1 -1
  34. package/dist/cli/{play-BCMGFYTF.js → chunk-P4N7233B.js} +342 -266
  35. package/dist/cli/{chunk-4WFNAWVO.mjs → chunk-QCXDLOGA.mjs} +2 -2
  36. package/dist/cli/{chunk-IB7OGQGH.js → chunk-QQMKX7JW.js} +29 -9
  37. package/dist/cli/{chunk-ICZB4GTY.mjs → chunk-RDUS2EDE.mjs} +23 -3
  38. package/dist/cli/{tools-G4HGCHNQ.mjs → chunk-UQBMMN4U.mjs} +180 -36
  39. package/dist/cli/{chunk-4NIQA6S7.mjs → chunk-US4S3E4Y.mjs} +1 -1
  40. package/dist/cli/{chunk-QYL5ZV4U.js → chunk-VSAYSOVC.js} +6 -6
  41. package/dist/cli/{chunk-G3QP2WYR.mjs → chunk-XFEPRIW7.mjs} +4 -4
  42. package/dist/cli/chunk-XNL3SEMC.js +406 -0
  43. package/dist/cli/{chunk-5OVNL6LS.mjs → chunk-YZ5PP6TX.mjs} +1 -1
  44. package/dist/cli/{client-PNLDT2M3.mjs → client-6RVRZ4OY.mjs} +3 -3
  45. package/dist/cli/client-XUTMYJDL.js +20 -0
  46. package/dist/cli/{csv-L5DN7F45.js → csv-2UHRG67O.js} +12 -12
  47. package/dist/cli/{csv-KUAATBZQ.mjs → csv-SBOHBKEG.mjs} +3 -3
  48. package/dist/cli/{db-WZOD6NP5.mjs → db-TUFTPYEA.mjs} +4 -4
  49. package/dist/cli/{db-6NIH4KTZ.js → db-VRE7CLIQ.js} +20 -20
  50. package/dist/cli/describe-GS5A2XH4.js +236 -0
  51. package/dist/cli/describe-WCBZTSOF.mjs +236 -0
  52. package/dist/cli/{executions-TQ5WRHDS.js → executions-ENMOOKWH.js} +6 -6
  53. package/dist/cli/{executions-RDDXGT2Y.mjs → executions-UTN3O372.mjs} +4 -4
  54. package/dist/cli/{feedback-NYKLEQB6.js → feedback-BLN6SIRG.js} +6 -6
  55. package/dist/cli/{feedback-ZB4WKBSS.mjs → feedback-PXBA3AG7.mjs} +3 -3
  56. package/dist/cli/git-26D3VD2I.js +23 -0
  57. package/dist/cli/{git-E6DKAX3X.mjs → git-YDMIRW3Q.mjs} +4 -4
  58. package/dist/cli/index.js +82 -78
  59. package/dist/cli/index.mjs +45 -41
  60. package/dist/cli/{legacy-noops-26Y5NSU6.js → legacy-noops-CWTUWTDB.js} +7 -7
  61. package/dist/cli/{legacy-noops-25B7JCOZ.mjs → legacy-noops-OVG2SICQ.mjs} +4 -4
  62. package/dist/cli/{monitors-WRTHO7DT.mjs → monitors-ATIDXJA4.mjs} +7 -7
  63. package/dist/cli/{monitors-L3GFT4MG.js → monitors-T63OPBTY.js} +88 -88
  64. package/dist/cli/{org-BNCTGPWF.js → org-7RVAPMRN.js} +60 -60
  65. package/dist/cli/{org-ZTHOFVEO.mjs → org-NXKNIM3H.mjs} +4 -4
  66. package/dist/cli/play-B62OVHD4.mjs +152 -0
  67. package/dist/cli/play-WM3NZVE7.js +152 -0
  68. package/dist/cli/{providers-MLOKTD6O.js → providers-EITVEGXD.js} +7 -7
  69. package/dist/cli/{providers-SLAPPVMU.mjs → providers-U7B7JEYY.mjs} +4 -4
  70. package/dist/cli/{quickstart-IWJLGET3.js → quickstart-G64F2XRU.js} +5 -5
  71. package/dist/cli/{quickstart-LZRKZLNQ.mjs → quickstart-VSWTYNJM.mjs} +3 -3
  72. package/dist/cli/{search-RCDGSNWW.mjs → search-SKYTFKKF.mjs} +13 -5
  73. package/dist/cli/search-TXQJJQWY.js +26 -0
  74. package/dist/cli/{secrets-GDPYV6OC.mjs → secrets-4DNS7KWS.mjs} +4 -4
  75. package/dist/cli/{secrets-24KEMSCU.js → secrets-JSFHSCGM.js} +13 -13
  76. package/dist/cli/{sessions-ST5XQQZN.mjs → sessions-HB7RQ4XN.mjs} +4 -4
  77. package/dist/cli/{sessions-QOUUGVP3.js → sessions-MLTYPPCG.js} +5 -5
  78. package/dist/cli/{settings-4XWDMAA2.js → settings-3PCKNBPU.js} +74 -74
  79. package/dist/cli/{settings-V6Z775TO.mjs → settings-BT5WCU65.mjs} +7 -7
  80. package/dist/cli/{setup-QPZLP25U.mjs → setup-EIWDTMCK.mjs} +6 -6
  81. package/dist/cli/{setup-W7SPZTUL.js → setup-EYZWTDJI.js} +41 -41
  82. package/dist/cli/skills-73T4BSDE.js +28 -0
  83. package/dist/cli/{skills-QG22R2EI.mjs → skills-AQ3ORN2C.mjs} +4 -4
  84. package/dist/cli/tools-OFTB2BGP.mjs +38 -0
  85. package/dist/cli/tools-ZGSOHWBY.js +38 -0
  86. package/dist/cli/{update-5PSYBGVU.mjs → update-ERQTFSAR.mjs} +6 -6
  87. package/dist/cli/update-L4V5TF7I.js +28 -0
  88. package/dist/cli/{workflow-ZEQ46MGG.js → workflow-6D6NHUH4.js} +16 -16
  89. package/dist/cli/{workflow-EK7DV4AQ.mjs → workflow-SRLWGGBM.mjs} +6 -6
  90. package/dist/{client-DpnkGBn-.d.mts → client-CtVdLZux.d.mts} +21 -4
  91. package/dist/{client-ypdBvf1o.d.ts → client-OBwghxPC.d.ts} +21 -4
  92. package/dist/{compiler-manifest-qtTNYvBl.d.ts → compiler-manifest-wOKNiXpa.d.mts} +21 -0
  93. package/dist/{compiler-manifest-qtTNYvBl.d.mts → compiler-manifest-wOKNiXpa.d.ts} +21 -0
  94. package/dist/index.d.mts +4 -4
  95. package/dist/index.d.ts +4 -4
  96. package/dist/index.js +13 -10
  97. package/dist/index.mjs +16 -13
  98. package/dist/install-integrity.json +84 -76
  99. package/dist/plays/bundle-play-file.d.mts +2 -2
  100. package/dist/plays/bundle-play-file.d.ts +2 -2
  101. package/dist/plays/bundle-play-file.mjs +2 -2
  102. package/dist/plays/run-request.d.mts +2 -2
  103. package/dist/plays/run-request.d.ts +2 -2
  104. package/dist/release.d.mts +1 -1
  105. package/dist/release.d.ts +1 -1
  106. package/dist/release.js +2 -2
  107. package/dist/release.mjs +1 -1
  108. package/package.json +1 -1
  109. package/dist/cli/auth-66JFXP6C.js +0 -31
  110. package/dist/cli/client-ADWRS22F.js +0 -20
  111. package/dist/cli/git-E56KOYJR.js +0 -23
  112. package/dist/cli/search-6FBDSA3K.js +0 -18
  113. package/dist/cli/skills-WKOQBJY7.js +0 -28
  114. package/dist/cli/update-WKVCWGYY.js +0 -28
@@ -1748,7 +1748,13 @@ export function definePlay<TInput, TOutput extends PlayReturnObject>(
1748
1748
  : {
1749
1749
  name: nameOrConfig.id,
1750
1750
  fn: nameOrConfig.run,
1751
- bindings: nameOrConfig.bindings,
1751
+ bindings:
1752
+ nameOrConfig.displayName !== undefined
1753
+ ? {
1754
+ ...nameOrConfig.bindings,
1755
+ displayName: nameOrConfig.displayName,
1756
+ }
1757
+ : nameOrConfig.bindings,
1752
1758
  description: nameOrConfig.description,
1753
1759
  inputSchema: nameOrConfig.input.schema,
1754
1760
  billing: nameOrConfig.billing,
@@ -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.311',
203
+ version: '0.3.313',
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: {
@@ -354,8 +354,10 @@ const POSTGRES_SCHEDULER_SCHEMA_INIT_STATEMENT_TIMEOUT_MS = 60_000;
354
354
  // v60 indexes all sandbox leases by Run, including released custody history.
355
355
  // v61 adds an operator-owned new-run projection cutover and admission pin.
356
356
  // Existing namespace mode values and unpinned runs keep their behavior.
357
+ // v62 adds a deploy-owned index for per-run active outbox lease exclusion.
358
+ // Failed facts remain indexed: their live leases still fence the run.
357
359
  // This marker records deployment work, never a runtime schema floor.
358
- export const POSTGRES_SCHEDULER_SCHEMA_VERSION = 61;
360
+ export const POSTGRES_SCHEDULER_SCHEMA_VERSION = 62;
359
361
  const POSTGRES_SCHEDULER_SCHEMA_MIGRATIONS_TABLE =
360
362
  'scheduler_schema_migrations';
361
363
  export const POSTGRES_SCHEDULER_OPEN_RECOVERY_INDEX =
@@ -1986,6 +1988,15 @@ export async function hasCurrentPostgresSchedulerSchemaShape(
1986
1988
  AND index_row.indrelid = to_regclass($3)
1987
1989
  AND index_row.indisready AND index_row.indisvalid
1988
1990
  )
1991
+ AND EXISTS (
1992
+ SELECT 1 FROM pg_index AS index_row
1993
+ JOIN pg_class AS index_class ON index_class.oid = index_row.indexrelid
1994
+ JOIN pg_namespace AS index_namespace ON index_namespace.oid = index_class.relnamespace
1995
+ WHERE index_namespace.nspname = $1
1996
+ AND index_class.relname = 'runtime_scheduler_outbox_run_lease_idx'
1997
+ AND index_row.indrelid = to_regclass($3)
1998
+ AND index_row.indisready AND index_row.indisvalid
1999
+ )
1989
2000
  AND EXISTS (
1990
2001
  SELECT 1 FROM pg_index AS index_row
1991
2002
  JOIN pg_class AS index_class ON index_class.oid = index_row.indexrelid
@@ -4018,6 +4029,11 @@ export async function ensurePostgresSchedulerSchema(
4018
4029
  )
4019
4030
  WHERE delivered_at IS NULL AND ${searchLaneSql('outbox')} = true
4020
4031
  `);
4032
+ await client.query(`
4033
+ CREATE INDEX IF NOT EXISTS runtime_scheduler_outbox_run_lease_idx
4034
+ ON ${tableName('outbox', options)} (run_id, lease_expires_at)
4035
+ WHERE delivered_at IS NULL AND lease_owner IS NOT NULL
4036
+ `);
4021
4037
  await client.query(`
4022
4038
  CREATE INDEX IF NOT EXISTS runtime_scheduler_outbox_lease_health_idx
4023
4039
  ON ${tableName('outbox', options)}
@@ -4194,6 +4210,12 @@ const POSTGRES_SCHEDULER_RUNTIME_ADMIN_INDEXES = [
4194
4210
  created_at, event_id)
4195
4211
  WHERE delivered_at IS NULL AND ${searchLaneSql('outbox')} = true`,
4196
4212
  },
4213
+ {
4214
+ name: 'runtime_scheduler_outbox_run_lease_idx',
4215
+ table: 'outbox',
4216
+ definition: `(run_id, lease_expires_at)
4217
+ WHERE delivered_at IS NULL AND lease_owner IS NOT NULL`,
4218
+ },
4197
4219
  {
4198
4220
  name: 'runtime_scheduler_outbox_lease_health_idx',
4199
4221
  table: 'outbox',
@@ -17982,8 +18004,11 @@ function runLedgerPrecedesSql(
17982
18004
  predecessor: string,
17983
18005
  successor: string,
17984
18006
  inclusive = false,
18007
+ searchOnly?: boolean,
17985
18008
  ): string {
17986
- return `(${searchLaneSql(predecessor)} = ${searchLaneSql(successor)} AND ${runLedgerPositionSql(predecessor)} ${inclusive ? '<=' : '<'} ${runLedgerPositionSql(successor)})`;
18009
+ const lane =
18010
+ searchOnly === undefined ? searchLaneSql(successor) : String(searchOnly);
18011
+ return `(${searchLaneSql(predecessor)} = ${lane} AND ${runLedgerPositionSql(predecessor)} ${inclusive ? '<=' : '<'} ${runLedgerPositionSql(successor)})`;
17987
18012
  }
17988
18013
 
17989
18014
  function searchLaneSql(alias: string): string {
@@ -18014,30 +18039,28 @@ function runLedgerTerminalPendingSql(
18014
18039
  )`;
18015
18040
  }
18016
18041
 
18017
- function runLedgerWaitingRunsSql(
18042
+ function runLedgerWaitingPendingSql(
18043
+ alias: string,
18018
18044
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
18019
- runIdParameter?: string,
18045
+ runIdSql = `${alias}.run_id`,
18020
18046
  ): string {
18021
- return `SELECT DISTINCT waiting_event.run_id
18047
+ return `EXISTS (SELECT 1
18022
18048
  FROM ${tableName('outbox', options)} AS waiting_event
18023
- WHERE waiting_event.event_type = ${RUN_EVENT_KIND_SQL_LITERAL.runWaiting}
18049
+ WHERE waiting_event.run_id = ${runIdSql}
18050
+ AND waiting_event.event_type = ${RUN_EVENT_KIND_SQL_LITERAL.runWaiting}
18024
18051
  -- Detached runner parks are transport-only and project no public wait.
18025
18052
  AND waiting_event.payload_json->>'kind' IS DISTINCT FROM 'detached_runner'
18026
18053
  AND waiting_event.delivered_at IS NULL
18027
18054
  AND waiting_event.failed_at IS NULL
18028
- AND coalesce(waiting_event.next_attempt_at, waiting_event.created_at) <= now()
18029
- ${runIdParameter ? `AND (${runIdParameter}::text IS NULL OR waiting_event.run_id = ${runIdParameter}::text)` : ''}`;
18030
- }
18031
-
18032
- function runLedgerWaitingPendingSql(alias: string): string {
18033
- return `(${alias}.run_id IN (SELECT run_id FROM waiting_runs))`;
18055
+ AND coalesce(waiting_event.next_attempt_at, waiting_event.created_at) <= now())`;
18034
18056
  }
18035
18057
 
18036
18058
  function runLedgerUrgentLifecyclePendingSql(
18037
18059
  alias: string,
18038
18060
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
18061
+ runIdSql?: string,
18039
18062
  ): string {
18040
- return `(${runLedgerTerminalPendingSql(alias, options)} OR ${runLedgerWaitingPendingSql(alias)})`;
18063
+ return `(${runLedgerTerminalPendingSql(alias, options)} OR ${runLedgerWaitingPendingSql(alias, options, runIdSql)})`;
18041
18064
  }
18042
18065
 
18043
18066
  /**
@@ -18060,6 +18083,8 @@ export async function readPendingPostgresSchedulerOutboxEvents(
18060
18083
  extendOwnedHead?: string;
18061
18084
  /** The dispatcher owns a short, exclusive claim transaction. */
18062
18085
  inTransaction?: boolean;
18086
+ /** The owner rolls back and restarts the whole claim on the old-schema adapter. */
18087
+ restartTransactionOnMissingMarker?: boolean;
18063
18088
  } = {},
18064
18089
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
18065
18090
  ): Promise<PostgresSchedulerOutboxEvent[]> {
@@ -18100,11 +18125,23 @@ async function claimPostgresSchedulerOutboxPrefix(
18100
18125
  `${alias}.delivered_at IS NULL${
18101
18126
  acknowledgement ? ` AND NOT (${alias}.event_id = ANY($5::text[]))` : ''
18102
18127
  }`;
18128
+ // The dispatcher already chose this run. Seek its oldest pending fact
18129
+ // before eligibility checks; comparing every candidate with every earlier
18130
+ // fact makes a single run's burst quadratic. Failed/delayed heads must
18131
+ // remain in this seek so they still block their causal successors.
18132
+ const seekRunHead = runId !== null && !input.extendOwnedHead;
18103
18133
  const claim = async (supportsTerminalBatchMarker: boolean) => {
18104
18134
  const urgentLifecyclePendingSql = (alias: string) =>
18105
18135
  input.searchOnly === true
18106
18136
  ? runLedgerTerminalPendingSql(alias, options)
18107
- : runLedgerUrgentLifecyclePendingSql(alias, options);
18137
+ : runLedgerUrgentLifecyclePendingSql(
18138
+ alias,
18139
+ options,
18140
+ // Every claimed row belongs to the selected run. An independent
18141
+ // wait probe evaluates once, instead of scanning its backlog for
18142
+ // each row whose delivery batch is being frozen.
18143
+ runId !== null ? '$2::text' : undefined,
18144
+ );
18108
18145
  // Search is locally indexed and acknowledged; it has no remote ingress
18109
18146
  // receipt to freeze. Preserve causal ordering/due/lease checks, but admit
18110
18147
  // its full prefix instead of inheriting lifecycle's singleton transport.
@@ -18132,11 +18169,17 @@ async function claimPostgresSchedulerOutboxPrefix(
18132
18169
  ),`
18133
18170
  : ''
18134
18171
  } ${
18135
- input.searchOnly === true
18136
- ? ''
18137
- : `waiting_runs AS MATERIALIZED (
18138
- ${runLedgerWaitingRunsSql(options, '$2')}
18139
- ), `
18172
+ seekRunHead
18173
+ ? `pending_head AS MATERIALIZED (
18174
+ SELECT pending.event_id
18175
+ FROM ${tableName('outbox', options)} AS pending
18176
+ WHERE pending.run_id = $2
18177
+ AND ${pending('pending')}
18178
+ AND ${searchLaneSql('pending')} = ${input.searchOnly === true ? 'true' : 'false'}
18179
+ ORDER BY ${runLedgerOrderSql('pending')}
18180
+ LIMIT 1
18181
+ ),`
18182
+ : ''
18140
18183
  }${
18141
18184
  input.extendOwnedHead
18142
18185
  ? `owned_run AS MATERIALIZED (
@@ -18152,6 +18195,7 @@ async function claimPostgresSchedulerOutboxPrefix(
18152
18195
  SELECT candidate.event_id, candidate.run_id, candidate.event_type,
18153
18196
  ${supportsTerminalBatchMarker ? 'candidate.delivery_batch_started_at' : 'NULL::timestamptz'} AS delivery_batch_started_at
18154
18197
  FROM ${tableName('outbox', options)} AS candidate
18198
+ ${seekRunHead ? 'JOIN pending_head ON pending_head.event_id = candidate.event_id' : ''}
18155
18199
  ${input.extendOwnedHead ? 'JOIN owned_run ON owned_run.run_id = candidate.run_id' : ''}
18156
18200
  WHERE ${pending('candidate')}
18157
18201
  AND ${searchLaneSql('candidate')} = ${input.searchOnly === true ? 'true' : 'false'}
@@ -18187,7 +18231,7 @@ async function claimPostgresSchedulerOutboxPrefix(
18187
18231
  SELECT 1 FROM ${tableName('outbox', options)} AS active
18188
18232
  WHERE active.run_id = candidate.run_id
18189
18233
  AND ${pending('active')}
18190
- AND ${searchLaneSql('active')} = ${searchLaneSql('candidate')}
18234
+ AND ${searchLaneSql('active')} = ${input.searchOnly === true ? 'true' : 'false'}
18191
18235
  AND active.lease_owner IS NOT NULL
18192
18236
  AND active.lease_expires_at > now()
18193
18237
  AND active.lease_owner IS DISTINCT FROM $3::text
@@ -18195,13 +18239,17 @@ async function claimPostgresSchedulerOutboxPrefix(
18195
18239
  -- Competing projectors acquire one run through its oldest event.
18196
18240
  -- Locking this head prevents another projector from splitting the
18197
18241
  -- run while still letting it skip to unrelated runs.
18198
- AND NOT EXISTS (
18242
+ ${
18243
+ seekRunHead
18244
+ ? ''
18245
+ : `AND NOT EXISTS (
18199
18246
  SELECT 1
18200
18247
  FROM ${tableName('outbox', options)} AS predecessor
18201
18248
  WHERE predecessor.run_id = candidate.run_id
18202
18249
  AND ${pending('predecessor')}
18203
- AND ${runLedgerPrecedesSql('predecessor', 'candidate')}
18204
- )
18250
+ AND ${runLedgerPrecedesSql('predecessor', 'candidate', false, input.searchOnly === true)}
18251
+ )`
18252
+ }
18205
18253
  -- A new durable admission has no causal dependency on unrelated
18206
18254
  -- terminal facts. Project it first so a lifecycle backlog cannot make
18207
18255
  -- an accepted run invisible; per-run predecessor checks above retain
@@ -18210,16 +18258,20 @@ async function claimPostgresSchedulerOutboxPrefix(
18210
18258
  -- predecessors. Giving priority only to a terminal head leaves a
18211
18259
  -- finished run's earlier progress behind unrelated progress. The
18212
18260
  -- predecessor checks still forbid skipping any fact within the run.
18213
- ORDER BY
18261
+ ${
18262
+ runId !== null
18263
+ ? ''
18264
+ : `ORDER BY
18214
18265
  CASE
18215
18266
  WHEN candidate.event_type = ${RUN_EVENT_KIND_SQL_LITERAL.runCreated} THEN 0
18216
18267
  WHEN ${runLedgerTerminalPendingSql('candidate', options)} THEN 1
18217
- ${input.searchOnly === true ? '' : `WHEN ${runLedgerWaitingPendingSql('candidate')} THEN 2`}
18268
+ ${input.searchOnly === true ? '' : `WHEN ${runLedgerWaitingPendingSql('candidate', options)} THEN 2`}
18218
18269
  ELSE 3
18219
18270
  END,
18220
18271
  coalesce(candidate.next_attempt_at, candidate.created_at),
18221
18272
  candidate.created_at,
18222
- candidate.event_id
18273
+ candidate.event_id`
18274
+ }
18223
18275
  FOR UPDATE SKIP LOCKED
18224
18276
  LIMIT 1
18225
18277
  ), candidates AS MATERIALIZED (
@@ -18227,6 +18279,7 @@ async function claimPostgresSchedulerOutboxPrefix(
18227
18279
  FROM ${tableName('outbox', options)} AS outbox
18228
18280
  JOIN head ON head.run_id = outbox.run_id
18229
18281
  WHERE ${pending('outbox')}
18282
+ ${runId !== null ? 'AND outbox.run_id = $2::text' : ''}
18230
18283
  AND ${searchLaneSql('outbox')} = ${input.searchOnly === true ? 'true' : 'false'}
18231
18284
  AND outbox.failed_at IS NULL
18232
18285
  -- Admission is causally first and must be projected alone. A later
@@ -18278,7 +18331,7 @@ async function claimPostgresSchedulerOutboxPrefix(
18278
18331
  AND terminal_predecessor.event_type IN (
18279
18332
  ${RUN_EVENT_KIND_SQL_LITERAL.runCompleted}, ${RUN_EVENT_KIND_SQL_LITERAL.runFailed}, ${RUN_EVENT_KIND_SQL_LITERAL.runCancelled}
18280
18333
  )
18281
- AND ${runLedgerPrecedesSql('terminal_predecessor', 'outbox')}
18334
+ AND ${runLedgerPrecedesSql('terminal_predecessor', 'outbox', false, input.searchOnly === true)}
18282
18335
  )
18283
18336
  -- Claim only the contiguous due prefix. A delayed, leased, or failed
18284
18337
  -- predecessor retains ownership of everything that follows it.
@@ -18287,7 +18340,7 @@ async function claimPostgresSchedulerOutboxPrefix(
18287
18340
  FROM ${tableName('outbox', options)} AS predecessor
18288
18341
  WHERE predecessor.run_id = outbox.run_id
18289
18342
  AND ${pending('predecessor')}
18290
- AND ${runLedgerPrecedesSql('predecessor', 'outbox')}
18343
+ AND ${runLedgerPrecedesSql('predecessor', 'outbox', false, input.searchOnly === true)}
18291
18344
  AND (
18292
18345
  (predecessor.event_type = ${RUN_SCHEDULER_OUTBOX_EVENT_TYPE_SQL_LITERAL.runEvents}
18293
18346
  AND NOT (${ingressPrefixSql}))
@@ -18383,7 +18436,9 @@ async function claimPostgresSchedulerOutboxPrefix(
18383
18436
  );
18384
18437
  };
18385
18438
  let result;
18386
- if (input.inTransaction) await client.query('SAVEPOINT run_ledger_claim');
18439
+ const useSavepoint =
18440
+ input.inTransaction && !input.restartTransactionOnMissingMarker;
18441
+ if (useSavepoint) await client.query('SAVEPOINT run_ledger_claim');
18387
18442
  if (supportsOutboxDeliveryBatchMarker(options)) {
18388
18443
  try {
18389
18444
  result = await claim(true);
@@ -18393,16 +18448,16 @@ async function claimPostgresSchedulerOutboxPrefix(
18393
18448
  // The ACK caller owns the whole short transaction. Roll it back and
18394
18449
  // repeat only this missing-column adapter, avoiding two SAVEPOINT round
18395
18450
  // trips on every successful acknowledgement.
18396
- if (acknowledgement) throw error;
18397
- if (input.inTransaction)
18451
+ if (acknowledgement || input.restartTransactionOnMissingMarker)
18452
+ throw error;
18453
+ if (useSavepoint)
18398
18454
  await client.query('ROLLBACK TO SAVEPOINT run_ledger_claim');
18399
18455
  result = await claim(false);
18400
18456
  }
18401
18457
  } else {
18402
18458
  result = await claim(false);
18403
18459
  }
18404
- if (input.inTransaction)
18405
- await client.query('RELEASE SAVEPOINT run_ledger_claim');
18460
+ if (useSavepoint) await client.query('RELEASE SAVEPOINT run_ledger_claim');
18406
18461
  const events = result.rows
18407
18462
  .filter((row) => row.row_kind === 'claimed')
18408
18463
  .map((row) => {
@@ -19871,7 +19926,7 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
19871
19926
  }
19872
19927
 
19873
19928
  async function dispatchRunLedgerOutboxBatchWithScope(
19874
- withClient: RunLedgerOutboxWithClient,
19929
+ acquireClient: RunLedgerOutboxWithClient,
19875
19930
  input: RunLedgerOutboxDispatchInput & { waitForRunLock?: boolean },
19876
19931
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
19877
19932
  scope?: RunLedgerOutboxDrainScope,
@@ -19879,6 +19934,25 @@ async function dispatchRunLedgerOutboxBatchWithScope(
19879
19934
  remainingEvents: number | null = null,
19880
19935
  ): Promise<PostgresSchedulerOutboxProjectionResult> {
19881
19936
  const dispatchStartedAtMs = Date.now();
19937
+ // A committed terminal is running-work control traffic. Use the existing
19938
+ // alternating critical admission lane so its short lease operations do not
19939
+ // sit behind a full ordinary-work backlog; capacity is unchanged.
19940
+ const terminalRunId =
19941
+ scope && scope.targetEventId === `run-terminal:${scope.runId}`
19942
+ ? scope.runId
19943
+ : null;
19944
+ const withClient: RunLedgerOutboxWithClient = (operation, trace) =>
19945
+ acquireClient(
19946
+ operation,
19947
+ trace ??
19948
+ (terminalRunId
19949
+ ? {
19950
+ runId: terminalRunId,
19951
+ phase: 'run_ledger_outbox_terminal_delivery',
19952
+ priority: 'critical',
19953
+ }
19954
+ : undefined),
19955
+ );
19882
19956
  const tokenAuthority = input.tokenAuthority ?? 'local_mint';
19883
19957
  const leaseOwner =
19884
19958
  input.leaseOwner?.trim() || `run-ledger-outbox-dispatch:${randomUUID()}`;
@@ -19930,6 +20004,9 @@ async function dispatchRunLedgerOutboxBatchWithScope(
19930
20004
  await client.query('BEGIN');
19931
20005
  try {
19932
20006
  const targetRunId = input.runId?.trim() || null;
20007
+ let lockedRun: {
20008
+ rows: { run_id: string; dispatch_launch_json: unknown }[];
20009
+ } | null = null;
19933
20010
  if (input.waitForRunLock && targetRunId) {
19934
20011
  // A synchronous drain must not mistake a busy run for an empty
19935
20012
  // outbox. In particular, cancellation owns its pending prefix lease,
@@ -19945,8 +20022,14 @@ async function dispatchRunLedgerOutboxBatchWithScope(
19945
20022
  `${RUNTIME_SCHEDULER_CANCEL_STATEMENT_TIMEOUT_MS}ms`,
19946
20023
  ],
19947
20024
  );
19948
- await client.query(
19949
- `SELECT run_id FROM ${tableName('runs', options)} WHERE run_id = $1 FOR UPDATE`,
20025
+ lockedRun = await client.query<{
20026
+ run_id: string;
20027
+ dispatch_launch_json: unknown;
20028
+ }>(
20029
+ `SELECT runs.run_id,
20030
+ ${needsLaunch ? runLedgerDispatchLaunchSql('runs') : 'NULL::jsonb'} AS dispatch_launch_json
20031
+ FROM ${tableName('runs', options)} AS runs
20032
+ WHERE runs.run_id = $1 FOR UPDATE`,
19950
20033
  [targetRunId],
19951
20034
  );
19952
20035
  }
@@ -19955,17 +20038,19 @@ async function dispatchRunLedgerOutboxBatchWithScope(
19955
20038
  // nullable predicate made PostgreSQL materialize every pending run head
19956
20039
  // for each lifecycle transition. Only the two dedicated projectors may
19957
20040
  // perform the global distinct-head scan below.
19958
- const selected = targetRunId
19959
- ? await client.query<{
19960
- run_id: string;
19961
- dispatch_launch_json: unknown;
19962
- }>(
19963
- `
20041
+ const selected =
20042
+ (input.headEventId ? null : lockedRun) ??
20043
+ (targetRunId
20044
+ ? await client.query<{
20045
+ run_id: string;
20046
+ dispatch_launch_json: unknown;
20047
+ }>(
20048
+ `
19964
20049
  WITH head AS MATERIALIZED (
19965
20050
  SELECT pending.run_id,
19966
20051
  pending.event_id,
19967
20052
  pending.event_type,
19968
- pending.payload_json,
20053
+ ${needsLaunch ? 'pending.payload_json,' : ''}
19969
20054
  pending.created_at,
19970
20055
  pending.next_attempt_at,
19971
20056
  pending.failed_at
@@ -19993,24 +20078,18 @@ async function dispatchRunLedgerOutboxBatchWithScope(
19993
20078
  )
19994
20079
  FOR UPDATE OF runs SKIP LOCKED
19995
20080
  `,
19996
- [targetRunId, leaseOwner, input.headEventId?.trim() || null],
19997
- )
19998
- : await client.query<{
19999
- run_id: string;
20000
- dispatch_launch_json: unknown;
20001
- }>(`
20002
- WITH ${
20003
- input.searchOnly === true
20004
- ? ''
20005
- : `waiting_runs AS MATERIALIZED (
20006
- ${runLedgerWaitingRunsSql(options)}
20007
- ), `
20008
- }heads AS MATERIALIZED (
20081
+ [targetRunId, leaseOwner, input.headEventId?.trim() || null],
20082
+ )
20083
+ : await client.query<{
20084
+ run_id: string;
20085
+ dispatch_launch_json: unknown;
20086
+ }>(`
20087
+ WITH heads AS MATERIALIZED (
20009
20088
  SELECT DISTINCT ON (pending.run_id)
20010
20089
  pending.run_id,
20011
20090
  pending.event_id,
20012
20091
  pending.event_type,
20013
- pending.payload_json,
20092
+ ${needsLaunch ? 'pending.payload_json,' : ''}
20014
20093
  pending.created_at,
20015
20094
  pending.next_attempt_at,
20016
20095
  pending.failed_at
@@ -20042,13 +20121,13 @@ async function dispatchRunLedgerOutboxBatchWithScope(
20042
20121
  : `CASE
20043
20122
  WHEN head.event_type = ${RUN_EVENT_KIND_SQL_LITERAL.runCreated} THEN 0
20044
20123
  WHEN ${runLedgerTerminalPendingSql('head', options)} THEN 1
20045
- ${input.searchOnly === true ? '' : `WHEN ${runLedgerWaitingPendingSql('head')} THEN 2`}
20124
+ ${input.searchOnly === true ? '' : `WHEN ${runLedgerWaitingPendingSql('head', options)} THEN 2`}
20046
20125
  ELSE 3
20047
20126
  END,`
20048
20127
  }
20049
20128
  head.created_at, runs.run_id
20050
20129
  FOR UPDATE OF runs SKIP LOCKED LIMIT 1
20051
- `);
20130
+ `));
20052
20131
  const runId = selected.rows[0]?.run_id;
20053
20132
  const claimed = runId
20054
20133
  ? await readPendingPostgresSchedulerOutboxEvents(
@@ -20059,6 +20138,7 @@ async function dispatchRunLedgerOutboxBatchWithScope(
20059
20138
  leaseOwner,
20060
20139
  leaseSeconds: input.leaseSeconds ?? 120,
20061
20140
  inTransaction: true,
20141
+ restartTransactionOnMissingMarker: true,
20062
20142
  searchOnly: input.searchOnly,
20063
20143
  },
20064
20144
  options,
@@ -20083,7 +20163,16 @@ async function dispatchRunLedgerOutboxBatchWithScope(
20083
20163
  : null;
20084
20164
  const claimed = preclaimed
20085
20165
  ? { events: preclaimed, launchJson: null }
20086
- : await withClient(claimEvents);
20166
+ : await withClient(async (client) => {
20167
+ try {
20168
+ return await claimEvents(client);
20169
+ } catch (error) {
20170
+ if (!isMissingOutboxDeliveryBatchMarker(error)) throw error;
20171
+ // claimEvents rolled back. Reacquire the run fence and its fresh
20172
+ // snapshot using only the named missing-column compatibility path.
20173
+ return claimEvents(client);
20174
+ }
20175
+ });
20087
20176
  const { launchJson } = claimed;
20088
20177
  let events = claimed.events;
20089
20178
  if (events.length === 0) {
@@ -20391,7 +20480,7 @@ async function dispatchRunLedgerOutboxBatchWithScope(
20391
20480
  events.length === 1 &&
20392
20481
  first.eventType === RUN_SCHEDULER_OUTBOX_EVENT_TYPE.runEvents
20393
20482
  ) {
20394
- const widened = await withClient(async (client) => {
20483
+ const widen = async (client: PostgresSchedulerQueryClient) => {
20395
20484
  await client.query('BEGIN');
20396
20485
  try {
20397
20486
  await client.query(
@@ -20422,6 +20511,7 @@ async function dispatchRunLedgerOutboxBatchWithScope(
20422
20511
  allowIngressPrefix: true,
20423
20512
  extendOwnedHead: first.eventId,
20424
20513
  inTransaction: true,
20514
+ restartTransactionOnMissingMarker: true,
20425
20515
  },
20426
20516
  options,
20427
20517
  );
@@ -20431,6 +20521,14 @@ async function dispatchRunLedgerOutboxBatchWithScope(
20431
20521
  await client.query('ROLLBACK');
20432
20522
  throw error;
20433
20523
  }
20524
+ };
20525
+ const widened = await withClient(async (client) => {
20526
+ try {
20527
+ return await widen(client);
20528
+ } catch (error) {
20529
+ if (!isMissingOutboxDeliveryBatchMarker(error)) throw error;
20530
+ return widen(client);
20531
+ }
20434
20532
  });
20435
20533
  if (widened[0]?.eventId !== first.eventId) {
20436
20534
  throw new RunLedgerProjectionLeaseLostError(
@@ -345,6 +345,7 @@ export function startRunLedgerOutboxRecovery(
345
345
  const pendingObservability = new Set<symbol>();
346
346
  const deliveries: Delivery[] = [];
347
347
  let refill = false;
348
+ let observabilityRefill = false;
348
349
  let dispatchFailed = false;
349
350
  let completed = 0;
350
351
  let attempted = 0;
@@ -420,11 +421,7 @@ export function startRunLedgerOutboxRecovery(
420
421
  // cannot consume a notification that arrived after its query began.
421
422
  if (canProbeObservability()) {
422
423
  observabilityRequested = false;
423
- while (
424
- pendingObservability.size < observabilityParallelism &&
425
- pending.size < parallelism
426
- )
427
- launch(true);
424
+ launch(true);
428
425
  }
429
426
  if (canProbeLifecycle()) {
430
427
  requested = false;
@@ -432,6 +429,14 @@ export function startRunLedgerOutboxRecovery(
432
429
  }
433
430
  // Productive completion permits immediate use of every free slot.
434
431
  // Empty results grant no refill, so idle work cannot spin on scans.
432
+ if (observabilityRefill) {
433
+ while (
434
+ pendingObservability.size < observabilityParallelism &&
435
+ pending.size < parallelism
436
+ )
437
+ launch(true);
438
+ observabilityRefill = false;
439
+ }
435
440
  if (refill) {
436
441
  while (
437
442
  pending.size < parallelism &&
@@ -477,7 +482,7 @@ export function startRunLedgerOutboxRecovery(
477
482
  // Retryable rows own durable retry clocks. The SQL claim keeps their
478
483
  // run ordered while successful slots keep unrelated runs moving.
479
484
  if (delivery.observability) {
480
- if (result.attempted > 0) observabilityRequested = true;
485
+ observabilityRefill = result.attempted > 0;
481
486
  } else refill = result.attempted > 0;
482
487
  }
483
488
  };
@@ -717,6 +717,7 @@ export type PlayAuthoringAstSqlListenerDeclaration = {
717
717
  operations: string[];
718
718
  };
719
719
  export type PlayAuthoringAstBindings = {
720
+ displayName?: string;
720
721
  webhook?: {
721
722
  hmac?: {
722
723
  algorithm?: 'sha256';
@@ -745,6 +746,7 @@ export type PlayAuthoringAstBindings = {
745
746
  */
746
747
  export const PLAY_AUTHORING_BINDING_FIELDS = {
747
748
  root: [
749
+ 'displayName',
748
750
  'billing',
749
751
  'compatibility',
750
752
  'cron',
@@ -792,6 +794,7 @@ export type PlayAuthoringCronInput<TInput> = TInput extends readonly unknown[]
792
794
 
793
795
  /** The one public type for options accepted by definePlay. */
794
796
  export type PlayAuthoringBindings<TInput = Record<string, unknown>> = {
797
+ displayName?: string;
795
798
  description?: string;
796
799
  compatibility?: {
797
800
  toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion;
@@ -929,6 +932,7 @@ export type PlayAuthoringDefineConfig<
929
932
  > = {
930
933
  id: string;
931
934
  description?: string;
935
+ displayName?: string;
932
936
  input: PlayAuthoringInputContract<TInput>;
933
937
  run: (ctx: TContext, input: TInput) => Promise<TOutput>;
934
938
  bindings?: PlayAuthoringBindings<TInput>;
@@ -1644,6 +1648,23 @@ const SqlListenerFilterScalarSchema = Type.Union([
1644
1648
  ]);
1645
1649
 
1646
1650
  export const PLAY_AUTHORING_FIELD_REGISTRY = {
1651
+ displayName: {
1652
+ schema: Type.String(),
1653
+ fixtures: {
1654
+ valid: 'Find verified emails',
1655
+ invalid: 42,
1656
+ absent: undefined,
1657
+ unresolved: { expression: 'displayName' },
1658
+ edition1: 'Legacy play',
1659
+ },
1660
+ referenceType: 'string',
1661
+ required: false,
1662
+ resolution: 'static-required',
1663
+ issueCode: 'play_authoring_binding_invalid',
1664
+ description:
1665
+ 'Optional human-readable Play name; empty uses the derived identifier.',
1666
+ errorMessage: 'displayName must be a static string.',
1667
+ },
1647
1668
  description: {
1648
1669
  schema: Type.String({ minLength: 1 }),
1649
1670
  fixtures: {
@@ -3198,6 +3219,7 @@ export const PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
3198
3219
  '};',
3199
3220
  'export type PlayCronInput<TInput> = TInput extends readonly unknown[] ? never : TInput extends object ? TInput : never;',
3200
3221
  'export type PlayBindings<TInput = Record<string, unknown>> = {',
3222
+ ` displayName?: ${cloudReferenceType('displayName')};`,
3201
3223
  ` description?: ${cloudReferenceType('description')};`,
3202
3224
  ` compatibility?: { toolErrorSchemaVersion?: ${cloudReferenceType('compatibility.toolErrorSchemaVersion')}; toolResponseReceiptRevision?: ${cloudReferenceType('compatibility.toolResponseReceiptRevision')} };`,
3203
3225
  ` inline?: ${cloudReferenceType('inline')};`,
@@ -3276,7 +3298,7 @@ export const PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
3276
3298
  " log(message: string, options?: { level?: 'debug' | 'info' | 'warn' | 'error'; context?: Readonly<Record<string, string | number | boolean>> }): void;",
3277
3299
  ` sleep(ms: ${cloudReferenceType('ctx.sleep.ms')}): Promise<void>;`,
3278
3300
  '}',
3279
- "export type DefinePlayConfig<TInput, TOutput extends PlayReturnObject> = { id: string; description?: string; input: PlayInputContract<TInput>; run: (ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>; bindings?: PlayBindings<TInput>; billing?: PlayBindings<TInput>['billing']; runtime?: PlayBindings<TInput>['runtime']; compatibility?: PlayBindings<TInput>['compatibility'] };",
3301
+ "export type DefinePlayConfig<TInput, TOutput extends PlayReturnObject> = { id: string; displayName?: string; description?: string; input: PlayInputContract<TInput>; run: (ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>; bindings?: PlayBindings<TInput>; billing?: PlayBindings<TInput>['billing']; runtime?: PlayBindings<TInput>['runtime']; compatibility?: PlayBindings<TInput>['compatibility'] };",
3280
3302
  "export type DefinedPlay<TInput, TOutput extends PlayReturnObject> = ((ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>) & { readonly name: string; readonly __inputType?: TInput; readonly __outputType?: TOutput; readonly bindings?: PlayBindings<TInput>; readonly runtime?: PlayBindings<TInput>['runtime']; readonly compatibility?: PlayBindings<TInput>['compatibility'] };",
3281
3303
  'export type PlayHandlerInput<THandler> = THandler extends (context: DeeplinePlayRuntimeContext, input: infer TInput) => Promise<PlayReturnObject> ? TInput : never;',
3282
3304
  'export type PlayHandlerOutput<THandler> = THandler extends (context: DeeplinePlayRuntimeContext, input: unknown) => Promise<infer TOutput extends PlayReturnObject> ? TOutput : never;',
@@ -358,7 +358,13 @@ export function createDefinedPlay<
358
358
  : {
359
359
  name: nameOrConfig.id,
360
360
  fn: nameOrConfig.run,
361
- bindings: nameOrConfig.bindings,
361
+ bindings:
362
+ nameOrConfig.displayName !== undefined
363
+ ? {
364
+ ...nameOrConfig.bindings,
365
+ displayName: nameOrConfig.displayName,
366
+ }
367
+ : nameOrConfig.bindings,
362
368
  description: nameOrConfig.description,
363
369
  inputSchema: nameOrConfig.input.schema,
364
370
  billing: nameOrConfig.billing,