@opengeni/db 0.27.3 → 0.27.8

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/index.d.ts CHANGED
@@ -276,6 +276,14 @@ export declare function getHostExportConsumerStatus(db: Database, input: {
276
276
  }): Promise<HostExportConsumerStatus | null>;
277
277
  export declare function setRlsContext(db: Database, context: RlsContext): Promise<void>;
278
278
  export declare function withRlsContext<T>(db: Database, context: RlsContext, fn: (db: Database) => Promise<T>, transactionConfig?: PgTransactionConfig): Promise<T>;
279
+ /**
280
+ * Run one bounded database operation on a transaction-pinned backend.
281
+ *
282
+ * Callers that also have an application deadline should check their abort
283
+ * signal before returning from `fn`; throwing there rolls the transaction back
284
+ * even when the application deadline won a surrounding Promise race.
285
+ */
286
+ export declare function withDatabaseStatementTimeout<T>(db: Database, timeoutMs: number, fn: (db: Database) => Promise<T>): Promise<T>;
279
287
  export declare function rlsContextForWorkspace(db: Database, workspaceId: string): Promise<RlsContext>;
280
288
  export declare function withWorkspaceRls<T>(db: Database, workspaceId: string, fn: (db: Database) => Promise<T>): Promise<T>;
281
289
  /**
@@ -900,6 +908,9 @@ export type StoreIntegrationOAuthClientInput = {
900
908
  metadata?: Record<string, unknown>;
901
909
  };
902
910
  export type ReplaceIntegrationOAuthClientInput = StoreIntegrationOAuthClientInput;
911
+ export type ReplaceIntegrationOAuthClientIfCurrentInput = ReplaceIntegrationOAuthClientInput & {
912
+ expectedClientId: string;
913
+ };
903
914
  export type ConsumeOAuthStateNonceInput = {
904
915
  accountId: string;
905
916
  workspaceId: string;
@@ -1588,6 +1599,7 @@ export declare function recordConnectionUsed(db: Database, workspaceId: string,
1588
1599
  export declare function loadIntegrationOAuthClient(db: Database, settings: Settings, issuer: string): Promise<IntegrationOAuthClientForUse | null>;
1589
1600
  export declare function storeIntegrationOAuthClient(db: Database, input: StoreIntegrationOAuthClientInput): Promise<StoredIntegrationOAuthClient>;
1590
1601
  export declare function replaceIntegrationOAuthClient(db: Database, input: ReplaceIntegrationOAuthClientInput): Promise<StoredIntegrationOAuthClient>;
1602
+ export declare function replaceIntegrationOAuthClientIfCurrent(db: Database, input: ReplaceIntegrationOAuthClientIfCurrentInput): Promise<StoredIntegrationOAuthClient | null>;
1591
1603
  export declare function consumeIntegrationOAuthStateNonce(db: Database, input: ConsumeOAuthStateNonceInput): Promise<boolean>;
1592
1604
  export declare function createKnowledgeMemory(db: Database, input: CreateKnowledgeMemoryInput): Promise<KnowledgeMemory>;
1593
1605
  export declare function updateKnowledgeMemory(db: Database, workspaceId: string, memoryId: string, input: UpdateKnowledgeMemoryInput, embedder?: MemoryEmbedder): Promise<KnowledgeMemory>;
@@ -6363,6 +6375,7 @@ export type RequestSessionTurnRecoveryInput = {
6363
6375
  attemptId: string;
6364
6376
  reason: string;
6365
6377
  detail?: Record<string, unknown>;
6378
+ providerRecoveryCount?: number;
6366
6379
  fromStatuses?: SessionTurnStatus[];
6367
6380
  providerArtifactInvalidation?: {
6368
6381
  codexCredentialId: string;
@@ -6489,6 +6502,7 @@ export declare function enqueueSessionWorkflowWakeInTransaction(tx: Database, in
6489
6502
  temporalWorkflowId: string;
6490
6503
  reason: string;
6491
6504
  notBefore?: Date;
6505
+ controlRequested?: boolean;
6492
6506
  }): Promise<number>;
6493
6507
  /** Standalone transactional producer for operations not already in a DB txn. */
6494
6508
  export declare function enqueueSessionWorkflowWake(db: Database, input: {
@@ -6498,6 +6512,7 @@ export declare function enqueueSessionWorkflowWake(db: Database, input: {
6498
6512
  temporalWorkflowId: string;
6499
6513
  reason: string;
6500
6514
  notBefore?: Date;
6515
+ controlRequested?: boolean;
6501
6516
  }): Promise<number>;
6502
6517
  /**
6503
6518
  * Re-deliver already-committed session work only when admission currently
@@ -6511,6 +6526,7 @@ export declare function enqueueSessionWorkflowWakeIfRunnable(db: Database, input
6511
6526
  temporalWorkflowId: string;
6512
6527
  reason: string;
6513
6528
  notBefore?: Date;
6529
+ controlRequested?: boolean;
6514
6530
  }): Promise<number | null>;
6515
6531
  /** Claim only explicit, undelivered wake revisions; never infer work by scan. */
6516
6532
  export declare function claimPendingSessionWorkflowWakes(db: Database, limit?: number): Promise<SessionWorkflowWake[]>;
package/dist/index.js CHANGED
@@ -114,7 +114,7 @@ import {
114
114
  workspaceVariableSetVariables,
115
115
  workspaceVariableSets,
116
116
  workspaces
117
- } from "./chunk-EX7CDSGH.js";
117
+ } from "./chunk-M6EOXYHK.js";
118
118
  import {
119
119
  migrate,
120
120
  runMigrations
@@ -2319,12 +2319,13 @@ async function registerContinuableWakes(db, input) {
2319
2319
  )
2320
2320
  ), upserted as (
2321
2321
  insert into ${sessionWorkflowWakeOutbox} (
2322
- session_id, account_id, workspace_id, temporal_workflow_id, reason
2322
+ session_id, account_id, workspace_id, temporal_workflow_id, reason, control_revision
2323
2323
  )
2324
- select session_id, account_id, workspace_id, temporal_workflow_id, ${input.reason}
2324
+ select session_id, account_id, workspace_id, temporal_workflow_id, ${input.reason}, 1
2325
2325
  from eligible
2326
2326
  on conflict (session_id) do update set
2327
2327
  wake_revision = ${sessionWorkflowWakeOutbox}.wake_revision + 1,
2328
+ control_revision = ${sessionWorkflowWakeOutbox}.wake_revision + 1,
2328
2329
  temporal_workflow_id = excluded.temporal_workflow_id,
2329
2330
  reason = excluded.reason,
2330
2331
  attempts = 0,
@@ -2405,12 +2406,13 @@ async function registerCancellationWakes(db, input) {
2405
2406
  and session.id in (${sessionIds})
2406
2407
  ), upserted as (
2407
2408
  insert into ${sessionWorkflowWakeOutbox} (
2408
- session_id, account_id, workspace_id, temporal_workflow_id, reason
2409
+ session_id, account_id, workspace_id, temporal_workflow_id, reason, control_revision
2409
2410
  )
2410
- select session_id, account_id, workspace_id, temporal_workflow_id, 'session_cancelled'
2411
+ select session_id, account_id, workspace_id, temporal_workflow_id, 'session_cancelled', 1
2411
2412
  from eligible
2412
2413
  on conflict (session_id) do update set
2413
2414
  wake_revision = ${sessionWorkflowWakeOutbox}.wake_revision + 1,
2415
+ control_revision = ${sessionWorkflowWakeOutbox}.wake_revision + 1,
2414
2416
  temporal_workflow_id = excluded.temporal_workflow_id,
2415
2417
  reason = excluded.reason,
2416
2418
  attempts = 0,
@@ -2429,12 +2431,14 @@ async function registerSessionWorkflowWakeInTransaction(db, input) {
2429
2431
  workspaceId: input.workspaceId,
2430
2432
  sessionId: input.sessionId,
2431
2433
  temporalWorkflowId: input.temporalWorkflowId,
2434
+ controlRevision: input.controlRequested ? 1 : 0,
2432
2435
  reason: input.reason
2433
2436
  }).onConflictDoUpdate({
2434
2437
  target: sessionWorkflowWakeOutbox.sessionId,
2435
2438
  set: {
2436
2439
  temporalWorkflowId: input.temporalWorkflowId,
2437
2440
  wakeRevision: sql3`${sessionWorkflowWakeOutbox.wakeRevision} + 1`,
2441
+ controlRevision: input.controlRequested ? sql3`${sessionWorkflowWakeOutbox.wakeRevision} + 1` : sql3`${sessionWorkflowWakeOutbox.controlRevision}`,
2438
2442
  reason: input.reason,
2439
2443
  attempts: 0,
2440
2444
  nextAttemptAt: /* @__PURE__ */ new Date(),
@@ -4206,7 +4210,8 @@ async function steerQueuedTurnInTransaction(db, input) {
4206
4210
  workspaceId: input.workspaceId,
4207
4211
  sessionId: input.sessionId,
4208
4212
  temporalWorkflowId: session.temporalWorkflowId ?? `session-${input.sessionId}`,
4209
- reason: "queue_steer"
4213
+ reason: "queue_steer",
4214
+ controlRequested: true
4210
4215
  });
4211
4216
  const receipt = await updateSessionCommandReceiptResult(db, reserved.receipt.id, {
4212
4217
  controlRevision: resumed.revision,
@@ -4626,7 +4631,8 @@ async function submitHumanPromptInTransaction(db, input) {
4626
4631
  workspaceId: input.workspaceId,
4627
4632
  sessionId: input.sessionId,
4628
4633
  temporalWorkflowId: workflowId,
4629
- reason: input.delivery === "steer" ? "prompt_steer" : "prompt_send"
4634
+ reason: input.delivery === "steer" ? "prompt_steer" : "prompt_send",
4635
+ controlRequested: input.delivery === "steer"
4630
4636
  });
4631
4637
  await db.insert(auditEvents).values({
4632
4638
  accountId: input.accountId,
@@ -5011,7 +5017,8 @@ async function steerAgentSessionInTransaction(db, input) {
5011
5017
  workspaceId: input.workspaceId,
5012
5018
  sessionId: input.targetSessionId,
5013
5019
  temporalWorkflowId: workflowId,
5014
- reason: "agent_steer"
5020
+ reason: "agent_steer",
5021
+ controlRequested: true
5015
5022
  });
5016
5023
  await db.update(sessions).set({
5017
5024
  activeTurnId: supersession.liveCurrentTurnId,
@@ -5065,7 +5072,7 @@ async function steerAgentSessionInTransaction(db, input) {
5065
5072
  // src/session-realtime-context.ts
5066
5073
  var SESSION_REALTIME_CONTEXT_MAX_BYTES = 65536;
5067
5074
  var SESSION_REALTIME_TAIL_SOURCE = "transcript_tail_flush";
5068
- var SESSION_REALTIME_TAIL_INSTRUCTION = "The user just ended their realtime session. Here is the remaining handoff/transcript tail. You probably do not have to do anything; acknowledge the handoff unless the transcript itself asks for something.";
5075
+ var SESSION_REALTIME_TAIL_INSTRUCTION = "The user just ended the realtime voice session but remains reachable by text. Ending voice changes only the communication mode; it does not stop, pause, or complete existing work. Treat the remaining transcript tail as additional context. If work was already underway, continue it from the current state. Change or stop that work only if the user explicitly requested it. If nothing was underway and the transcript contains no unhandled request, acknowledge briefly; otherwise handle any unhandled request.";
5069
5076
  function deterministicUuid2(seed) {
5070
5077
  const bytes = createHash3("sha256").update(seed, "utf8").digest().subarray(0, 16);
5071
5078
  bytes[6] = (bytes[6] ?? 0) & 15 | 80;
@@ -14145,6 +14152,19 @@ async function withRlsContext(db, context, fn, transactionConfig) {
14145
14152
  return await fn(scoped);
14146
14153
  }, transactionConfig);
14147
14154
  }
14155
+ async function withDatabaseStatementTimeout(db, timeoutMs, fn) {
14156
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
14157
+ throw new Error("withDatabaseStatementTimeout requires a positive timeout");
14158
+ }
14159
+ const boundedTimeoutMs = Math.max(1, Math.floor(timeoutMs));
14160
+ return await db.transaction(async (tx) => {
14161
+ const scoped = tx;
14162
+ await scoped.execute(
14163
+ sql14`select set_config('statement_timeout', ${`${boundedTimeoutMs}ms`}, true)`
14164
+ );
14165
+ return await fn(scoped);
14166
+ });
14167
+ }
14148
14168
  async function rlsContextForWorkspace(db, workspaceId) {
14149
14169
  const [row] = await db.select({ accountId: workspaces.accountId }).from(workspaces).where(eq17(workspaces.id, workspaceId)).limit(1);
14150
14170
  if (!row) {
@@ -17998,6 +18018,22 @@ async function replaceIntegrationOAuthClient(db, input) {
17998
18018
  }
17999
18019
  return mapStoredIntegrationOAuthClient(row);
18000
18020
  }
18021
+ async function replaceIntegrationOAuthClientIfCurrent(db, input) {
18022
+ const [row] = await db.update(integrationOauthClients).set({
18023
+ authorizationServer: input.authorizationServer,
18024
+ clientId: input.clientId,
18025
+ clientSecretEncrypted: input.clientSecretEncrypted ?? null,
18026
+ tokenEndpointAuthMethod: input.tokenEndpointAuthMethod ?? "none",
18027
+ metadata: input.metadata ?? {},
18028
+ updatedAt: /* @__PURE__ */ new Date()
18029
+ }).where(
18030
+ and17(
18031
+ eq17(integrationOauthClients.issuer, input.issuer),
18032
+ eq17(integrationOauthClients.clientId, input.expectedClientId)
18033
+ )
18034
+ ).returning();
18035
+ return row ? mapStoredIntegrationOAuthClient(row) : null;
18036
+ }
18001
18037
  function mapStoredIntegrationOAuthClient(row) {
18002
18038
  return {
18003
18039
  id: row.id,
@@ -35612,7 +35648,8 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
35612
35648
  "pending",
35613
35649
  "delivered",
35614
35650
  "acknowledged",
35615
- "settled"
35651
+ "settled",
35652
+ "rejected_stale"
35616
35653
  ]),
35617
35654
  isNull6(sessionTurnAttempts.quiescedAt)
35618
35655
  )
@@ -36305,24 +36342,6 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
36305
36342
  turn: mapSessionTurnForExecution(internalTurn)
36306
36343
  };
36307
36344
  }
36308
- const predecessorAttemptId = queuedSteerReplacementAttemptId(queuedTurn.metadata);
36309
- if (predecessorAttemptId) {
36310
- const [predecessor] = await tx.select({ quiescedAt: sessionTurnAttempts.quiescedAt }).from(sessionTurnAttempts).where(
36311
- and17(
36312
- eq17(sessionTurnAttempts.workspaceId, workspaceId),
36313
- eq17(sessionTurnAttempts.sessionId, sessionId),
36314
- eq17(sessionTurnAttempts.id, predecessorAttemptId)
36315
- )
36316
- ).limit(1);
36317
- if (!predecessor) {
36318
- throw new SessionControlInvariantError(
36319
- `Queued Steer ${id} points to missing predecessor attempt ${predecessorAttemptId}`
36320
- );
36321
- }
36322
- if (!predecessor.quiescedAt) {
36323
- return { action: "unclaimed", reason: "control-pending" };
36324
- }
36325
- }
36326
36345
  const now = /* @__PURE__ */ new Date();
36327
36346
  const queuedDispatch = readTurnDispatchMetadata(queuedTurn?.metadata);
36328
36347
  if (queuedDispatch.kind === "malformed") {
@@ -36747,6 +36766,14 @@ async function settleSessionAttemptInterruptions(db, workspaceId, sessionId, att
36747
36766
  interruptions.map((interruption) => interruption.id)
36748
36767
  )
36749
36768
  );
36769
+ await enqueueSessionWorkflowWakeInTransaction(tx, {
36770
+ accountId: session.accountId,
36771
+ workspaceId,
36772
+ sessionId,
36773
+ temporalWorkflowId: session.temporalWorkflowId ?? `session-${sessionId}`,
36774
+ reason: "attempt_interruption_rejected_stale",
36775
+ controlRequested: true
36776
+ });
36750
36777
  return {
36751
36778
  action: effectiveControl.state === "paused" ? "paused" : "continue",
36752
36779
  events: [],
@@ -36897,6 +36924,14 @@ async function settleSessionAttemptInterruptions(db, workspaceId, sessionId, att
36897
36924
  interruptions.map((interruption) => interruption.id)
36898
36925
  )
36899
36926
  );
36927
+ await enqueueSessionWorkflowWakeInTransaction(tx, {
36928
+ accountId: session.accountId,
36929
+ workspaceId,
36930
+ sessionId,
36931
+ temporalWorkflowId: session.temporalWorkflowId ?? `session-${sessionId}`,
36932
+ reason: "attempt_interruption_settled",
36933
+ controlRequested: true
36934
+ });
36900
36935
  return {
36901
36936
  action: effectiveControl.state === "paused" ? "paused" : "continue",
36902
36937
  events: [...closedTools.events, ...eventRows.map(mapEvent2)],
@@ -36907,6 +36942,30 @@ async function settleSessionAttemptInterruptions(db, workspaceId, sessionId, att
36907
36942
  })
36908
36943
  );
36909
36944
  }
36945
+ async function nextSessionAttemptAwaitingQuiescence(db, workspaceId, sessionId) {
36946
+ const [row] = await db.select({
36947
+ attemptId: sessionTurnAttempts.id
36948
+ }).from(sessionTurnAttempts).innerJoin(
36949
+ sessionAttemptInterruptions,
36950
+ and17(
36951
+ eq17(sessionAttemptInterruptions.workspaceId, sessionTurnAttempts.workspaceId),
36952
+ eq17(sessionAttemptInterruptions.sessionId, sessionTurnAttempts.sessionId),
36953
+ eq17(sessionAttemptInterruptions.attemptId, sessionTurnAttempts.id)
36954
+ )
36955
+ ).where(
36956
+ and17(
36957
+ eq17(sessionTurnAttempts.workspaceId, workspaceId),
36958
+ eq17(sessionTurnAttempts.sessionId, sessionId),
36959
+ eq17(sessionTurnAttempts.state, "closed"),
36960
+ isNull6(sessionTurnAttempts.quiescedAt),
36961
+ inArray11(sessionAttemptInterruptions.state, ["settled", "rejected_stale"])
36962
+ )
36963
+ ).orderBy(
36964
+ asc8(sessionAttemptInterruptions.requestedAt),
36965
+ asc8(sessionAttemptInterruptions.id)
36966
+ ).limit(1);
36967
+ return row ?? null;
36968
+ }
36910
36969
  async function latestSessionAttemptInterruption(db, workspaceId, sessionId) {
36911
36970
  const [latestAttempt] = await db.select({
36912
36971
  attemptId: sessionTurnAttempts.id,
@@ -36933,6 +36992,32 @@ async function latestSessionAttemptInterruption(db, workspaceId, sessionId) {
36933
36992
  interruptionState: interruption.state
36934
36993
  } : null;
36935
36994
  }
36995
+ async function queuedSteerHasUnquiescedPredecessor(db, workspaceId, sessionId, predecessorAttemptIds) {
36996
+ if (predecessorAttemptIds.length === 0) return false;
36997
+ const [row] = await db.select({ attemptId: sessionTurnAttempts.id }).from(sessionTurnAttempts).innerJoin(
36998
+ sessionAttemptInterruptions,
36999
+ and17(
37000
+ eq17(sessionAttemptInterruptions.workspaceId, sessionTurnAttempts.workspaceId),
37001
+ eq17(sessionAttemptInterruptions.sessionId, sessionTurnAttempts.sessionId),
37002
+ eq17(sessionAttemptInterruptions.attemptId, sessionTurnAttempts.id)
37003
+ )
37004
+ ).where(
37005
+ and17(
37006
+ eq17(sessionTurnAttempts.workspaceId, workspaceId),
37007
+ eq17(sessionTurnAttempts.sessionId, sessionId),
37008
+ inArray11(sessionTurnAttempts.id, predecessorAttemptIds),
37009
+ isNull6(sessionTurnAttempts.quiescedAt),
37010
+ inArray11(sessionAttemptInterruptions.state, [
37011
+ "pending",
37012
+ "delivered",
37013
+ "acknowledged",
37014
+ "settled",
37015
+ "rejected_stale"
37016
+ ])
37017
+ )
37018
+ ).limit(1);
37019
+ return row !== void 0;
37020
+ }
36936
37021
  async function peekSessionWork(db, workspaceId, sessionId) {
36937
37022
  return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
36938
37023
  const effectiveControl = await evaluateSessionControl(scopedDb, workspaceId, sessionId, {
@@ -36961,15 +37046,15 @@ async function peekSessionWork(db, workspaceId, sessionId) {
36961
37046
  };
36962
37047
  }
36963
37048
  if (effectiveControl.state !== "active") return { kind: "idle" };
36964
- const latestInterruption = await latestSessionAttemptInterruption(
37049
+ const awaitingQuiescence = await nextSessionAttemptAwaitingQuiescence(
36965
37050
  scopedDb,
36966
37051
  workspaceId,
36967
37052
  sessionId
36968
37053
  );
36969
- if (latestInterruption && latestInterruption.quiescedAt === null && latestInterruption.interruptionState === "settled") {
37054
+ if (awaitingQuiescence) {
36970
37055
  return {
36971
37056
  kind: "cancellation-wait",
36972
- attemptId: latestInterruption.attemptId
37057
+ attemptId: awaitingQuiescence.attemptId
36973
37058
  };
36974
37059
  }
36975
37060
  const [capacityWait] = await scopedDb.select().from(codexCapacityWaiters).where(
@@ -38296,7 +38381,19 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
38296
38381
  { workspaceControl: locks.control ?? void 0 }
38297
38382
  );
38298
38383
  const turnStatus = turn?.status ?? null;
38299
- if (!locks.workspace || !turn || !attempt || turn.accountId !== session.accountId || turn.sessionId !== input.sessionId || attempt.accountId !== session.accountId || attempt.sessionId !== input.sessionId || attempt.turnId !== input.turnId || attempt.executionGeneration !== turn.executionGeneration || effectiveControl.state !== "active" || session.activeTurnId !== input.turnId || !fromStatuses.includes(turnStatus) || turn.activeAttemptId !== input.attemptId) {
38384
+ const [pendingInterruption] = attempt ? await tx.select({ id: sessionAttemptInterruptions.id }).from(sessionAttemptInterruptions).where(
38385
+ and17(
38386
+ eq17(sessionAttemptInterruptions.workspaceId, workspaceId),
38387
+ eq17(sessionAttemptInterruptions.sessionId, input.sessionId),
38388
+ eq17(sessionAttemptInterruptions.attemptId, input.attemptId),
38389
+ inArray11(sessionAttemptInterruptions.state, [
38390
+ "pending",
38391
+ "delivered",
38392
+ "acknowledged"
38393
+ ])
38394
+ )
38395
+ ).limit(1).for("update") : [];
38396
+ if (!locks.workspace || !turn || !attempt || turn.accountId !== session.accountId || turn.sessionId !== input.sessionId || attempt.accountId !== session.accountId || attempt.sessionId !== input.sessionId || attempt.turnId !== input.turnId || attempt.executionGeneration !== turn.executionGeneration || effectiveControl.state !== "active" || session.activeTurnId !== input.turnId || !fromStatuses.includes(turnStatus) || turn.activeAttemptId !== input.attemptId || pendingInterruption !== void 0) {
38300
38397
  return {
38301
38398
  action: "stale",
38302
38399
  events: [],
@@ -38305,6 +38402,9 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
38305
38402
  };
38306
38403
  }
38307
38404
  const now = /* @__PURE__ */ new Date();
38405
+ if (input.providerRecoveryCount !== void 0 && (!Number.isSafeInteger(input.providerRecoveryCount) || input.providerRecoveryCount <= 0)) {
38406
+ throw new Error("providerRecoveryCount must be a positive safe integer");
38407
+ }
38308
38408
  let providerArtifactsInvalidated = 0;
38309
38409
  if (input.providerArtifactInvalidation) {
38310
38410
  const invalidatedHistory = await tx.update(sessionHistoryItems).set({
@@ -38422,7 +38522,10 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
38422
38522
  cancelledBy: null,
38423
38523
  cancelReason: null,
38424
38524
  version: turn.version + 1,
38425
- metadata: metadataWithoutTurnDispatchAttempt(turn.metadata),
38525
+ metadata: {
38526
+ ...metadataWithoutTurnDispatchAttempt(turn.metadata),
38527
+ ...input.providerRecoveryCount !== void 0 ? { providerRecoveryCount: input.providerRecoveryCount } : {}
38528
+ },
38426
38529
  updatedAt: now
38427
38530
  }).where(
38428
38531
  and17(
@@ -38851,12 +38954,19 @@ async function getSessionQueueSnapshot(db, workspaceId, sessionId) {
38851
38954
  asc8(sessionSystemUpdates.createdAt),
38852
38955
  asc8(sessionSystemUpdates.id)
38853
38956
  );
38957
+ const items = rows.map(mapSessionTurn);
38854
38958
  const latestInterruption = await latestSessionAttemptInterruption(
38855
38959
  scopedDb,
38856
38960
  workspaceId,
38857
38961
  sessionId
38858
38962
  );
38859
- const items = rows.map(mapSessionTurn);
38963
+ const queuedSteerPredecessorIds = items.map((turn) => queuedSteerReplacementAttemptId(turn.metadata)).filter((attemptId) => attemptId !== null);
38964
+ const stoppingPreviousAttempt = latestInterruption !== null && latestInterruption.interruptionState !== "rejected_stale" && latestInterruption.quiescedAt === null || await queuedSteerHasUnquiescedPredecessor(
38965
+ scopedDb,
38966
+ workspaceId,
38967
+ sessionId,
38968
+ queuedSteerPredecessorIds
38969
+ );
38860
38970
  const nextInputBatch = selectBoundedSystemUpdateBatch(pendingInputs);
38861
38971
  const hasPendingAgentSteer = pendingInputs.some(
38862
38972
  (update) => update.kind === "agent_steer_instruction"
@@ -38866,7 +38976,7 @@ async function getSessionQueueSnapshot(db, workspaceId, sessionId) {
38866
38976
  version: session.queueVersion,
38867
38977
  effectiveControl: serializeEffectiveSessionControl(effectiveControl),
38868
38978
  activePersonalConnections,
38869
- stoppingPreviousAttempt: latestInterruption !== null && latestInterruption.interruptionState !== "rejected_stale" && latestInterruption.quiescedAt === null,
38979
+ stoppingPreviousAttempt,
38870
38980
  items,
38871
38981
  pendingInputs: pendingInputs.map((update) => {
38872
38982
  const canonical = mapSessionSystemUpdate(update);
@@ -38895,7 +39005,7 @@ function queuedSteerReplacementAttemptId(metadata) {
38895
39005
  if (typeof attemptId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(attemptId) && typeof interruptionCount === "number" && Number.isSafeInteger(interruptionCount) && interruptionCount > 0) {
38896
39006
  return attemptId;
38897
39007
  }
38898
- throw new SessionControlInvariantError("Queued Steer has malformed predecessor metadata");
39008
+ return null;
38899
39009
  }
38900
39010
  async function enqueueFailedChildOutboxForTurnTx(tx, workspaceId, session, turn) {
38901
39011
  if (!session.parentSessionId) return;
@@ -39016,6 +39126,7 @@ async function enqueueSessionWorkflowWakeInTransaction(tx, input) {
39016
39126
  workspaceId: input.workspaceId,
39017
39127
  sessionId: input.sessionId,
39018
39128
  temporalWorkflowId: input.temporalWorkflowId,
39129
+ controlRevision: input.controlRequested ? 1 : 0,
39019
39130
  reason: input.reason,
39020
39131
  nextAttemptAt
39021
39132
  }).onConflictDoUpdate({
@@ -39023,6 +39134,7 @@ async function enqueueSessionWorkflowWakeInTransaction(tx, input) {
39023
39134
  set: {
39024
39135
  temporalWorkflowId: input.temporalWorkflowId,
39025
39136
  wakeRevision: sql14`${sessionWorkflowWakeOutbox.wakeRevision} + 1`,
39137
+ controlRevision: input.controlRequested ? sql14`${sessionWorkflowWakeOutbox.wakeRevision} + 1` : sql14`${sessionWorkflowWakeOutbox.controlRevision}`,
39026
39138
  reason: input.reason,
39027
39139
  attempts: 0,
39028
39140
  // Coalescing a delayed retry must never postpone an already-due wake
@@ -40148,6 +40260,7 @@ function mapSession(row, effectiveControl, mcpServers = [], pin = mapSessionPin(
40148
40260
  // the fence exact even if the column type ever drifts (the lease-epoch lesson).
40149
40261
  activeSandboxId: row.activeSandboxId ?? null,
40150
40262
  activeEpoch: Number(row.activeEpoch),
40263
+ workingDir: row.workingDir ?? null,
40151
40264
  variableSetId: row.variableSetId,
40152
40265
  environmentId: row.variableSetId,
40153
40266
  // The rig + frozen rig version the session rides (M3). Both null for a
@@ -41528,6 +41641,7 @@ export {
41528
41641
  reopenSlackInteractionDelivery,
41529
41642
  replaceExpiredWorkspaceArchiveCapture,
41530
41643
  replaceIntegrationOAuthClient,
41644
+ replaceIntegrationOAuthClientIfCurrent,
41531
41645
  requestDueSandboxRotationsGlobal,
41532
41646
  requestSessionCompaction,
41533
41647
  requestSessionTurnRecovery,
@@ -41682,6 +41796,7 @@ export {
41682
41796
  withCodexCapacityMutation,
41683
41797
  withCodexCredentialRefreshLock,
41684
41798
  withCodexTokenDeadline,
41799
+ withDatabaseStatementTimeout,
41685
41800
  withRlsContext,
41686
41801
  withWorkspaceRls,
41687
41802
  withWorkspaceSubjectRls,