@opengeni/db 1.0.2 → 1.2.0
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/artifact-catalog.d.ts +12 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-reconciliation.js +2 -2
- package/dist/{chunk-KH3TYDGY.js → chunk-3GAR5UN3.js} +2 -2
- package/dist/{chunk-UEQQ2WWU.js → chunk-3TGYFFZR.js} +3 -26
- package/dist/chunk-3TGYFFZR.js.map +1 -0
- package/dist/{chunk-F3YMVRF4.js → chunk-6EIEGHPK.js} +4 -4
- package/dist/{chunk-OQZOGCLG.js → chunk-CONWNUSL.js} +2 -2
- package/dist/{chunk-7S53WLPI.js → chunk-CYP72JTQ.js} +11 -4
- package/dist/{chunk-7S53WLPI.js.map → chunk-CYP72JTQ.js.map} +1 -1
- package/dist/{chunk-LDTQGYD2.js → chunk-FWQNHNOU.js} +54 -2
- package/dist/chunk-FWQNHNOU.js.map +1 -0
- package/dist/{chunk-KEDWSSNP.js → chunk-GDN5TSZP.js} +2 -2
- package/dist/{chunk-PQGTWFRF.js → chunk-JICUP7ZR.js} +2 -2
- package/dist/{chunk-NLSG7GGA.js → chunk-JLVWBIEM.js} +3 -3
- package/dist/{chunk-Y77PVZZD.js → chunk-LXPFNG4L.js} +2 -2
- package/dist/{chunk-56MYWKQV.js → chunk-O2Y5E5M3.js} +68 -32
- package/dist/chunk-O2Y5E5M3.js.map +1 -0
- package/dist/{chunk-7POTLB24.js → chunk-QFJNEXFL.js} +2 -2
- package/dist/{chunk-MACELCXG.js → chunk-UCIMHNII.js} +101 -21
- package/dist/chunk-UCIMHNII.js.map +1 -0
- package/dist/{chunk-OMHBVHX7.js → chunk-VDPCIFKL.js} +5 -5
- package/dist/{chunk-N5QCKHYL.js → chunk-YOYILDLY.js} +87 -19
- package/dist/chunk-YOYILDLY.js.map +1 -0
- package/dist/connect-attempts.js +3 -3
- package/dist/credit-balances.d.ts +18 -0
- package/dist/credit-promotion-policy.d.ts +10 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +35 -10
- package/dist/index.js +2432 -2255
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/managed-user-setup.d.ts +1 -0
- package/dist/mcp-operations.js +2 -2
- package/dist/organization-integration-policy.js +3 -3
- package/dist/organization-membership-lifecycle.d.ts +1 -0
- package/dist/persistence-errors.d.ts +9 -6
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.js +6 -6
- package/dist/runtime-posture.d.ts +7 -6
- package/dist/schema.d.ts +214 -2
- package/dist/schema.js +5 -1
- package/dist/session-background-commands.js +4 -4
- package/dist/session-command-output.js +5 -5
- package/dist/session-event-slices.js +3 -3
- package/dist/session-mcp-credential-rotation.js +5 -5
- package/dist/session-tenancy.d.ts +6 -1
- package/dist/session-tenancy.js +3 -5
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0610_artifact_catalog_pins.sql +165 -0
- package/drizzle/0611_universal_session_tenancy_activation.sql +166 -0
- package/drizzle/0612_scheduled_model_unavailable_refusal.sql +21 -0
- package/drizzle/0613_model_scoped_promotional_credits.sql +238 -0
- package/drizzle/0614_quiescence_command_containment.sql +7 -0
- package/drizzle/0615_credit_promotion_policy_validation.sql +63 -0
- package/package.json +7 -7
- package/src/artifact-catalog.ts +31 -5
- package/src/credit-balances.ts +109 -0
- package/src/credit-promotion-policy.ts +12 -0
- package/src/database.ts +81 -42
- package/src/index.ts +295 -79
- package/src/managed-user-setup.ts +8 -0
- package/src/organization-membership-lifecycle.ts +8 -0
- package/src/persistence-errors.ts +17 -6
- package/src/provision-roles.ts +25 -0
- package/src/runtime-posture-cli.ts +0 -2
- package/src/runtime-posture.ts +86 -27
- package/src/schema.ts +57 -1
- package/src/session-tenancy.ts +9 -31
- package/src/video-generation.ts +2 -7
- package/dist/chunk-56MYWKQV.js.map +0 -1
- package/dist/chunk-LDTQGYD2.js.map +0 -1
- package/dist/chunk-MACELCXG.js.map +0 -1
- package/dist/chunk-N5QCKHYL.js.map +0 -1
- package/dist/chunk-UEQQ2WWU.js.map +0 -1
- /package/dist/{chunk-KH3TYDGY.js.map → chunk-3GAR5UN3.js.map} +0 -0
- /package/dist/{chunk-F3YMVRF4.js.map → chunk-6EIEGHPK.js.map} +0 -0
- /package/dist/{chunk-OQZOGCLG.js.map → chunk-CONWNUSL.js.map} +0 -0
- /package/dist/{chunk-KEDWSSNP.js.map → chunk-GDN5TSZP.js.map} +0 -0
- /package/dist/{chunk-PQGTWFRF.js.map → chunk-JICUP7ZR.js.map} +0 -0
- /package/dist/{chunk-NLSG7GGA.js.map → chunk-JLVWBIEM.js.map} +0 -0
- /package/dist/{chunk-Y77PVZZD.js.map → chunk-LXPFNG4L.js.map} +0 -0
- /package/dist/{chunk-7POTLB24.js.map → chunk-QFJNEXFL.js.map} +0 -0
- /package/dist/{chunk-OMHBVHX7.js.map → chunk-VDPCIFKL.js.map} +0 -0
package/src/index.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export { readCreditPromotionPolicy } from "./credit-promotion-policy";
|
|
2
|
+
import { getBillingBalance, getSpendableCreditBalance, planCreditDebit } from "./credit-balances";
|
|
3
|
+
export {
|
|
4
|
+
getBillingBalance,
|
|
5
|
+
getSpendableCreditBalance,
|
|
6
|
+
spendableCreditMicros,
|
|
7
|
+
} from "./credit-balances";
|
|
1
8
|
import {
|
|
2
9
|
agentSelectionNotes,
|
|
3
10
|
loadInboxExecutionContext,
|
|
@@ -6,6 +13,7 @@ import {
|
|
|
6
13
|
} from "./inbox-execution-context";
|
|
7
14
|
import { parentOutboxAuthorityTx } from "./child-outbox-authority";
|
|
8
15
|
export { SubscriptionAccountChangedError } from "./subscription-account-conflict";
|
|
16
|
+
import type { GoalAdmissionPausedReason } from "@opengeni/contracts";
|
|
9
17
|
import {
|
|
10
18
|
claudeSubscriptionAccountRepository,
|
|
11
19
|
claudeSubscriptionTables,
|
|
@@ -439,7 +447,6 @@ import {
|
|
|
439
447
|
closePrivateSessionCreateCapability,
|
|
440
448
|
openPrivateChildSessionCreateCapability,
|
|
441
449
|
openPrivateSessionCreateCapability,
|
|
442
|
-
sessionTenancyProductActivated,
|
|
443
450
|
} from "./session-tenancy";
|
|
444
451
|
import {
|
|
445
452
|
completeCodemodeOperationInTransaction,
|
|
@@ -616,6 +623,7 @@ import {
|
|
|
616
623
|
SessionEventPersistenceError,
|
|
617
624
|
type IdempotentPersistenceTransactionOptions,
|
|
618
625
|
} from "./persistence-errors";
|
|
626
|
+
export { DatabaseTransactionError } from "./persistence-errors";
|
|
619
627
|
import {
|
|
620
628
|
assertCapabilityComponentVersionCanChange,
|
|
621
629
|
effectiveCapabilityOwnerSql,
|
|
@@ -5701,6 +5709,7 @@ export async function applyCreditLedgerEntry(
|
|
|
5701
5709
|
workspaceId?: string | null;
|
|
5702
5710
|
type: string;
|
|
5703
5711
|
amountMicros: number;
|
|
5712
|
+
eligibleModelIds?: string[] | undefined;
|
|
5704
5713
|
sourceType?: string | null;
|
|
5705
5714
|
sourceId?: string | null;
|
|
5706
5715
|
idempotencyKey: string;
|
|
@@ -5719,6 +5728,7 @@ export async function applyCreditLedgerEntry(
|
|
|
5719
5728
|
workspaceId: input.workspaceId ?? null,
|
|
5720
5729
|
type: input.type,
|
|
5721
5730
|
amountMicros: input.amountMicros,
|
|
5731
|
+
eligibleModelIds: input.eligibleModelIds ?? null,
|
|
5722
5732
|
sourceType: input.sourceType ?? null,
|
|
5723
5733
|
sourceId: input.sourceId ?? null,
|
|
5724
5734
|
idempotencyKey: input.idempotencyKey,
|
|
@@ -5740,6 +5750,9 @@ export async function applyCreditDebitUpToBalance(
|
|
|
5740
5750
|
workspaceId?: string | null;
|
|
5741
5751
|
type: string;
|
|
5742
5752
|
requestedAmountMicros: number;
|
|
5753
|
+
/** Canonical, accepted model ID. Omitted for non-model usage. */
|
|
5754
|
+
modelId?: string;
|
|
5755
|
+
creditPolicyRevision?: number | undefined;
|
|
5743
5756
|
sourceType?: string | null;
|
|
5744
5757
|
sourceId?: string | null;
|
|
5745
5758
|
idempotencyKey: string;
|
|
@@ -5758,35 +5771,42 @@ export async function applyCreditDebitUpToBalance(
|
|
|
5758
5771
|
{ accountId: input.accountId, workspaceId: input.workspaceId ?? null },
|
|
5759
5772
|
async (scopedDb) => {
|
|
5760
5773
|
await scopedDb.execute(sql`select pg_advisory_xact_lock(hashtext(${input.accountId}))`);
|
|
5761
|
-
const before = await getBillingBalance(scopedDb, input.accountId);
|
|
5762
|
-
const
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
.
|
|
5770
|
-
.
|
|
5774
|
+
const before = await getBillingBalance(scopedDb, input.accountId, input.creditPolicyRevision);
|
|
5775
|
+
const plan = planCreditDebit(before, input.requestedAmountMicros, input.modelId);
|
|
5776
|
+
const candidateDebitMicros = plan.debitedMicros;
|
|
5777
|
+
// Keep a zero-cost receipt too: retrying after a top-up must not charge
|
|
5778
|
+
// a response that was already settled while the eligible balance was empty.
|
|
5779
|
+
const [inserted] = await scopedDb
|
|
5780
|
+
.insert(schema.creditLedgerEntries)
|
|
5781
|
+
.values({
|
|
5782
|
+
accountId: input.accountId,
|
|
5783
|
+
workspaceId: input.workspaceId ?? null,
|
|
5784
|
+
type: input.type,
|
|
5785
|
+
amountMicros: -candidateDebitMicros,
|
|
5786
|
+
sourceType: input.sourceType ?? null,
|
|
5787
|
+
sourceId: input.sourceId ?? null,
|
|
5788
|
+
idempotencyKey: input.idempotencyKey,
|
|
5789
|
+
metadata: {
|
|
5790
|
+
...input.metadata,
|
|
5791
|
+
requestedAmountMicros: input.requestedAmountMicros,
|
|
5792
|
+
debitedMicros: candidateDebitMicros,
|
|
5793
|
+
creditPolicyRevision: before.creditPolicyRevision,
|
|
5794
|
+
},
|
|
5795
|
+
occurredAt: input.occurredAt ?? new Date(),
|
|
5796
|
+
})
|
|
5797
|
+
.onConflictDoNothing({
|
|
5798
|
+
target: schema.creditLedgerEntries.idempotencyKey,
|
|
5799
|
+
})
|
|
5800
|
+
.returning({ id: schema.creditLedgerEntries.id });
|
|
5801
|
+
const debitedMicros = inserted ? candidateDebitMicros : 0;
|
|
5802
|
+
if (inserted && plan.allocations.length) {
|
|
5803
|
+
await scopedDb.insert(schema.creditDebitAllocations).values(
|
|
5804
|
+
plan.allocations.map((allocation) => ({
|
|
5805
|
+
...allocation,
|
|
5771
5806
|
accountId: input.accountId,
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
sourceType: input.sourceType ?? null,
|
|
5776
|
-
sourceId: input.sourceId ?? null,
|
|
5777
|
-
idempotencyKey: input.idempotencyKey,
|
|
5778
|
-
metadata: {
|
|
5779
|
-
...input.metadata,
|
|
5780
|
-
requestedAmountMicros: input.requestedAmountMicros,
|
|
5781
|
-
debitedMicros: candidateDebitMicros,
|
|
5782
|
-
},
|
|
5783
|
-
occurredAt: input.occurredAt ?? new Date(),
|
|
5784
|
-
})
|
|
5785
|
-
.onConflictDoNothing({
|
|
5786
|
-
target: schema.creditLedgerEntries.idempotencyKey,
|
|
5787
|
-
})
|
|
5788
|
-
.returning({ id: schema.creditLedgerEntries.id });
|
|
5789
|
-
debitedMicros = inserted.length === 1 ? candidateDebitMicros : 0;
|
|
5807
|
+
debitEntryId: inserted.id,
|
|
5808
|
+
})),
|
|
5809
|
+
);
|
|
5790
5810
|
}
|
|
5791
5811
|
return {
|
|
5792
5812
|
balance: await getBillingBalance(scopedDb, input.accountId),
|
|
@@ -6019,23 +6039,6 @@ export async function markStripeWebhookProcessed(db: Database, id: string): Prom
|
|
|
6019
6039
|
.where(eq(schema.stripeWebhookEvents.id, id));
|
|
6020
6040
|
}
|
|
6021
6041
|
|
|
6022
|
-
export async function getBillingBalance(db: Database, accountId: string): Promise<BillingBalance> {
|
|
6023
|
-
return await withAccountRls(db, accountId, async (scopedDb) => {
|
|
6024
|
-
const [{ balance } = { balance: 0 }] = await scopedDb
|
|
6025
|
-
.select({
|
|
6026
|
-
balance: sql<number>`coalesce(sum(${schema.creditLedgerEntries.amountMicros}), 0)`,
|
|
6027
|
-
})
|
|
6028
|
-
.from(schema.creditLedgerEntries)
|
|
6029
|
-
.where(eq(schema.creditLedgerEntries.accountId, accountId));
|
|
6030
|
-
return {
|
|
6031
|
-
accountId,
|
|
6032
|
-
balanceMicros: Number(balance),
|
|
6033
|
-
currency: "usd",
|
|
6034
|
-
updatedAt: new Date().toISOString(),
|
|
6035
|
-
};
|
|
6036
|
-
});
|
|
6037
|
-
}
|
|
6038
|
-
|
|
6039
6042
|
/**
|
|
6040
6043
|
* Whether the organization holds a positive OpenGeni credit balance, whatever
|
|
6041
6044
|
* its source: a purchase, an operator grant, a test credit, or the one-time
|
|
@@ -18575,6 +18578,7 @@ export type ScheduledTaskAdmissionRefusalReason =
|
|
|
18575
18578
|
| "machine_enrollment_inactive"
|
|
18576
18579
|
| "variable_set_unavailable"
|
|
18577
18580
|
| "rig_version_unavailable"
|
|
18581
|
+
| "scheduled_model_unavailable"
|
|
18578
18582
|
| "insufficient_credits"
|
|
18579
18583
|
| "allowance_exhausted"
|
|
18580
18584
|
| "monthly_model_cost_limit"
|
|
@@ -37685,11 +37689,6 @@ async function readSessionListForSubject(
|
|
|
37685
37689
|
throw new SessionListAccessError();
|
|
37686
37690
|
}
|
|
37687
37691
|
|
|
37688
|
-
const tenancyActivated = await sessionTenancyProductActivated(
|
|
37689
|
-
tx as unknown as Database,
|
|
37690
|
-
workspaceId,
|
|
37691
|
-
);
|
|
37692
|
-
|
|
37693
37692
|
const archiveMode = options.archiveStatus ?? (options.archivedOnly ? "archived" : "active");
|
|
37694
37693
|
const sortBy = options.sortBy ?? (archiveMode === "archived" ? "archivedAt" : "updatedAt");
|
|
37695
37694
|
if (options.archivedOnly && archiveMode !== "archived") {
|
|
@@ -38139,7 +38138,7 @@ async function readSessionListForSubject(
|
|
|
38139
38138
|
? { archivedAt: exactArchiveTimestamps.get(session.id)! }
|
|
38140
38139
|
: {}),
|
|
38141
38140
|
},
|
|
38142
|
-
{ subjectId: options.subjectId
|
|
38141
|
+
{ subjectId: options.subjectId },
|
|
38143
38142
|
),
|
|
38144
38143
|
...(sortBy === "updatedAt" && exactOrdinaryTimestamps.has(session.id)
|
|
38145
38144
|
? { updatedAt: exactOrdinaryTimestamps.get(session.id)! }
|
|
@@ -38395,7 +38394,6 @@ export async function getSessionForSubject(
|
|
|
38395
38394
|
const mcpServers = await sessionMcpServerMetadataForSessions(scopedDb, workspaceId, [
|
|
38396
38395
|
sessionId,
|
|
38397
38396
|
]);
|
|
38398
|
-
const tenancyActivated = await sessionTenancyProductActivated(scopedDb, workspaceId);
|
|
38399
38397
|
const failureDiagnostics = await sessionFailureDiagnostics(scopedDb, workspaceId, session);
|
|
38400
38398
|
return projectSessionForRelatedAccess(
|
|
38401
38399
|
{
|
|
@@ -38407,7 +38405,7 @@ export async function getSessionForSubject(
|
|
|
38407
38405
|
mapSessionAttention(session, row.pin),
|
|
38408
38406
|
mapSessionArchive(row.pin),
|
|
38409
38407
|
undefined,
|
|
38410
|
-
{ subjectId
|
|
38408
|
+
{ subjectId },
|
|
38411
38409
|
)),
|
|
38412
38410
|
failureDiagnostics,
|
|
38413
38411
|
},
|
|
@@ -44562,6 +44560,19 @@ export async function applyContextCompaction(
|
|
|
44562
44560
|
),
|
|
44563
44561
|
);
|
|
44564
44562
|
const supersededFrom = Math.floor(Number(maxPosition)) + 1;
|
|
44563
|
+
// Installing a successful summary is model progress, including a
|
|
44564
|
+
// maintenance turn that never enters the ordinary response stream.
|
|
44565
|
+
// Clear only provider recovery under the same locked attempt fence.
|
|
44566
|
+
if (providerRecoveryCountFromTurnMetadata(fence.turn!.metadata) > 0) {
|
|
44567
|
+
await tx
|
|
44568
|
+
.update(schema.sessionTurns)
|
|
44569
|
+
.set({
|
|
44570
|
+
metadata: metadataWithoutProviderRecoveryCount(fence.turn!.metadata),
|
|
44571
|
+
version: fence.turn!.version + 1,
|
|
44572
|
+
updatedAt: new Date(),
|
|
44573
|
+
})
|
|
44574
|
+
.where(eq(schema.sessionTurns.id, input.turnId));
|
|
44575
|
+
}
|
|
44565
44576
|
await tx
|
|
44566
44577
|
.update(schema.sessionHistoryItems)
|
|
44567
44578
|
.set({ active: false })
|
|
@@ -47845,7 +47856,7 @@ async function acquireLeaseOnce(
|
|
|
47845
47856
|
refusal,
|
|
47846
47857
|
);
|
|
47847
47858
|
}
|
|
47848
|
-
const balance = await
|
|
47859
|
+
const balance = await getSpendableCreditBalance(tx, accountId);
|
|
47849
47860
|
if (balance.balanceMicros <= 0) {
|
|
47850
47861
|
throw new SandboxPaidComputeAdmissionError(workspaceId, sandboxGroupId);
|
|
47851
47862
|
}
|
|
@@ -52806,8 +52817,8 @@ type LostProviderBlockerScope = {
|
|
|
52806
52817
|
};
|
|
52807
52818
|
|
|
52808
52819
|
/** Take the session-event write prefix (workspace control share, workspace,
|
|
52809
|
-
* sessions, cursors) for
|
|
52810
|
-
*
|
|
52820
|
+
* sessions, cursors) for linked background commands and attempt owners in the
|
|
52821
|
+
* exact lost-provider scope. Call it before the
|
|
52811
52822
|
* process/admission/PTY/lease locks so terminal command delivery keeps the
|
|
52812
52823
|
* canonical control -> session -> process -> admission -> lease order. */
|
|
52813
52824
|
async function linkedLostProviderCommandSessionIdsTx(
|
|
@@ -52817,7 +52828,8 @@ async function linkedLostProviderCommandSessionIdsTx(
|
|
|
52817
52828
|
const sessions = await rawRows<{ session_id: string }>(
|
|
52818
52829
|
tx,
|
|
52819
52830
|
sql`
|
|
52820
|
-
select distinct
|
|
52831
|
+
select distinct session_id from (
|
|
52832
|
+
select command.session_id
|
|
52821
52833
|
from session_background_commands command
|
|
52822
52834
|
join sandbox_retained_processes process on process.id = command.retained_process_id
|
|
52823
52835
|
and process.workspace_id = command.workspace_id
|
|
@@ -52831,7 +52843,21 @@ async function linkedLostProviderCommandSessionIdsTx(
|
|
|
52831
52843
|
and process.provider_instance_id = ${input.lostInstanceId}
|
|
52832
52844
|
and process.state = 'active'
|
|
52833
52845
|
and command.state in ('running', 'stopping')
|
|
52834
|
-
|
|
52846
|
+
union
|
|
52847
|
+
select process.session_id
|
|
52848
|
+
from sandbox_retained_processes process
|
|
52849
|
+
join session_turn_attempts attempt on attempt.id = process.owner_attempt_id
|
|
52850
|
+
and attempt.workspace_id = process.workspace_id
|
|
52851
|
+
and attempt.session_id = process.session_id
|
|
52852
|
+
where process.account_id = ${input.accountId}
|
|
52853
|
+
and process.workspace_id = ${input.workspaceId}
|
|
52854
|
+
and process.lease_id = ${input.leaseId}
|
|
52855
|
+
and process.sandbox_group_id = ${input.sandboxGroupId}
|
|
52856
|
+
and process.lease_epoch = ${input.lostEpoch}
|
|
52857
|
+
and process.provider_backend = ${input.lostBackend}
|
|
52858
|
+
and process.provider_instance_id = ${input.lostInstanceId}
|
|
52859
|
+
and process.state = 'active'
|
|
52860
|
+
) owners order by session_id
|
|
52835
52861
|
`,
|
|
52836
52862
|
);
|
|
52837
52863
|
return sessions.map((row) => row.session_id);
|
|
@@ -53053,6 +53079,37 @@ async function settleExactLostProviderWorkspaceBlockersTx(
|
|
|
53053
53079
|
}
|
|
53054
53080
|
}
|
|
53055
53081
|
|
|
53082
|
+
if (lostProcesses.length) {
|
|
53083
|
+
// The session prefix was locked before blockers and revalidated afterward.
|
|
53084
|
+
// Unadopted writers have no background-command delivery to wake their owner;
|
|
53085
|
+
// persist that obligation atomically with physical provider settlement.
|
|
53086
|
+
const owners = await rawRows<{ session_id: string; temporal_workflow_id: string }>(
|
|
53087
|
+
tx,
|
|
53088
|
+
sql`
|
|
53089
|
+
select distinct attempt.session_id, attempt.temporal_workflow_id
|
|
53090
|
+
from session_turn_attempts attempt
|
|
53091
|
+
join sandbox_retained_processes process on process.owner_attempt_id = attempt.id
|
|
53092
|
+
and process.workspace_id = attempt.workspace_id
|
|
53093
|
+
and process.session_id = attempt.session_id
|
|
53094
|
+
where attempt.workspace_id = ${input.workspaceId}
|
|
53095
|
+
and attempt.account_id = ${input.accountId}
|
|
53096
|
+
and attempt.state = 'closed' and attempt.quiesced_at is null
|
|
53097
|
+
and attempt.temporal_workflow_id is not null
|
|
53098
|
+
and process.id = any(${`{${lostProcesses.map((p) => p.id).join(",")}}`}::uuid[])
|
|
53099
|
+
order by attempt.session_id, attempt.temporal_workflow_id
|
|
53100
|
+
`,
|
|
53101
|
+
);
|
|
53102
|
+
for (const owner of owners) {
|
|
53103
|
+
await enqueueSessionWorkflowWakeInTransaction(tx, {
|
|
53104
|
+
accountId: input.accountId,
|
|
53105
|
+
workspaceId: input.workspaceId,
|
|
53106
|
+
sessionId: owner.session_id,
|
|
53107
|
+
temporalWorkflowId: owner.temporal_workflow_id,
|
|
53108
|
+
reason: "attempt_writer_provider_settled",
|
|
53109
|
+
});
|
|
53110
|
+
}
|
|
53111
|
+
}
|
|
53112
|
+
|
|
53056
53113
|
return {
|
|
53057
53114
|
processesLost: lostProcesses.length,
|
|
53058
53115
|
admissionsRejected: rejectedAdmissions.length,
|
|
@@ -53939,7 +53996,7 @@ export async function heartbeatLeaseHolderStatus(
|
|
|
53939
53996
|
(input.billingMode ?? snapshot?.mode) === "credits" &&
|
|
53940
53997
|
snapshot?.mode === "credits" &&
|
|
53941
53998
|
snapshot.rateMicrosPerSecond > 0 &&
|
|
53942
|
-
(await
|
|
53999
|
+
(await getSpendableCreditBalance(tx, input.accountId)).balanceMicros <= 0
|
|
53943
54000
|
) {
|
|
53944
54001
|
// Touching the holder above is essential: the running writer remains
|
|
53945
54002
|
// protected while it winds down. The provider/lease TTL is not renewed.
|
|
@@ -54595,23 +54652,69 @@ export async function reapStaleLeaseHolders(
|
|
|
54595
54652
|
}
|
|
54596
54653
|
|
|
54597
54654
|
/** Settlement reasons for legacy retained commands that containment stopped
|
|
54598
|
-
* after a verified checkpoint.
|
|
54655
|
+
* after a verified checkpoint. None is exit proof: the command settles
|
|
54599
54656
|
* `lost` with no exit code. */
|
|
54600
54657
|
export const IDLE_COMMAND_CONTAINMENT_REASON = "idle_containment";
|
|
54601
54658
|
export const DEADLINE_COMMAND_CONTAINMENT_REASON = "provider_deadline_containment";
|
|
54659
|
+
export const QUIESCENCE_COMMAND_CONTAINMENT_REASON = "quiescence_containment";
|
|
54660
|
+
|
|
54661
|
+
type SettledCommandOwner = {
|
|
54662
|
+
sessionId: string;
|
|
54663
|
+
attemptId: string;
|
|
54664
|
+
temporalWorkflowId: string;
|
|
54665
|
+
temporalWorkflowRunId: string;
|
|
54666
|
+
temporalActivityId: string;
|
|
54667
|
+
};
|
|
54668
|
+
|
|
54669
|
+
/** Exact dispatch settlement is enrollment authority, never physical proof. */
|
|
54670
|
+
async function settledCommandOwnerMatchesTx(
|
|
54671
|
+
tx: Database,
|
|
54672
|
+
input: { accountId: string; workspaceId: string; sandboxGroupId: string },
|
|
54673
|
+
owner: SettledCommandOwner,
|
|
54674
|
+
): Promise<boolean> {
|
|
54675
|
+
const [row] = await rawRows<{ eligible: boolean }>(
|
|
54676
|
+
tx,
|
|
54677
|
+
sql`
|
|
54678
|
+
select exists (
|
|
54679
|
+
select 1 from session_turn_attempts attempt
|
|
54680
|
+
join sessions session on session.id = attempt.session_id
|
|
54681
|
+
and session.workspace_id = attempt.workspace_id
|
|
54682
|
+
where attempt.account_id = ${input.accountId}
|
|
54683
|
+
and attempt.workspace_id = ${input.workspaceId}
|
|
54684
|
+
and attempt.session_id = ${owner.sessionId} and attempt.id = ${owner.attemptId}
|
|
54685
|
+
and attempt.temporal_workflow_id = ${owner.temporalWorkflowId}
|
|
54686
|
+
and attempt.temporal_workflow_run_id = ${owner.temporalWorkflowRunId}
|
|
54687
|
+
and attempt.temporal_activity_id = ${owner.temporalActivityId}
|
|
54688
|
+
and attempt.state = 'closed' and attempt.quiesced_at is null
|
|
54689
|
+
and session.sandbox_group_id = ${input.sandboxGroupId}
|
|
54690
|
+
and not exists (select 1 from session_attempt_interruptions interruption
|
|
54691
|
+
where interruption.workspace_id = attempt.workspace_id
|
|
54692
|
+
and interruption.session_id = attempt.session_id and interruption.attempt_id = attempt.id
|
|
54693
|
+
and interruption.state in ('pending', 'delivered', 'acknowledged'))
|
|
54694
|
+
) and not exists (
|
|
54695
|
+
select 1 from session_turn_attempts live
|
|
54696
|
+
join sessions member on member.id = live.session_id and member.workspace_id = live.workspace_id
|
|
54697
|
+
where live.workspace_id = ${input.workspaceId}
|
|
54698
|
+
and member.sandbox_group_id = ${input.sandboxGroupId} and live.state <> 'closed'
|
|
54699
|
+
) as eligible
|
|
54700
|
+
`,
|
|
54701
|
+
);
|
|
54702
|
+
return row?.eligible === true;
|
|
54703
|
+
}
|
|
54602
54704
|
|
|
54603
54705
|
/** Bounded per-candidate containment inspection outcomes (metric labels). */
|
|
54604
54706
|
export type CommandContainmentInspection =
|
|
54605
54707
|
| "idle_enrolled"
|
|
54606
54708
|
| "deadline_enrolled"
|
|
54709
|
+
| "quiescence_enrolled"
|
|
54607
54710
|
| "resumed_enrolled"
|
|
54608
54711
|
| "not_eligible"
|
|
54609
54712
|
| "inspection_failed";
|
|
54610
54713
|
|
|
54611
54714
|
export type CommandContainmentEnrollment = ReapDrainable & {
|
|
54612
|
-
/** `idle`/`deadline` newly enrolled this call; `resumed` continues an
|
|
54715
|
+
/** `idle`/`deadline`/`quiescence` newly enrolled this call; `resumed` continues an
|
|
54613
54716
|
* existing enrollment whose drain has not committed yet. */
|
|
54614
|
-
mode: "idle" | "deadline" | "resumed";
|
|
54717
|
+
mode: "idle" | "deadline" | "quiescence" | "resumed";
|
|
54615
54718
|
};
|
|
54616
54719
|
|
|
54617
54720
|
/** Whole-group "unused" from durable facts only. Every session of the group,
|
|
@@ -54814,6 +54917,8 @@ export async function enrollRetainedCommandContainment(
|
|
|
54814
54917
|
sandboxGroupId: string;
|
|
54815
54918
|
/** Omit to evaluate only the provider-deadline rule. */
|
|
54816
54919
|
idleCommandContainmentMs?: number | undefined;
|
|
54920
|
+
/** Internal recovery authority: caller verified exact Temporal settlement. */
|
|
54921
|
+
settledOwner?: SettledCommandOwner;
|
|
54817
54922
|
},
|
|
54818
54923
|
): Promise<CommandContainmentEnrollment | null> {
|
|
54819
54924
|
const screened = await withRlsContext(db, input, async (tx) => {
|
|
@@ -54826,6 +54931,8 @@ export async function enrollRetainedCommandContainment(
|
|
|
54826
54931
|
// Enrolled drains resume; any requested rotation also refreshes command
|
|
54827
54932
|
// observation backoff below and may meet the deadline rule.
|
|
54828
54933
|
(Boolean(lease.unobservableCommandDrainIds?.length) ||
|
|
54934
|
+
(input.settledOwner !== undefined &&
|
|
54935
|
+
(await settledCommandOwnerMatchesTx(tx, input, input.settledOwner))) ||
|
|
54829
54936
|
lease.rotationRequestedAt !== null ||
|
|
54830
54937
|
(input.idleCommandContainmentMs !== undefined &&
|
|
54831
54938
|
(await sandboxGroupIdleForCommandContainmentTx(tx, {
|
|
@@ -54875,11 +54982,17 @@ export async function enrollRetainedCommandContainment(
|
|
|
54875
54982
|
supervised: boolean;
|
|
54876
54983
|
owned: boolean;
|
|
54877
54984
|
deadline_ready: boolean;
|
|
54985
|
+
adopted: boolean;
|
|
54878
54986
|
}>(
|
|
54879
54987
|
tx,
|
|
54880
54988
|
sql`
|
|
54881
54989
|
select process.id, process.session_id, process.owner_attempt_id,
|
|
54882
54990
|
process.parent_admission_id, process.holder_id,
|
|
54991
|
+
exists (select 1 from session_background_commands background
|
|
54992
|
+
where background.retained_process_id = process.id
|
|
54993
|
+
and background.workspace_id = process.workspace_id
|
|
54994
|
+
and background.session_id = process.session_id
|
|
54995
|
+
and background.state in ('running', 'stopping')) as adopted,
|
|
54883
54996
|
(coalesce(process.provider_command, '{}'::jsonb) ? 'supervision') as supervised,
|
|
54884
54997
|
(process.lease_epoch = ${initial.leaseEpoch}
|
|
54885
54998
|
and process.provider_instance_id = ${initial.instanceId}
|
|
@@ -54953,8 +55066,19 @@ export async function enrollRetainedCommandContainment(
|
|
|
54953
55066
|
};
|
|
54954
55067
|
if (enrolled) return { ...target, mode: "resumed" };
|
|
54955
55068
|
if (lease.archive_capture_id !== null) return null;
|
|
54956
|
-
let mode: "idle" | "deadline" | null = null;
|
|
55069
|
+
let mode: "idle" | "deadline" | "quiescence" | null = null;
|
|
54957
55070
|
if (
|
|
55071
|
+
input.settledOwner !== undefined &&
|
|
55072
|
+
processes.every(
|
|
55073
|
+
(p) =>
|
|
55074
|
+
p.owner_attempt_id === input.settledOwner!.attemptId &&
|
|
55075
|
+
p.session_id === input.settledOwner!.sessionId &&
|
|
55076
|
+
!p.adopted,
|
|
55077
|
+
) &&
|
|
55078
|
+
(await settledCommandOwnerMatchesTx(tx, input, input.settledOwner))
|
|
55079
|
+
) {
|
|
55080
|
+
mode = "quiescence";
|
|
55081
|
+
} else if (
|
|
54958
55082
|
deadlineRotation &&
|
|
54959
55083
|
processes.every((p) => p.deadline_ready) &&
|
|
54960
55084
|
!(await deadlineOwnerQuiescencePending(tx, input.workspaceId, processes)) &&
|
|
@@ -54978,9 +55102,11 @@ export async function enrollRetainedCommandContainment(
|
|
|
54978
55102
|
await tx.execute(sql`
|
|
54979
55103
|
update sandbox_leases set unobservable_command_drain_ids = ${`{${ids.join(",")}}`}::uuid[],
|
|
54980
55104
|
command_containment_reason = ${
|
|
54981
|
-
mode === "
|
|
54982
|
-
?
|
|
54983
|
-
:
|
|
55105
|
+
mode === "quiescence"
|
|
55106
|
+
? QUIESCENCE_COMMAND_CONTAINMENT_REASON
|
|
55107
|
+
: mode === "deadline"
|
|
55108
|
+
? DEADLINE_COMMAND_CONTAINMENT_REASON
|
|
55109
|
+
: IDLE_COMMAND_CONTAINMENT_REASON
|
|
54984
55110
|
},
|
|
54985
55111
|
liveness = 'draining', rotation_requested_at = coalesce(rotation_requested_at, now()),
|
|
54986
55112
|
rotation_reason = coalesce(rotation_reason, 'operator'), expires_at = now(), updated_at = now()
|
|
@@ -55336,7 +55462,7 @@ export async function reArmDrainingLease(
|
|
|
55336
55462
|
? snapshot.mode === "credits" && snapshot.rateMicrosPerSecond > 0
|
|
55337
55463
|
: input.warmBilling?.rateMicrosPerSecond !== undefined &&
|
|
55338
55464
|
input.warmBilling.rateMicrosPerSecond > 0) &&
|
|
55339
|
-
(await
|
|
55465
|
+
(await getSpendableCreditBalance(tx, input.accountId)).balanceMicros <= 0
|
|
55340
55466
|
) {
|
|
55341
55467
|
throw new SandboxPaidComputeAdmissionError(input.workspaceId, input.sandboxGroupId);
|
|
55342
55468
|
}
|
|
@@ -55520,7 +55646,8 @@ export async function confirmDrainCold(
|
|
|
55520
55646
|
!input.providerMissingBeforeCapture &&
|
|
55521
55647
|
row.archive_capture_published_at !== null &&
|
|
55522
55648
|
(reason === IDLE_COMMAND_CONTAINMENT_REASON ||
|
|
55523
|
-
reason === DEADLINE_COMMAND_CONTAINMENT_REASON
|
|
55649
|
+
reason === DEADLINE_COMMAND_CONTAINMENT_REASON ||
|
|
55650
|
+
reason === QUIESCENCE_COMMAND_CONTAINMENT_REASON) &&
|
|
55524
55651
|
row.unobservable_command_drain_ids?.length
|
|
55525
55652
|
? row.unobservable_command_drain_ids
|
|
55526
55653
|
: [];
|
|
@@ -68341,6 +68468,16 @@ export async function updateSessionTitleWithEvent(
|
|
|
68341
68468
|
);
|
|
68342
68469
|
}
|
|
68343
68470
|
|
|
68471
|
+
/** Read-only admission at a locked paused-to-active transition. */
|
|
68472
|
+
export type SessionGoalResumeValidation = (
|
|
68473
|
+
tx: Database,
|
|
68474
|
+
session: Pick<
|
|
68475
|
+
Session,
|
|
68476
|
+
"accountId" | "workspaceId" | "model" | "codexCompactionMode" | "latencyMode"
|
|
68477
|
+
>,
|
|
68478
|
+
causalTurn: { id: string; initiatingHumanSubjectId: string | null } | null,
|
|
68479
|
+
) => Promise<void>;
|
|
68480
|
+
|
|
68344
68481
|
/**
|
|
68345
68482
|
* Status transition helper. Idempotent: requesting the current status returns
|
|
68346
68483
|
* `changed: false` so callers can skip emitting a duplicate event. `completed`
|
|
@@ -68360,6 +68497,8 @@ export async function setSessionGoalStatus(
|
|
|
68360
68497
|
reportDeliveries?: SessionGoalReportDelivery[];
|
|
68361
68498
|
reportArtifactActor?: ReportArtifactActor;
|
|
68362
68499
|
commandActor?: Extract<SessionCommandActor, { type: "agent_attempt" }>;
|
|
68500
|
+
/** Runs under the canonical control/session/goal locks, before a paused goal changes. */
|
|
68501
|
+
beforeResume?: SessionGoalResumeValidation;
|
|
68363
68502
|
},
|
|
68364
68503
|
): Promise<{
|
|
68365
68504
|
goal: SessionGoal;
|
|
@@ -68409,6 +68548,51 @@ export async function setSessionGoalStatus(
|
|
|
68409
68548
|
if (existing.status === "completed") {
|
|
68410
68549
|
throw new Error("session goal is completed; set a new goal to continue");
|
|
68411
68550
|
}
|
|
68551
|
+
if (input.status === "active" && input.beforeResume) {
|
|
68552
|
+
const [effectiveSession] = await withEffectiveSessionPolicy(scopedDb, workspaceId, [session]);
|
|
68553
|
+
// An accepted running turn owns the work the goal will continue after it
|
|
68554
|
+
// settles. An idle Resume uses the exact latest-finished causal row.
|
|
68555
|
+
const causalTurnFilter = session.activeTurnId
|
|
68556
|
+
? or(
|
|
68557
|
+
eq(schema.sessionTurns.id, session.activeTurnId),
|
|
68558
|
+
sql`${schema.sessionTurns.finishedAt} is not null`,
|
|
68559
|
+
)
|
|
68560
|
+
: sql`${schema.sessionTurns.finishedAt} is not null`;
|
|
68561
|
+
const [causalTurn] = await scopedDb
|
|
68562
|
+
.select({
|
|
68563
|
+
id: schema.sessionTurns.id,
|
|
68564
|
+
initiatingHumanSubjectId: schema.sessionTurns.initiatingHumanSubjectId,
|
|
68565
|
+
})
|
|
68566
|
+
.from(schema.sessionTurns)
|
|
68567
|
+
.where(
|
|
68568
|
+
and(
|
|
68569
|
+
eq(schema.sessionTurns.workspaceId, workspaceId),
|
|
68570
|
+
eq(schema.sessionTurns.sessionId, sessionId),
|
|
68571
|
+
causalTurnFilter,
|
|
68572
|
+
),
|
|
68573
|
+
)
|
|
68574
|
+
.orderBy(
|
|
68575
|
+
...(session.activeTurnId
|
|
68576
|
+
? [desc(sql`${schema.sessionTurns.id} = ${session.activeTurnId}`)]
|
|
68577
|
+
: []),
|
|
68578
|
+
desc(schema.sessionTurns.finishedAt),
|
|
68579
|
+
desc(schema.sessionTurns.position),
|
|
68580
|
+
desc(schema.sessionTurns.createdAt),
|
|
68581
|
+
desc(schema.sessionTurns.id),
|
|
68582
|
+
)
|
|
68583
|
+
.limit(1);
|
|
68584
|
+
await input.beforeResume(
|
|
68585
|
+
scopedDb,
|
|
68586
|
+
{
|
|
68587
|
+
accountId: session.accountId,
|
|
68588
|
+
workspaceId,
|
|
68589
|
+
model: effectiveSession!.model,
|
|
68590
|
+
latencyMode: effectiveSession!.latencyMode as Session["latencyMode"],
|
|
68591
|
+
codexCompactionMode: session.codexCompactionMode as Session["codexCompactionMode"],
|
|
68592
|
+
},
|
|
68593
|
+
causalTurn ?? null,
|
|
68594
|
+
);
|
|
68595
|
+
}
|
|
68412
68596
|
if (input.status === "completed") {
|
|
68413
68597
|
await verifyGoalReportDeliveries(scopedDb, {
|
|
68414
68598
|
accountId: session.accountId,
|
|
@@ -68502,6 +68686,7 @@ export async function setSessionGoalStatusWithEvent(
|
|
|
68502
68686
|
reportDeliveries?: SessionGoalReportDelivery[];
|
|
68503
68687
|
reportArtifactActor?: ReportArtifactActor;
|
|
68504
68688
|
commandActor?: Extract<SessionCommandActor, { type: "agent_attempt" }>;
|
|
68689
|
+
beforeResume?: SessionGoalResumeValidation;
|
|
68505
68690
|
event: SetSessionGoalStatusEvent;
|
|
68506
68691
|
},
|
|
68507
68692
|
): Promise<{
|
|
@@ -68535,6 +68720,7 @@ export async function setSessionGoalStatusWithEvent(
|
|
|
68535
68720
|
: {}),
|
|
68536
68721
|
...(input.reportArtifactActor ? { reportArtifactActor: input.reportArtifactActor } : {}),
|
|
68537
68722
|
...(input.commandActor ? { commandActor: input.commandActor } : {}),
|
|
68723
|
+
...(input.beforeResume ? { beforeResume: input.beforeResume } : {}),
|
|
68538
68724
|
...(input.evidence !== undefined ? { evidence: input.evidence } : {}),
|
|
68539
68725
|
...(input.rationale !== undefined ? { rationale: input.rationale } : {}),
|
|
68540
68726
|
...(input.pausedReason !== undefined ? { pausedReason: input.pausedReason } : {}),
|
|
@@ -68633,7 +68819,7 @@ export type GoalContinuationDecision =
|
|
|
68633
68819
|
| { decision: "queue" }
|
|
68634
68820
|
| {
|
|
68635
68821
|
decision: "paused";
|
|
68636
|
-
reason: "max_auto_continuations" | "limits" |
|
|
68822
|
+
reason: "max_auto_continuations" | "limits" | GoalAdmissionPausedReason;
|
|
68637
68823
|
goal: SessionGoal;
|
|
68638
68824
|
}
|
|
68639
68825
|
| {
|
|
@@ -68712,7 +68898,7 @@ export async function evaluateGoalContinuation(
|
|
|
68712
68898
|
// decision (before the counter bump) so a budget pause never consumes
|
|
68713
68899
|
// continuation budget.
|
|
68714
68900
|
budgetBlocked?: string | null;
|
|
68715
|
-
budgetPausedReason?: "limits" |
|
|
68901
|
+
budgetPausedReason?: "limits" | GoalAdmissionPausedReason | undefined;
|
|
68716
68902
|
},
|
|
68717
68903
|
): Promise<GoalContinuationDecision> {
|
|
68718
68904
|
return await withWorkspaceRls(
|
|
@@ -68974,7 +69160,7 @@ export async function materializeGoalContinuation(
|
|
|
68974
69160
|
workflowId: string;
|
|
68975
69161
|
defaultMaxAutoContinuations?: number | null;
|
|
68976
69162
|
budgetBlocked?: string | null;
|
|
68977
|
-
budgetPausedReason?: "limits" |
|
|
69163
|
+
budgetPausedReason?: "limits" | GoalAdmissionPausedReason | undefined;
|
|
68978
69164
|
/** Trusted worker admission, evaluated under the same session/goal locks
|
|
68979
69165
|
* as lineage materialization. Never substitutes a mutable latest human. */
|
|
68980
69166
|
admission?: (
|
|
@@ -68982,7 +69168,7 @@ export async function materializeGoalContinuation(
|
|
|
68982
69168
|
causalTurn: { id: string; initiatingHumanSubjectId: string | null } | null,
|
|
68983
69169
|
) => Promise<{
|
|
68984
69170
|
budgetBlocked: string | null;
|
|
68985
|
-
budgetPausedReason?: "limits" |
|
|
69171
|
+
budgetPausedReason?: "limits" | GoalAdmissionPausedReason;
|
|
68986
69172
|
}>;
|
|
68987
69173
|
idleBackoff?: GoalIdleBackoffPolicy | null;
|
|
68988
69174
|
policy: {
|
|
@@ -75299,6 +75485,28 @@ export async function reconcileSessionAttemptQuiescence(
|
|
|
75299
75485
|
};
|
|
75300
75486
|
}
|
|
75301
75487
|
if (!input.activitySettled || eligibility.writer_pending) {
|
|
75488
|
+
if (input.activitySettled && eligibility.writer_pending) {
|
|
75489
|
+
const group = await withRlsContext(db, input, async (tx) => {
|
|
75490
|
+
const [row] = await tx
|
|
75491
|
+
.select({ id: schema.sessions.sandboxGroupId })
|
|
75492
|
+
.from(schema.sessions)
|
|
75493
|
+
.where(
|
|
75494
|
+
and(
|
|
75495
|
+
eq(schema.sessions.workspaceId, input.workspaceId),
|
|
75496
|
+
eq(schema.sessions.id, input.sessionId),
|
|
75497
|
+
),
|
|
75498
|
+
)
|
|
75499
|
+
.limit(1);
|
|
75500
|
+
return row?.id;
|
|
75501
|
+
});
|
|
75502
|
+
if (group)
|
|
75503
|
+
await enrollRetainedCommandContainment(db, {
|
|
75504
|
+
accountId: input.accountId,
|
|
75505
|
+
workspaceId: input.workspaceId,
|
|
75506
|
+
sandboxGroupId: group,
|
|
75507
|
+
settledOwner: input,
|
|
75508
|
+
});
|
|
75509
|
+
}
|
|
75302
75510
|
return { action: "pending", events: [] };
|
|
75303
75511
|
}
|
|
75304
75512
|
const events = await markSessionAttemptQuiesced(db, {
|
|
@@ -78873,6 +79081,8 @@ function metadataWithoutProviderRecoveryCount(
|
|
|
78873
79081
|
): Record<string, unknown> {
|
|
78874
79082
|
const next = { ...(metadata ?? {}) };
|
|
78875
79083
|
delete next.providerRecoveryCount;
|
|
79084
|
+
delete next.providerRecoveryStartedAt;
|
|
79085
|
+
delete next.providerRecoveryReason;
|
|
78876
79086
|
return next;
|
|
78877
79087
|
}
|
|
78878
79088
|
|
|
@@ -81315,7 +81525,15 @@ export async function requestSessionTurnRecovery(
|
|
|
81315
81525
|
}
|
|
81316
81526
|
: {}),
|
|
81317
81527
|
...(input.providerRecoveryCount !== undefined
|
|
81318
|
-
? {
|
|
81528
|
+
? {
|
|
81529
|
+
providerRecoveryCount: input.providerRecoveryCount,
|
|
81530
|
+
providerRecoveryStartedAt:
|
|
81531
|
+
providerRecoveryCountFromTurnMetadata(turn.metadata) > 0 &&
|
|
81532
|
+
typeof turn.metadata?.providerRecoveryStartedAt === "string"
|
|
81533
|
+
? turn.metadata.providerRecoveryStartedAt
|
|
81534
|
+
: now.toISOString(),
|
|
81535
|
+
providerRecoveryReason: input.reason,
|
|
81536
|
+
}
|
|
81319
81537
|
: {}),
|
|
81320
81538
|
},
|
|
81321
81539
|
updatedAt: now,
|
|
@@ -86759,7 +86977,7 @@ async function mapSessionWithControl(
|
|
|
86759
86977
|
},
|
|
86760
86978
|
archive: Pick<Session, "archived" | "archivedAt" | "archiveVersion"> = mapSessionArchive(null),
|
|
86761
86979
|
workspaceControl?: WorkspaceControlRow,
|
|
86762
|
-
tenancyViewer?: { subjectId: string
|
|
86980
|
+
tenancyViewer?: { subjectId: string },
|
|
86763
86981
|
): Promise<Session> {
|
|
86764
86982
|
const controls = await sessionControlProjections(db, row.workspaceId, [row.id], workspaceControl);
|
|
86765
86983
|
const control = controls.get(row.id);
|
|
@@ -86896,7 +87114,7 @@ function mapSession(
|
|
|
86896
87114
|
attentionVersion: 0,
|
|
86897
87115
|
},
|
|
86898
87116
|
archive: Pick<Session, "archived" | "archivedAt" | "archiveVersion"> = mapSessionArchive(null),
|
|
86899
|
-
tenancyViewer?: { subjectId: string
|
|
87117
|
+
tenancyViewer?: { subjectId: string },
|
|
86900
87118
|
): Session {
|
|
86901
87119
|
const bundledSkillIds = bundledSkillSelectionFromMetadata(row.metadata);
|
|
86902
87120
|
return {
|
|
@@ -86930,9 +87148,7 @@ function mapSession(
|
|
|
86930
87148
|
toolPolicyVersion: Number(row.toolPolicyVersion),
|
|
86931
87149
|
mcpApprovalPolicies: row.mcpApprovalPolicies,
|
|
86932
87150
|
metadata: row.metadata,
|
|
86933
|
-
...(tenancyViewer
|
|
86934
|
-
? { tenancy: mapSessionTenancy(row, tenancyViewer.subjectId) }
|
|
86935
|
-
: {}),
|
|
87151
|
+
...(tenancyViewer ? { tenancy: mapSessionTenancy(row, tenancyViewer.subjectId) } : {}),
|
|
86936
87152
|
createdBy: initiatorFromStorage(
|
|
86937
87153
|
row.createdByKind,
|
|
86938
87154
|
row.createdBySubjectId,
|