@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/{chunk-EX7CDSGH.js → chunk-M6EOXYHK.js} +6 -1
- package/dist/chunk-M6EOXYHK.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +151 -36
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +17 -0
- package/dist/schema.js +1 -1
- package/dist/session-control.d.ts +1 -0
- package/dist/session-realtime-context.d.ts +1 -1
- package/drizzle/0170_session_control_wake_revision.sql +133 -0
- package/package.json +4 -4
- package/src/index.ts +214 -37
- package/src/schema.ts +5 -0
- package/src/session-control.ts +11 -4
- package/src/session-queue-commands.ts +3 -0
- package/src/session-realtime-context.ts +1 -1
- package/dist/chunk-EX7CDSGH.js.map +0 -1
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-
|
|
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
|
|
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
|
|
37049
|
+
const awaitingQuiescence = await nextSessionAttemptAwaitingQuiescence(
|
|
36965
37050
|
scopedDb,
|
|
36966
37051
|
workspaceId,
|
|
36967
37052
|
sessionId
|
|
36968
37053
|
);
|
|
36969
|
-
if (
|
|
37054
|
+
if (awaitingQuiescence) {
|
|
36970
37055
|
return {
|
|
36971
37056
|
kind: "cancellation-wait",
|
|
36972
|
-
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|