@opengeni/db 0.27.7 → 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
@@ -6375,6 +6375,7 @@ export type RequestSessionTurnRecoveryInput = {
6375
6375
  attemptId: string;
6376
6376
  reason: string;
6377
6377
  detail?: Record<string, unknown>;
6378
+ providerRecoveryCount?: number;
6378
6379
  fromStatuses?: SessionTurnStatus[];
6379
6380
  providerArtifactInvalidation?: {
6380
6381
  codexCredentialId: string;
@@ -6501,6 +6502,7 @@ export declare function enqueueSessionWorkflowWakeInTransaction(tx: Database, in
6501
6502
  temporalWorkflowId: string;
6502
6503
  reason: string;
6503
6504
  notBefore?: Date;
6505
+ controlRequested?: boolean;
6504
6506
  }): Promise<number>;
6505
6507
  /** Standalone transactional producer for operations not already in a DB txn. */
6506
6508
  export declare function enqueueSessionWorkflowWake(db: Database, input: {
@@ -6510,6 +6512,7 @@ export declare function enqueueSessionWorkflowWake(db: Database, input: {
6510
6512
  temporalWorkflowId: string;
6511
6513
  reason: string;
6512
6514
  notBefore?: Date;
6515
+ controlRequested?: boolean;
6513
6516
  }): Promise<number>;
6514
6517
  /**
6515
6518
  * Re-deliver already-committed session work only when admission currently
@@ -6523,6 +6526,7 @@ export declare function enqueueSessionWorkflowWakeIfRunnable(db: Database, input
6523
6526
  temporalWorkflowId: string;
6524
6527
  reason: string;
6525
6528
  notBefore?: Date;
6529
+ controlRequested?: boolean;
6526
6530
  }): Promise<number | null>;
6527
6531
  /** Claim only explicit, undelivered wake revisions; never infer work by scan. */
6528
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,
@@ -35641,7 +35648,8 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
35641
35648
  "pending",
35642
35649
  "delivered",
35643
35650
  "acknowledged",
35644
- "settled"
35651
+ "settled",
35652
+ "rejected_stale"
35645
35653
  ]),
35646
35654
  isNull6(sessionTurnAttempts.quiescedAt)
35647
35655
  )
@@ -36334,24 +36342,6 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
36334
36342
  turn: mapSessionTurnForExecution(internalTurn)
36335
36343
  };
36336
36344
  }
36337
- const predecessorAttemptId = queuedSteerReplacementAttemptId(queuedTurn.metadata);
36338
- if (predecessorAttemptId) {
36339
- const [predecessor] = await tx.select({ quiescedAt: sessionTurnAttempts.quiescedAt }).from(sessionTurnAttempts).where(
36340
- and17(
36341
- eq17(sessionTurnAttempts.workspaceId, workspaceId),
36342
- eq17(sessionTurnAttempts.sessionId, sessionId),
36343
- eq17(sessionTurnAttempts.id, predecessorAttemptId)
36344
- )
36345
- ).limit(1);
36346
- if (!predecessor) {
36347
- throw new SessionControlInvariantError(
36348
- `Queued Steer ${id} points to missing predecessor attempt ${predecessorAttemptId}`
36349
- );
36350
- }
36351
- if (!predecessor.quiescedAt) {
36352
- return { action: "unclaimed", reason: "control-pending" };
36353
- }
36354
- }
36355
36345
  const now = /* @__PURE__ */ new Date();
36356
36346
  const queuedDispatch = readTurnDispatchMetadata(queuedTurn?.metadata);
36357
36347
  if (queuedDispatch.kind === "malformed") {
@@ -36776,6 +36766,14 @@ async function settleSessionAttemptInterruptions(db, workspaceId, sessionId, att
36776
36766
  interruptions.map((interruption) => interruption.id)
36777
36767
  )
36778
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
+ });
36779
36777
  return {
36780
36778
  action: effectiveControl.state === "paused" ? "paused" : "continue",
36781
36779
  events: [],
@@ -36926,6 +36924,14 @@ async function settleSessionAttemptInterruptions(db, workspaceId, sessionId, att
36926
36924
  interruptions.map((interruption) => interruption.id)
36927
36925
  )
36928
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
+ });
36929
36935
  return {
36930
36936
  action: effectiveControl.state === "paused" ? "paused" : "continue",
36931
36937
  events: [...closedTools.events, ...eventRows.map(mapEvent2)],
@@ -36936,6 +36942,30 @@ async function settleSessionAttemptInterruptions(db, workspaceId, sessionId, att
36936
36942
  })
36937
36943
  );
36938
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
+ }
36939
36969
  async function latestSessionAttemptInterruption(db, workspaceId, sessionId) {
36940
36970
  const [latestAttempt] = await db.select({
36941
36971
  attemptId: sessionTurnAttempts.id,
@@ -36962,6 +36992,32 @@ async function latestSessionAttemptInterruption(db, workspaceId, sessionId) {
36962
36992
  interruptionState: interruption.state
36963
36993
  } : null;
36964
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
+ }
36965
37021
  async function peekSessionWork(db, workspaceId, sessionId) {
36966
37022
  return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
36967
37023
  const effectiveControl = await evaluateSessionControl(scopedDb, workspaceId, sessionId, {
@@ -36990,15 +37046,15 @@ async function peekSessionWork(db, workspaceId, sessionId) {
36990
37046
  };
36991
37047
  }
36992
37048
  if (effectiveControl.state !== "active") return { kind: "idle" };
36993
- const latestInterruption = await latestSessionAttemptInterruption(
37049
+ const awaitingQuiescence = await nextSessionAttemptAwaitingQuiescence(
36994
37050
  scopedDb,
36995
37051
  workspaceId,
36996
37052
  sessionId
36997
37053
  );
36998
- if (latestInterruption && latestInterruption.quiescedAt === null && latestInterruption.interruptionState === "settled") {
37054
+ if (awaitingQuiescence) {
36999
37055
  return {
37000
37056
  kind: "cancellation-wait",
37001
- attemptId: latestInterruption.attemptId
37057
+ attemptId: awaitingQuiescence.attemptId
37002
37058
  };
37003
37059
  }
37004
37060
  const [capacityWait] = await scopedDb.select().from(codexCapacityWaiters).where(
@@ -38325,7 +38381,19 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
38325
38381
  { workspaceControl: locks.control ?? void 0 }
38326
38382
  );
38327
38383
  const turnStatus = turn?.status ?? null;
38328
- 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) {
38329
38397
  return {
38330
38398
  action: "stale",
38331
38399
  events: [],
@@ -38334,6 +38402,9 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
38334
38402
  };
38335
38403
  }
38336
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
+ }
38337
38408
  let providerArtifactsInvalidated = 0;
38338
38409
  if (input.providerArtifactInvalidation) {
38339
38410
  const invalidatedHistory = await tx.update(sessionHistoryItems).set({
@@ -38451,7 +38522,10 @@ async function requestSessionTurnRecovery(db, workspaceId, input) {
38451
38522
  cancelledBy: null,
38452
38523
  cancelReason: null,
38453
38524
  version: turn.version + 1,
38454
- metadata: metadataWithoutTurnDispatchAttempt(turn.metadata),
38525
+ metadata: {
38526
+ ...metadataWithoutTurnDispatchAttempt(turn.metadata),
38527
+ ...input.providerRecoveryCount !== void 0 ? { providerRecoveryCount: input.providerRecoveryCount } : {}
38528
+ },
38455
38529
  updatedAt: now
38456
38530
  }).where(
38457
38531
  and17(
@@ -38880,12 +38954,19 @@ async function getSessionQueueSnapshot(db, workspaceId, sessionId) {
38880
38954
  asc8(sessionSystemUpdates.createdAt),
38881
38955
  asc8(sessionSystemUpdates.id)
38882
38956
  );
38957
+ const items = rows.map(mapSessionTurn);
38883
38958
  const latestInterruption = await latestSessionAttemptInterruption(
38884
38959
  scopedDb,
38885
38960
  workspaceId,
38886
38961
  sessionId
38887
38962
  );
38888
- 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
+ );
38889
38970
  const nextInputBatch = selectBoundedSystemUpdateBatch(pendingInputs);
38890
38971
  const hasPendingAgentSteer = pendingInputs.some(
38891
38972
  (update) => update.kind === "agent_steer_instruction"
@@ -38895,7 +38976,7 @@ async function getSessionQueueSnapshot(db, workspaceId, sessionId) {
38895
38976
  version: session.queueVersion,
38896
38977
  effectiveControl: serializeEffectiveSessionControl(effectiveControl),
38897
38978
  activePersonalConnections,
38898
- stoppingPreviousAttempt: latestInterruption !== null && latestInterruption.interruptionState !== "rejected_stale" && latestInterruption.quiescedAt === null,
38979
+ stoppingPreviousAttempt,
38899
38980
  items,
38900
38981
  pendingInputs: pendingInputs.map((update) => {
38901
38982
  const canonical = mapSessionSystemUpdate(update);
@@ -38924,7 +39005,7 @@ function queuedSteerReplacementAttemptId(metadata) {
38924
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) {
38925
39006
  return attemptId;
38926
39007
  }
38927
- throw new SessionControlInvariantError("Queued Steer has malformed predecessor metadata");
39008
+ return null;
38928
39009
  }
38929
39010
  async function enqueueFailedChildOutboxForTurnTx(tx, workspaceId, session, turn) {
38930
39011
  if (!session.parentSessionId) return;
@@ -39045,6 +39126,7 @@ async function enqueueSessionWorkflowWakeInTransaction(tx, input) {
39045
39126
  workspaceId: input.workspaceId,
39046
39127
  sessionId: input.sessionId,
39047
39128
  temporalWorkflowId: input.temporalWorkflowId,
39129
+ controlRevision: input.controlRequested ? 1 : 0,
39048
39130
  reason: input.reason,
39049
39131
  nextAttemptAt
39050
39132
  }).onConflictDoUpdate({
@@ -39052,6 +39134,7 @@ async function enqueueSessionWorkflowWakeInTransaction(tx, input) {
39052
39134
  set: {
39053
39135
  temporalWorkflowId: input.temporalWorkflowId,
39054
39136
  wakeRevision: sql14`${sessionWorkflowWakeOutbox.wakeRevision} + 1`,
39137
+ controlRevision: input.controlRequested ? sql14`${sessionWorkflowWakeOutbox.wakeRevision} + 1` : sql14`${sessionWorkflowWakeOutbox.controlRevision}`,
39055
39138
  reason: input.reason,
39056
39139
  attempts: 0,
39057
39140
  // Coalescing a delayed retry must never postpone an already-due wake