@opengeni/db 0.13.4 → 0.14.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/{chunk-IQOXTFKA.js → chunk-PQKVUWQW.js} +19 -6
- package/dist/chunk-PQKVUWQW.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +422 -117
- package/dist/index.js.map +1 -1
- package/dist/provision-roles.d.ts +54 -10
- package/dist/{schema-SbaSoRdr.d.ts → schema-FoPub9yt.d.ts} +51 -15
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +1 -1
- package/drizzle/0134_session_first_party_mcp_tools.sql +15 -0
- package/drizzle/0135_durable_machine_input_batches.sql +359 -0
- package/package.json +3 -3
- package/src/index.ts +543 -143
- package/src/schema.ts +25 -6
- package/src/session-queue-commands.ts +22 -14
- package/dist/chunk-IQOXTFKA.js.map +0 -1
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
FileAsset,
|
|
17
17
|
FileStatus,
|
|
18
18
|
FileUploadStatus,
|
|
19
|
+
FirstPartyMcpToolName,
|
|
19
20
|
HumanInputAnswer,
|
|
20
21
|
HumanInputQuestion,
|
|
21
22
|
HumanInputResponse,
|
|
@@ -113,6 +114,7 @@ import {
|
|
|
113
114
|
RigChange as RigChangeContract,
|
|
114
115
|
SessionGoal as SessionGoalContract,
|
|
115
116
|
SessionSystemUpdatePayload,
|
|
117
|
+
sessionSystemUpdateBatchHistoryItem,
|
|
116
118
|
HostEventExport as HostEventExportContract,
|
|
117
119
|
HostEventExportBatch as HostEventExportBatchContract,
|
|
118
120
|
HostExportConsumerId,
|
|
@@ -13888,6 +13890,7 @@ export type SessionCreateInput = {
|
|
|
13888
13890
|
rigId?: string | null;
|
|
13889
13891
|
rigVersionId?: string | null;
|
|
13890
13892
|
firstPartyMcpPermissions?: Permission[] | null;
|
|
13893
|
+
firstPartyMcpTools?: FirstPartyMcpToolName[] | null;
|
|
13891
13894
|
instructions?: string | null;
|
|
13892
13895
|
parentSessionId?: string | null;
|
|
13893
13896
|
createIdempotencyKey?: string | null;
|
|
@@ -14288,6 +14291,7 @@ async function createSessionInTransaction(
|
|
|
14288
14291
|
rigId: input.rigId ?? null,
|
|
14289
14292
|
rigVersionId: input.rigVersionId ?? null,
|
|
14290
14293
|
firstPartyMcpPermissions: input.firstPartyMcpPermissions ?? null,
|
|
14294
|
+
firstPartyMcpTools: input.firstPartyMcpTools ?? null,
|
|
14291
14295
|
instructions: input.instructions ?? null,
|
|
14292
14296
|
parentSessionId: input.parentSessionId ?? null,
|
|
14293
14297
|
createIdempotencyKey,
|
|
@@ -28985,7 +28989,7 @@ export async function materializeGoalContinuation(
|
|
|
28985
28989
|
eq(schema.sessionSystemUpdates.workspaceId, input.workspaceId),
|
|
28986
28990
|
eq(schema.sessionSystemUpdates.sessionId, input.sessionId),
|
|
28987
28991
|
eq(schema.sessionSystemUpdates.kind, "agent_steer_instruction"),
|
|
28988
|
-
|
|
28992
|
+
eq(schema.sessionSystemUpdates.state, "pending"),
|
|
28989
28993
|
),
|
|
28990
28994
|
)
|
|
28991
28995
|
.limit(1);
|
|
@@ -29148,6 +29152,7 @@ export async function materializeGoalContinuation(
|
|
|
29148
29152
|
})
|
|
29149
29153
|
.onConflictDoNothing({ target: schema.usageEvents.idempotencyKey });
|
|
29150
29154
|
|
|
29155
|
+
const eventPreview = internalUpdateEventMember(update);
|
|
29151
29156
|
const insertedEvents = await tx
|
|
29152
29157
|
.insert(schema.sessionEvents)
|
|
29153
29158
|
.values([
|
|
@@ -29158,11 +29163,13 @@ export async function materializeGoalContinuation(
|
|
|
29158
29163
|
sequence: session.lastSequence + 1,
|
|
29159
29164
|
type: "system.update.pending",
|
|
29160
29165
|
payload: sanitizeEventPayload({
|
|
29161
|
-
updateId:
|
|
29162
|
-
kind:
|
|
29163
|
-
classification:
|
|
29164
|
-
sourceId:
|
|
29165
|
-
|
|
29166
|
+
updateId: eventPreview.id,
|
|
29167
|
+
kind: eventPreview.kind,
|
|
29168
|
+
classification: eventPreview.classification,
|
|
29169
|
+
sourceId: eventPreview.sourceId,
|
|
29170
|
+
sourceIdTruncated: eventPreview.sourceIdTruncated,
|
|
29171
|
+
summary: eventPreview.summary,
|
|
29172
|
+
summaryTruncated: eventPreview.summaryTruncated,
|
|
29166
29173
|
}),
|
|
29167
29174
|
occurredAt: now,
|
|
29168
29175
|
},
|
|
@@ -29663,6 +29670,79 @@ export type SessionWorkTrigger = { kind: "next" } | { kind: "approval"; triggerE
|
|
|
29663
29670
|
export const MAX_INTERNAL_UPDATE_BYTES = 64 * 1024;
|
|
29664
29671
|
export const MAX_INTERNAL_UPDATE_BATCH_MEMBERS = 100;
|
|
29665
29672
|
export const MAX_INTERNAL_UPDATE_BATCH_BYTES = 256 * 1024;
|
|
29673
|
+
const MAX_INTERNAL_UPDATE_EVENT_SUMMARY_BYTES = 512;
|
|
29674
|
+
const MAX_INTERNAL_UPDATE_EVENT_SOURCE_BYTES = 256;
|
|
29675
|
+
|
|
29676
|
+
type BoundedSystemUpdate = Pick<
|
|
29677
|
+
typeof schema.sessionSystemUpdates.$inferSelect,
|
|
29678
|
+
"id" | "kind" | "classification" | "sourceId" | "summary" | "payload" | "lineage"
|
|
29679
|
+
>;
|
|
29680
|
+
|
|
29681
|
+
function boundedInternalUpdateEventText(
|
|
29682
|
+
value: string,
|
|
29683
|
+
maxBytes: number,
|
|
29684
|
+
): {
|
|
29685
|
+
text: string;
|
|
29686
|
+
truncated: boolean;
|
|
29687
|
+
} {
|
|
29688
|
+
if (Buffer.byteLength(value) <= maxBytes) return { text: value, truncated: false };
|
|
29689
|
+
const suffix = "…";
|
|
29690
|
+
const bodyBudget = maxBytes - Buffer.byteLength(suffix);
|
|
29691
|
+
const bytes = Buffer.from(value);
|
|
29692
|
+
let text = bytes.subarray(0, bodyBudget).toString("utf8");
|
|
29693
|
+
if (text.endsWith("\uFFFD")) text = text.slice(0, -1);
|
|
29694
|
+
return { text: `${text}${suffix}`, truncated: true };
|
|
29695
|
+
}
|
|
29696
|
+
|
|
29697
|
+
function internalUpdateEventMember(update: BoundedSystemUpdate) {
|
|
29698
|
+
const summary = boundedInternalUpdateEventText(
|
|
29699
|
+
update.summary,
|
|
29700
|
+
MAX_INTERNAL_UPDATE_EVENT_SUMMARY_BYTES,
|
|
29701
|
+
);
|
|
29702
|
+
const source = boundedInternalUpdateEventText(
|
|
29703
|
+
update.sourceId,
|
|
29704
|
+
MAX_INTERNAL_UPDATE_EVENT_SOURCE_BYTES,
|
|
29705
|
+
);
|
|
29706
|
+
return {
|
|
29707
|
+
id: update.id,
|
|
29708
|
+
kind: update.kind,
|
|
29709
|
+
classification: update.classification,
|
|
29710
|
+
sourceId: source.text,
|
|
29711
|
+
sourceIdTruncated: source.truncated,
|
|
29712
|
+
summary: summary.text,
|
|
29713
|
+
summaryTruncated: summary.truncated,
|
|
29714
|
+
};
|
|
29715
|
+
}
|
|
29716
|
+
|
|
29717
|
+
function selectBoundedSystemUpdateBatch<T extends BoundedSystemUpdate>(updates: readonly T[]): T[] {
|
|
29718
|
+
const selected: T[] = [];
|
|
29719
|
+
let selectedBytes = 0;
|
|
29720
|
+
for (const update of updates) {
|
|
29721
|
+
const updateBytes = Buffer.byteLength(
|
|
29722
|
+
JSON.stringify({
|
|
29723
|
+
id: update.id,
|
|
29724
|
+
kind: update.kind,
|
|
29725
|
+
classification: update.classification,
|
|
29726
|
+
sourceId: update.sourceId,
|
|
29727
|
+
summary: update.summary,
|
|
29728
|
+
payload: update.payload,
|
|
29729
|
+
lineage: update.lineage,
|
|
29730
|
+
}),
|
|
29731
|
+
);
|
|
29732
|
+
if (
|
|
29733
|
+
selected.length >= MAX_INTERNAL_UPDATE_BATCH_MEMBERS ||
|
|
29734
|
+
// One individually large canonical input must still make progress. The
|
|
29735
|
+
// model/context boundary may reject it explicitly, but the queue cannot
|
|
29736
|
+
// wedge forever merely because the coalescing target is smaller.
|
|
29737
|
+
(selected.length > 0 && selectedBytes + updateBytes > MAX_INTERNAL_UPDATE_BATCH_BYTES)
|
|
29738
|
+
) {
|
|
29739
|
+
break;
|
|
29740
|
+
}
|
|
29741
|
+
selected.push(update);
|
|
29742
|
+
selectedBytes += updateBytes;
|
|
29743
|
+
}
|
|
29744
|
+
return selected;
|
|
29745
|
+
}
|
|
29666
29746
|
|
|
29667
29747
|
export type ClaimSessionWorkForAttemptInput = {
|
|
29668
29748
|
sessionId: string;
|
|
@@ -29708,7 +29788,10 @@ export async function claimSessionWorkForAttempt(
|
|
|
29708
29788
|
count: number;
|
|
29709
29789
|
lastSequence: number;
|
|
29710
29790
|
triggerEventId: string | null;
|
|
29791
|
+
historyItemId: string | null;
|
|
29792
|
+
historyItem: Record<string, unknown> | null;
|
|
29711
29793
|
updates: Array<typeof schema.sessionSystemUpdates.$inferSelect>;
|
|
29794
|
+
events: Array<typeof schema.sessionEvents.$inferInsert>;
|
|
29712
29795
|
event: typeof schema.sessionEvents.$inferInsert | null;
|
|
29713
29796
|
}> => {
|
|
29714
29797
|
const [agentSteer] = await tx
|
|
@@ -29718,7 +29801,7 @@ export async function claimSessionWorkForAttempt(
|
|
|
29718
29801
|
and(
|
|
29719
29802
|
eq(schema.sessionSystemUpdates.workspaceId, workspaceId),
|
|
29720
29803
|
eq(schema.sessionSystemUpdates.sessionId, sessionId),
|
|
29721
|
-
|
|
29804
|
+
eq(schema.sessionSystemUpdates.state, "pending"),
|
|
29722
29805
|
eq(schema.sessionSystemUpdates.kind, "agent_steer_instruction"),
|
|
29723
29806
|
),
|
|
29724
29807
|
)
|
|
@@ -29728,20 +29811,6 @@ export async function claimSessionWorkForAttempt(
|
|
|
29728
29811
|
)
|
|
29729
29812
|
.limit(1)
|
|
29730
29813
|
.for("update");
|
|
29731
|
-
if (agentSteer) {
|
|
29732
|
-
await tx
|
|
29733
|
-
.update(schema.sessionSystemUpdates)
|
|
29734
|
-
.set({ state: "superseded" })
|
|
29735
|
-
.where(
|
|
29736
|
-
and(
|
|
29737
|
-
eq(schema.sessionSystemUpdates.workspaceId, workspaceId),
|
|
29738
|
-
eq(schema.sessionSystemUpdates.sessionId, sessionId),
|
|
29739
|
-
eq(schema.sessionSystemUpdates.kind, "agent_steer_instruction"),
|
|
29740
|
-
inArray(schema.sessionSystemUpdates.state, ["pending", "deferred"]),
|
|
29741
|
-
ne(schema.sessionSystemUpdates.id, agentSteer.id),
|
|
29742
|
-
),
|
|
29743
|
-
);
|
|
29744
|
-
}
|
|
29745
29814
|
const ordinary = await tx
|
|
29746
29815
|
.select()
|
|
29747
29816
|
.from(schema.sessionSystemUpdates)
|
|
@@ -29749,7 +29818,7 @@ export async function claimSessionWorkForAttempt(
|
|
|
29749
29818
|
and(
|
|
29750
29819
|
eq(schema.sessionSystemUpdates.workspaceId, workspaceId),
|
|
29751
29820
|
eq(schema.sessionSystemUpdates.sessionId, sessionId),
|
|
29752
|
-
|
|
29821
|
+
eq(schema.sessionSystemUpdates.state, "pending"),
|
|
29753
29822
|
ne(schema.sessionSystemUpdates.kind, "agent_steer_instruction"),
|
|
29754
29823
|
),
|
|
29755
29824
|
)
|
|
@@ -29765,12 +29834,15 @@ export async function claimSessionWorkForAttempt(
|
|
|
29765
29834
|
count: 0,
|
|
29766
29835
|
lastSequence: nextSequence - 1,
|
|
29767
29836
|
triggerEventId: null,
|
|
29837
|
+
historyItemId: null,
|
|
29838
|
+
historyItem: null,
|
|
29768
29839
|
updates: [],
|
|
29840
|
+
events: [],
|
|
29769
29841
|
event: null,
|
|
29770
29842
|
};
|
|
29771
29843
|
}
|
|
29772
|
-
const
|
|
29773
|
-
|
|
29844
|
+
const validUpdates: typeof updates = [];
|
|
29845
|
+
const cancelledUpdateIds: string[] = [];
|
|
29774
29846
|
for (const update of updates) {
|
|
29775
29847
|
const payload = update.payload;
|
|
29776
29848
|
if (payload.type === "goal_continuation") {
|
|
@@ -29799,43 +29871,66 @@ export async function claimSessionWorkForAttempt(
|
|
|
29799
29871
|
.update(schema.sessionSystemUpdates)
|
|
29800
29872
|
.set({ state: "cancelled" })
|
|
29801
29873
|
.where(eq(schema.sessionSystemUpdates.id, update.id));
|
|
29874
|
+
cancelledUpdateIds.push(update.id);
|
|
29802
29875
|
continue;
|
|
29803
29876
|
}
|
|
29804
29877
|
}
|
|
29805
|
-
|
|
29806
|
-
JSON.stringify({
|
|
29807
|
-
id: update.id,
|
|
29808
|
-
kind: update.kind,
|
|
29809
|
-
classification: update.classification,
|
|
29810
|
-
sourceId: update.sourceId,
|
|
29811
|
-
summary: update.summary,
|
|
29812
|
-
payload: update.payload,
|
|
29813
|
-
lineage: update.lineage,
|
|
29814
|
-
}),
|
|
29815
|
-
);
|
|
29816
|
-
if (
|
|
29817
|
-
deliverable.length >= MAX_INTERNAL_UPDATE_BATCH_MEMBERS ||
|
|
29818
|
-
deliveredBytes + updateBytes > MAX_INTERNAL_UPDATE_BATCH_BYTES
|
|
29819
|
-
) {
|
|
29820
|
-
break;
|
|
29821
|
-
}
|
|
29822
|
-
deliverable.push(update);
|
|
29823
|
-
deliveredBytes += updateBytes;
|
|
29878
|
+
validUpdates.push(update);
|
|
29824
29879
|
}
|
|
29880
|
+
const deliverable = selectBoundedSystemUpdateBatch(validUpdates);
|
|
29825
29881
|
if (deliverable.length === 0) {
|
|
29882
|
+
const cancellationEvent =
|
|
29883
|
+
cancelledUpdateIds.length > 0
|
|
29884
|
+
? {
|
|
29885
|
+
accountId,
|
|
29886
|
+
workspaceId,
|
|
29887
|
+
sessionId,
|
|
29888
|
+
// No receiving turn exists when every candidate was
|
|
29889
|
+
// cancelled before a model batch could be persisted.
|
|
29890
|
+
turnId: null,
|
|
29891
|
+
turnGeneration: null,
|
|
29892
|
+
turnAttemptId: null,
|
|
29893
|
+
turnAssociation: null,
|
|
29894
|
+
sequence: nextSequence,
|
|
29895
|
+
type: "system.update.cancelled" as const,
|
|
29896
|
+
payload: sanitizeEventPayload({
|
|
29897
|
+
updateIds: cancelledUpdateIds,
|
|
29898
|
+
count: cancelledUpdateIds.length,
|
|
29899
|
+
reason: "stale_goal_continuation",
|
|
29900
|
+
}),
|
|
29901
|
+
occurredAt,
|
|
29902
|
+
}
|
|
29903
|
+
: null;
|
|
29826
29904
|
return {
|
|
29827
29905
|
count: 0,
|
|
29828
|
-
lastSequence: nextSequence - 1,
|
|
29906
|
+
lastSequence: cancellationEvent ? nextSequence : nextSequence - 1,
|
|
29829
29907
|
triggerEventId: null,
|
|
29908
|
+
historyItemId: null,
|
|
29909
|
+
historyItem: null,
|
|
29830
29910
|
updates: [],
|
|
29911
|
+
events: cancellationEvent ? [cancellationEvent] : [],
|
|
29831
29912
|
event: null,
|
|
29832
29913
|
};
|
|
29833
29914
|
}
|
|
29915
|
+
// Inclusion gives the newest Steer first refusal on the bounded
|
|
29916
|
+
// batch. Model ordering is deliberately the opposite: ordinary
|
|
29917
|
+
// updates establish context, then the authoritative replacement
|
|
29918
|
+
// direction is last so it cannot be overridden by an older goal or
|
|
29919
|
+
// lifecycle notice.
|
|
29920
|
+
const modelOrdered = [
|
|
29921
|
+
...deliverable.filter((update) => update.kind !== "agent_steer_instruction"),
|
|
29922
|
+
...deliverable.filter((update) => update.kind === "agent_steer_instruction"),
|
|
29923
|
+
];
|
|
29924
|
+
const historyItemId = crypto.randomUUID();
|
|
29925
|
+
const historyItem = sessionSystemUpdateBatchHistoryItem(
|
|
29926
|
+
modelOrdered.map((update) => mapSessionSystemUpdate(update)),
|
|
29927
|
+
) as Record<string, unknown>;
|
|
29834
29928
|
await tx
|
|
29835
29929
|
.update(schema.sessionSystemUpdates)
|
|
29836
29930
|
.set({
|
|
29837
29931
|
state: "delivered",
|
|
29838
29932
|
deliveredTurnId: turnId,
|
|
29933
|
+
deliveredHistoryItemId: historyItemId,
|
|
29839
29934
|
deliveredAt: occurredAt,
|
|
29840
29935
|
})
|
|
29841
29936
|
.where(
|
|
@@ -29849,6 +29944,27 @@ export async function claimSessionWorkForAttempt(
|
|
|
29849
29944
|
),
|
|
29850
29945
|
);
|
|
29851
29946
|
const eventId = triggerEventId ?? crypto.randomUUID();
|
|
29947
|
+
let sequence = nextSequence - 1;
|
|
29948
|
+
const events: Array<typeof schema.sessionEvents.$inferInsert> = [];
|
|
29949
|
+
if (cancelledUpdateIds.length > 0) {
|
|
29950
|
+
events.push({
|
|
29951
|
+
accountId,
|
|
29952
|
+
workspaceId,
|
|
29953
|
+
sessionId,
|
|
29954
|
+
turnId,
|
|
29955
|
+
turnGeneration,
|
|
29956
|
+
turnAttemptId: input.attemptId,
|
|
29957
|
+
turnAssociation: "current",
|
|
29958
|
+
sequence: ++sequence,
|
|
29959
|
+
type: "system.update.cancelled",
|
|
29960
|
+
payload: sanitizeEventPayload({
|
|
29961
|
+
updateIds: cancelledUpdateIds,
|
|
29962
|
+
count: cancelledUpdateIds.length,
|
|
29963
|
+
reason: "stale_goal_continuation",
|
|
29964
|
+
}),
|
|
29965
|
+
occurredAt,
|
|
29966
|
+
});
|
|
29967
|
+
}
|
|
29852
29968
|
const event: typeof schema.sessionEvents.$inferInsert = {
|
|
29853
29969
|
id: eventId,
|
|
29854
29970
|
accountId,
|
|
@@ -29858,24 +29974,64 @@ export async function claimSessionWorkForAttempt(
|
|
|
29858
29974
|
turnGeneration,
|
|
29859
29975
|
turnAttemptId: input.attemptId,
|
|
29860
29976
|
turnAssociation: "current",
|
|
29861
|
-
sequence:
|
|
29977
|
+
sequence: ++sequence,
|
|
29862
29978
|
type: "system.update.delivered",
|
|
29863
29979
|
payload: sanitizeEventPayload({
|
|
29864
29980
|
updateIds: deliverable.map((update) => update.id),
|
|
29981
|
+
historyItemId,
|
|
29865
29982
|
count: deliverable.length,
|
|
29866
29983
|
classifications: [...new Set(deliverable.map((update) => update.classification))],
|
|
29984
|
+
members: modelOrdered.map(internalUpdateEventMember),
|
|
29867
29985
|
}),
|
|
29868
29986
|
occurredAt,
|
|
29869
29987
|
};
|
|
29988
|
+
events.push(event);
|
|
29870
29989
|
return {
|
|
29871
29990
|
count: deliverable.length,
|
|
29872
|
-
lastSequence:
|
|
29991
|
+
lastSequence: sequence,
|
|
29873
29992
|
triggerEventId: eventId,
|
|
29993
|
+
historyItemId,
|
|
29994
|
+
historyItem,
|
|
29874
29995
|
updates: deliverable,
|
|
29996
|
+
events,
|
|
29875
29997
|
event,
|
|
29876
29998
|
};
|
|
29877
29999
|
};
|
|
29878
30000
|
|
|
30001
|
+
const persistDeliveredUpdateBatch = async (
|
|
30002
|
+
delivered: Awaited<ReturnType<typeof deliverPendingUpdates>>,
|
|
30003
|
+
accountId: string,
|
|
30004
|
+
turnId: string,
|
|
30005
|
+
): Promise<void> => {
|
|
30006
|
+
if (!delivered.historyItemId || !delivered.historyItem) {
|
|
30007
|
+
if (delivered.count !== 0) {
|
|
30008
|
+
throw new Error("Delivered machine-input batch has no model-memory item");
|
|
30009
|
+
}
|
|
30010
|
+
return;
|
|
30011
|
+
}
|
|
30012
|
+
const [{ position } = { position: 0 }] = await tx
|
|
30013
|
+
.select({
|
|
30014
|
+
position: sql<number>`coalesce(max(${schema.sessionHistoryItems.position}), -1) + 1`,
|
|
30015
|
+
})
|
|
30016
|
+
.from(schema.sessionHistoryItems)
|
|
30017
|
+
.where(
|
|
30018
|
+
and(
|
|
30019
|
+
eq(schema.sessionHistoryItems.workspaceId, workspaceId),
|
|
30020
|
+
eq(schema.sessionHistoryItems.sessionId, sessionId),
|
|
30021
|
+
),
|
|
30022
|
+
);
|
|
30023
|
+
await tx.insert(schema.sessionHistoryItems).values({
|
|
30024
|
+
id: delivered.historyItemId,
|
|
30025
|
+
accountId,
|
|
30026
|
+
workspaceId,
|
|
30027
|
+
sessionId,
|
|
30028
|
+
turnId,
|
|
30029
|
+
position: Number(position),
|
|
30030
|
+
item: sanitizeModelPayload(delivered.historyItem),
|
|
30031
|
+
producerCodexCredentialId: null,
|
|
30032
|
+
});
|
|
30033
|
+
};
|
|
30034
|
+
|
|
29879
30035
|
// Capacity settlement and resume use session -> turn after their
|
|
29880
30036
|
// workspace rotation lock. Claiming must preserve that shared order:
|
|
29881
30037
|
// taking a queued turn first can deadlock with a settlement that owns
|
|
@@ -30211,7 +30367,7 @@ export async function claimSessionWorkForAttempt(
|
|
|
30211
30367
|
eq(schema.sessionSystemUpdates.workspaceId, workspaceId),
|
|
30212
30368
|
eq(schema.sessionSystemUpdates.sessionId, sessionId),
|
|
30213
30369
|
eq(schema.sessionSystemUpdates.kind, "agent_steer_instruction"),
|
|
30214
|
-
|
|
30370
|
+
eq(schema.sessionSystemUpdates.state, "pending"),
|
|
30215
30371
|
),
|
|
30216
30372
|
)
|
|
30217
30373
|
.orderBy(
|
|
@@ -30448,6 +30604,22 @@ export async function claimSessionWorkForAttempt(
|
|
|
30448
30604
|
triggerEventId,
|
|
30449
30605
|
);
|
|
30450
30606
|
if (delivered.count === 0) {
|
|
30607
|
+
if (delivered.events.length > 0) {
|
|
30608
|
+
await tx.insert(schema.sessionEvents).values(delivered.events);
|
|
30609
|
+
await tx
|
|
30610
|
+
.update(schema.sessions)
|
|
30611
|
+
.set({
|
|
30612
|
+
status: "idle",
|
|
30613
|
+
lastSequence: delivered.lastSequence,
|
|
30614
|
+
updatedAt: now,
|
|
30615
|
+
})
|
|
30616
|
+
.where(
|
|
30617
|
+
and(
|
|
30618
|
+
eq(schema.sessions.workspaceId, workspaceId),
|
|
30619
|
+
eq(schema.sessions.id, sessionId),
|
|
30620
|
+
),
|
|
30621
|
+
);
|
|
30622
|
+
}
|
|
30451
30623
|
return { action: "unclaimed", reason: "no-work" };
|
|
30452
30624
|
}
|
|
30453
30625
|
const goalUpdate = delivered.updates.find(
|
|
@@ -30634,9 +30806,10 @@ export async function claimSessionWorkForAttempt(
|
|
|
30634
30806
|
})
|
|
30635
30807
|
.returning();
|
|
30636
30808
|
if (!internalTurn) throw new Error("Failed to create internal update inference");
|
|
30809
|
+
await persistDeliveredUpdateBatch(delivered, session.accountId, internalTurn.id);
|
|
30637
30810
|
await registerAttempt(internalTurn);
|
|
30638
30811
|
if (!delivered.event) throw new Error("Delivered update batch has no durable event");
|
|
30639
|
-
await tx.insert(schema.sessionEvents).values(delivered.
|
|
30812
|
+
await tx.insert(schema.sessionEvents).values(delivered.events);
|
|
30640
30813
|
if (goalUpdate && typeof goalUpdate.payload.goalId === "string") {
|
|
30641
30814
|
await tx
|
|
30642
30815
|
.update(schema.sessionGoals)
|
|
@@ -30753,8 +30926,9 @@ export async function claimSessionWorkForAttempt(
|
|
|
30753
30926
|
session.lastSequence + 1,
|
|
30754
30927
|
now,
|
|
30755
30928
|
);
|
|
30756
|
-
|
|
30757
|
-
|
|
30929
|
+
await persistDeliveredUpdateBatch(delivered, session.accountId, row.id);
|
|
30930
|
+
if (delivered.events.length > 0) {
|
|
30931
|
+
await tx.insert(schema.sessionEvents).values(delivered.events);
|
|
30758
30932
|
}
|
|
30759
30933
|
await tx
|
|
30760
30934
|
.update(schema.sessions)
|
|
@@ -30998,6 +31172,196 @@ export async function markSessionAttemptQuiesced(
|
|
|
30998
31172
|
});
|
|
30999
31173
|
}
|
|
31000
31174
|
|
|
31175
|
+
export type ReconcileSessionAttemptQuiescenceResult =
|
|
31176
|
+
| { action: "quiesced"; events: SessionEvent[] }
|
|
31177
|
+
| { action: "pending"; events: [] }
|
|
31178
|
+
| { action: "stale"; events: [] };
|
|
31179
|
+
|
|
31180
|
+
export type SessionAttemptActivityRef = {
|
|
31181
|
+
workflowId: string;
|
|
31182
|
+
workflowRunId: string;
|
|
31183
|
+
activityId: string;
|
|
31184
|
+
quiesced: boolean;
|
|
31185
|
+
};
|
|
31186
|
+
|
|
31187
|
+
export async function getSessionAttemptActivityRef(
|
|
31188
|
+
db: Database,
|
|
31189
|
+
input: {
|
|
31190
|
+
accountId: string;
|
|
31191
|
+
workspaceId: string;
|
|
31192
|
+
sessionId: string;
|
|
31193
|
+
attemptId: string;
|
|
31194
|
+
temporalWorkflowId: string;
|
|
31195
|
+
},
|
|
31196
|
+
): Promise<SessionAttemptActivityRef | null> {
|
|
31197
|
+
return await withRlsContext(
|
|
31198
|
+
db,
|
|
31199
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
31200
|
+
async (scopedDb) => {
|
|
31201
|
+
const [attempt] = await scopedDb
|
|
31202
|
+
.select({
|
|
31203
|
+
workflowId: schema.sessionTurnAttempts.temporalWorkflowId,
|
|
31204
|
+
workflowRunId: schema.sessionTurnAttempts.temporalWorkflowRunId,
|
|
31205
|
+
activityId: schema.sessionTurnAttempts.temporalActivityId,
|
|
31206
|
+
quiescedAt: schema.sessionTurnAttempts.quiescedAt,
|
|
31207
|
+
})
|
|
31208
|
+
.from(schema.sessionTurnAttempts)
|
|
31209
|
+
.where(
|
|
31210
|
+
and(
|
|
31211
|
+
eq(schema.sessionTurnAttempts.accountId, input.accountId),
|
|
31212
|
+
eq(schema.sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
31213
|
+
eq(schema.sessionTurnAttempts.sessionId, input.sessionId),
|
|
31214
|
+
eq(schema.sessionTurnAttempts.id, input.attemptId),
|
|
31215
|
+
eq(schema.sessionTurnAttempts.temporalWorkflowId, input.temporalWorkflowId),
|
|
31216
|
+
),
|
|
31217
|
+
)
|
|
31218
|
+
.limit(1);
|
|
31219
|
+
return attempt
|
|
31220
|
+
? {
|
|
31221
|
+
workflowId: attempt.workflowId,
|
|
31222
|
+
workflowRunId: attempt.workflowRunId,
|
|
31223
|
+
activityId: attempt.activityId,
|
|
31224
|
+
quiesced: attempt.quiescedAt !== null,
|
|
31225
|
+
}
|
|
31226
|
+
: null;
|
|
31227
|
+
},
|
|
31228
|
+
);
|
|
31229
|
+
}
|
|
31230
|
+
|
|
31231
|
+
/**
|
|
31232
|
+
* Recover the quiescence receipt when the original activity disappeared after
|
|
31233
|
+
* its attempt was durably interrupted. The caller first proves through
|
|
31234
|
+
* Temporal that the exact activity is absent or its server-owned heartbeat
|
|
31235
|
+
* lease expired. The closed attempt then cannot admit another workspace writer,
|
|
31236
|
+
* and every writer it did admit (including retained-process child writes) must
|
|
31237
|
+
* carry a physical settlement before the ordinary receipt transaction is
|
|
31238
|
+
* allowed to run.
|
|
31239
|
+
*/
|
|
31240
|
+
export async function reconcileSessionAttemptQuiescence(
|
|
31241
|
+
db: Database,
|
|
31242
|
+
input: {
|
|
31243
|
+
accountId: string;
|
|
31244
|
+
workspaceId: string;
|
|
31245
|
+
sessionId: string;
|
|
31246
|
+
attemptId: string;
|
|
31247
|
+
temporalWorkflowId: string;
|
|
31248
|
+
temporalWorkflowRunId: string;
|
|
31249
|
+
temporalActivityId: string;
|
|
31250
|
+
activitySettled: boolean;
|
|
31251
|
+
},
|
|
31252
|
+
): Promise<ReconcileSessionAttemptQuiescenceResult> {
|
|
31253
|
+
const eligibility = await withRlsContext(
|
|
31254
|
+
db,
|
|
31255
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
31256
|
+
async (scopedDb) => {
|
|
31257
|
+
const rows = await scopedDb.execute<{
|
|
31258
|
+
account_id: string;
|
|
31259
|
+
state: string;
|
|
31260
|
+
quiesced_at: Date | string | null;
|
|
31261
|
+
temporal_workflow_id: string;
|
|
31262
|
+
temporal_workflow_run_id: string;
|
|
31263
|
+
temporal_activity_id: string;
|
|
31264
|
+
interruption_settled: boolean;
|
|
31265
|
+
interruption_pending: boolean;
|
|
31266
|
+
writer_pending: boolean;
|
|
31267
|
+
}>(sql`
|
|
31268
|
+
select
|
|
31269
|
+
attempt.account_id,
|
|
31270
|
+
attempt.state,
|
|
31271
|
+
attempt.quiesced_at,
|
|
31272
|
+
attempt.temporal_workflow_id,
|
|
31273
|
+
attempt.temporal_workflow_run_id,
|
|
31274
|
+
attempt.temporal_activity_id,
|
|
31275
|
+
exists (
|
|
31276
|
+
select 1
|
|
31277
|
+
from session_attempt_interruptions interruption
|
|
31278
|
+
where interruption.workspace_id = attempt.workspace_id
|
|
31279
|
+
and interruption.session_id = attempt.session_id
|
|
31280
|
+
and interruption.attempt_id = attempt.id
|
|
31281
|
+
and interruption.state in ('settled', 'rejected_stale')
|
|
31282
|
+
) as interruption_settled,
|
|
31283
|
+
exists (
|
|
31284
|
+
select 1
|
|
31285
|
+
from session_attempt_interruptions interruption
|
|
31286
|
+
where interruption.workspace_id = attempt.workspace_id
|
|
31287
|
+
and interruption.session_id = attempt.session_id
|
|
31288
|
+
and interruption.attempt_id = attempt.id
|
|
31289
|
+
and interruption.state in ('pending', 'delivered', 'acknowledged')
|
|
31290
|
+
) as interruption_pending,
|
|
31291
|
+
(
|
|
31292
|
+
exists (
|
|
31293
|
+
select 1
|
|
31294
|
+
from sandbox_workspace_mutation_admissions admission
|
|
31295
|
+
where admission.account_id = attempt.account_id
|
|
31296
|
+
and admission.workspace_id = attempt.workspace_id
|
|
31297
|
+
and admission.session_id = attempt.session_id
|
|
31298
|
+
and admission.settled_at is null
|
|
31299
|
+
and (
|
|
31300
|
+
admission.attempt_id = attempt.id
|
|
31301
|
+
or (
|
|
31302
|
+
admission.actor_kind = 'process'
|
|
31303
|
+
and exists (
|
|
31304
|
+
select 1
|
|
31305
|
+
from sandbox_retained_processes process
|
|
31306
|
+
where process.account_id = attempt.account_id
|
|
31307
|
+
and process.workspace_id = attempt.workspace_id
|
|
31308
|
+
and process.session_id = attempt.session_id
|
|
31309
|
+
and process.id = admission.actor_id
|
|
31310
|
+
and process.owner_attempt_id = attempt.id
|
|
31311
|
+
)
|
|
31312
|
+
)
|
|
31313
|
+
)
|
|
31314
|
+
)
|
|
31315
|
+
or exists (
|
|
31316
|
+
select 1
|
|
31317
|
+
from sandbox_retained_processes process
|
|
31318
|
+
where process.account_id = attempt.account_id
|
|
31319
|
+
and process.workspace_id = attempt.workspace_id
|
|
31320
|
+
and process.session_id = attempt.session_id
|
|
31321
|
+
and process.owner_attempt_id = attempt.id
|
|
31322
|
+
and process.state = 'active'
|
|
31323
|
+
)
|
|
31324
|
+
) as writer_pending
|
|
31325
|
+
from session_turn_attempts attempt
|
|
31326
|
+
where attempt.account_id = ${input.accountId}
|
|
31327
|
+
and attempt.workspace_id = ${input.workspaceId}
|
|
31328
|
+
and attempt.session_id = ${input.sessionId}
|
|
31329
|
+
and attempt.id = ${input.attemptId}
|
|
31330
|
+
limit 1
|
|
31331
|
+
`);
|
|
31332
|
+
return rows[0] ?? null;
|
|
31333
|
+
},
|
|
31334
|
+
);
|
|
31335
|
+
if (
|
|
31336
|
+
!eligibility ||
|
|
31337
|
+
eligibility.account_id !== input.accountId ||
|
|
31338
|
+
eligibility.temporal_workflow_id !== input.temporalWorkflowId ||
|
|
31339
|
+
eligibility.temporal_workflow_run_id !== input.temporalWorkflowRunId ||
|
|
31340
|
+
eligibility.temporal_activity_id !== input.temporalActivityId ||
|
|
31341
|
+
eligibility.state !== "closed" ||
|
|
31342
|
+
!eligibility.interruption_settled ||
|
|
31343
|
+
eligibility.interruption_pending
|
|
31344
|
+
) {
|
|
31345
|
+
return { action: "stale", events: [] };
|
|
31346
|
+
}
|
|
31347
|
+
if (eligibility.quiesced_at) {
|
|
31348
|
+
return { action: "quiesced", events: [] };
|
|
31349
|
+
}
|
|
31350
|
+
if (!input.activitySettled || eligibility.writer_pending) {
|
|
31351
|
+
return { action: "pending", events: [] };
|
|
31352
|
+
}
|
|
31353
|
+
const events = await markSessionAttemptQuiesced(db, {
|
|
31354
|
+
accountId: input.accountId,
|
|
31355
|
+
workspaceId: input.workspaceId,
|
|
31356
|
+
sessionId: input.sessionId,
|
|
31357
|
+
attemptId: input.attemptId,
|
|
31358
|
+
temporalWorkflowId: input.temporalWorkflowId,
|
|
31359
|
+
temporalWorkflowRunId: input.temporalWorkflowRunId,
|
|
31360
|
+
temporalActivityId: input.temporalActivityId,
|
|
31361
|
+
});
|
|
31362
|
+
return { action: "quiesced", events };
|
|
31363
|
+
}
|
|
31364
|
+
|
|
31001
31365
|
/**
|
|
31002
31366
|
* Settle every durable interruption cause for one exact first-class attempt.
|
|
31003
31367
|
* Steer wins the logical-turn fate when causes coexist; effective control after
|
|
@@ -31168,13 +31532,6 @@ export async function settleSessionAttemptInterruptions(
|
|
|
31168
31532
|
outcome,
|
|
31169
31533
|
closedAt: now,
|
|
31170
31534
|
});
|
|
31171
|
-
await requeueInterruptedSessionSystemUpdatesForTurnTx(
|
|
31172
|
-
tx as unknown as Database,
|
|
31173
|
-
workspaceId,
|
|
31174
|
-
sessionId,
|
|
31175
|
-
turn.id,
|
|
31176
|
-
);
|
|
31177
|
-
|
|
31178
31535
|
const eventValues: Array<typeof schema.sessionEvents.$inferInsert> = steer
|
|
31179
31536
|
? [
|
|
31180
31537
|
{
|
|
@@ -32459,10 +32816,45 @@ export async function applySessionTurnSettlement(
|
|
|
32459
32816
|
},
|
|
32460
32817
|
}),
|
|
32461
32818
|
);
|
|
32819
|
+
const settledMachineInputs = ["completed", "failed", "cancelled", "superseded"].includes(
|
|
32820
|
+
input.turnStatus,
|
|
32821
|
+
)
|
|
32822
|
+
? await tx
|
|
32823
|
+
.select({
|
|
32824
|
+
id: schema.sessionSystemUpdates.id,
|
|
32825
|
+
historyItemId: schema.sessionSystemUpdates.deliveredHistoryItemId,
|
|
32826
|
+
})
|
|
32827
|
+
.from(schema.sessionSystemUpdates)
|
|
32828
|
+
.where(
|
|
32829
|
+
and(
|
|
32830
|
+
eq(schema.sessionSystemUpdates.workspaceId, workspaceId),
|
|
32831
|
+
eq(schema.sessionSystemUpdates.sessionId, input.sessionId),
|
|
32832
|
+
eq(schema.sessionSystemUpdates.deliveredTurnId, input.turnId),
|
|
32833
|
+
eq(schema.sessionSystemUpdates.state, "delivered"),
|
|
32834
|
+
),
|
|
32835
|
+
)
|
|
32836
|
+
.orderBy(
|
|
32837
|
+
asc(schema.sessionSystemUpdates.createdAt),
|
|
32838
|
+
asc(schema.sessionSystemUpdates.id),
|
|
32839
|
+
)
|
|
32840
|
+
: [];
|
|
32841
|
+
const machineInputSettlementEvent: AppendEventInput | null =
|
|
32842
|
+
settledMachineInputs.length > 0
|
|
32843
|
+
? {
|
|
32844
|
+
type: "system.update.settled",
|
|
32845
|
+
payload: {
|
|
32846
|
+
updateIds: settledMachineInputs.map((update) => update.id),
|
|
32847
|
+
count: settledMachineInputs.length,
|
|
32848
|
+
historyItemId: settledMachineInputs[0]!.historyItemId,
|
|
32849
|
+
outcome: input.turnStatus,
|
|
32850
|
+
},
|
|
32851
|
+
}
|
|
32852
|
+
: null;
|
|
32462
32853
|
const settlementEvents = [
|
|
32463
32854
|
...(recordingEvent ? [recordingEvent] : []),
|
|
32464
32855
|
...(compactionRequestEvent ? [compactionRequestEvent] : []),
|
|
32465
32856
|
...terminalHumanInputEvents,
|
|
32857
|
+
...(machineInputSettlementEvent ? [machineInputSettlementEvent] : []),
|
|
32466
32858
|
...input.events,
|
|
32467
32859
|
];
|
|
32468
32860
|
const values = settlementEvents.map((event) => {
|
|
@@ -32545,21 +32937,6 @@ export async function applySessionTurnSettlement(
|
|
|
32545
32937
|
turn,
|
|
32546
32938
|
);
|
|
32547
32939
|
}
|
|
32548
|
-
if (input.turnStatus === "failed") {
|
|
32549
|
-
await deferFailedSessionSystemUpdatesForTurnTx(
|
|
32550
|
-
tx as unknown as Database,
|
|
32551
|
-
workspaceId,
|
|
32552
|
-
input.sessionId,
|
|
32553
|
-
input.turnId,
|
|
32554
|
-
);
|
|
32555
|
-
} else if (["cancelled", "superseded"].includes(input.turnStatus)) {
|
|
32556
|
-
await requeueInterruptedSessionSystemUpdatesForTurnTx(
|
|
32557
|
-
tx as unknown as Database,
|
|
32558
|
-
workspaceId,
|
|
32559
|
-
input.sessionId,
|
|
32560
|
-
input.turnId,
|
|
32561
|
-
);
|
|
32562
|
-
}
|
|
32563
32940
|
await tx
|
|
32564
32941
|
.update(schema.sessions)
|
|
32565
32942
|
.set({
|
|
@@ -32843,14 +33220,6 @@ export async function settleCodexCredentialLeaseLoss(
|
|
|
32843
33220
|
eq(schema.sessions.activeTurnId, input.turnId),
|
|
32844
33221
|
),
|
|
32845
33222
|
);
|
|
32846
|
-
if (!input.checkpointDurable) {
|
|
32847
|
-
await deferFailedSessionSystemUpdatesForTurnTx(
|
|
32848
|
-
tx as unknown as Database,
|
|
32849
|
-
input.workspaceId,
|
|
32850
|
-
input.sessionId,
|
|
32851
|
-
input.turnId,
|
|
32852
|
-
);
|
|
32853
|
-
}
|
|
32854
33223
|
await tx.execute(sql`
|
|
32855
33224
|
delete from codex_credential_leases
|
|
32856
33225
|
where account_id = ${input.accountId}
|
|
@@ -33794,11 +34163,34 @@ export async function getSessionQueueSnapshot(
|
|
|
33794
34163
|
),
|
|
33795
34164
|
)
|
|
33796
34165
|
.orderBy(asc(schema.sessionTurns.position), asc(schema.sessionTurns.createdAt));
|
|
34166
|
+
const pendingInputs = await scopedDb
|
|
34167
|
+
.select()
|
|
34168
|
+
.from(schema.sessionSystemUpdates)
|
|
34169
|
+
.where(
|
|
34170
|
+
and(
|
|
34171
|
+
eq(schema.sessionSystemUpdates.workspaceId, workspaceId),
|
|
34172
|
+
eq(schema.sessionSystemUpdates.sessionId, sessionId),
|
|
34173
|
+
eq(schema.sessionSystemUpdates.state, "pending"),
|
|
34174
|
+
),
|
|
34175
|
+
)
|
|
34176
|
+
.orderBy(
|
|
34177
|
+
sql`case when ${schema.sessionSystemUpdates.kind} = 'agent_steer_instruction' then 0 else 1 end`,
|
|
34178
|
+
asc(schema.sessionSystemUpdates.createdAt),
|
|
34179
|
+
asc(schema.sessionSystemUpdates.id),
|
|
34180
|
+
);
|
|
33797
34181
|
const latestInterruption = await latestSessionAttemptInterruption(
|
|
33798
34182
|
scopedDb,
|
|
33799
34183
|
workspaceId,
|
|
33800
34184
|
sessionId,
|
|
33801
34185
|
);
|
|
34186
|
+
const items = rows.map(mapSessionTurn);
|
|
34187
|
+
const nextInputBatch = selectBoundedSystemUpdateBatch(pendingInputs);
|
|
34188
|
+
const hasPendingAgentSteer = pendingInputs.some(
|
|
34189
|
+
(update) => update.kind === "agent_steer_instruction",
|
|
34190
|
+
);
|
|
34191
|
+
const attachmentTurn = hasPendingAgentSteer
|
|
34192
|
+
? items.find((turn) => turn.metadata.delivery === "steer")
|
|
34193
|
+
: items[0];
|
|
33802
34194
|
return {
|
|
33803
34195
|
version: session.queueVersion,
|
|
33804
34196
|
effectiveControl: serializeEffectiveSessionControl(effectiveControl),
|
|
@@ -33806,7 +34198,26 @@ export async function getSessionQueueSnapshot(
|
|
|
33806
34198
|
latestInterruption !== null &&
|
|
33807
34199
|
latestInterruption.interruptionState !== "rejected_stale" &&
|
|
33808
34200
|
latestInterruption.quiescedAt === null,
|
|
33809
|
-
items
|
|
34201
|
+
items,
|
|
34202
|
+
pendingInputs: pendingInputs.map((update) => {
|
|
34203
|
+
const canonical = mapSessionSystemUpdate(update);
|
|
34204
|
+
return {
|
|
34205
|
+
id: canonical.id,
|
|
34206
|
+
sessionId: canonical.sessionId,
|
|
34207
|
+
kind: canonical.kind,
|
|
34208
|
+
classification: canonical.classification,
|
|
34209
|
+
sourceId: boundedInternalUpdateEventText(canonical.sourceId, 256).text,
|
|
34210
|
+
summary: boundedInternalUpdateEventText(canonical.summary, 512).text,
|
|
34211
|
+
createdAt: canonical.createdAt,
|
|
34212
|
+
};
|
|
34213
|
+
}),
|
|
34214
|
+
pendingInputAttachment:
|
|
34215
|
+
attachmentTurn && nextInputBatch.length > 0
|
|
34216
|
+
? {
|
|
34217
|
+
turnId: attachmentTurn.id,
|
|
34218
|
+
inputIds: nextInputBatch.map((update) => update.id),
|
|
34219
|
+
}
|
|
34220
|
+
: null,
|
|
33810
34221
|
};
|
|
33811
34222
|
});
|
|
33812
34223
|
}
|
|
@@ -34155,12 +34566,13 @@ export async function claimPendingSessionWorkflowWakes(
|
|
|
34155
34566
|
|
|
34156
34567
|
/**
|
|
34157
34568
|
* Acknowledge an immediate post-commit signal only after it cannot strand an
|
|
34158
|
-
* accepted
|
|
34159
|
-
*
|
|
34160
|
-
*
|
|
34161
|
-
* the revision so the bounded outbox
|
|
34162
|
-
* attempt-fenced claim consumes
|
|
34163
|
-
*
|
|
34569
|
+
* accepted direction or an interrupted attempt awaiting writer-set proof.
|
|
34570
|
+
* Temporal accepting a signal is transport evidence, not proof that a closing
|
|
34571
|
+
* workflow observed Postgres. While active control still has an actionable
|
|
34572
|
+
* Agent Steer or pending quiescence, retain the revision so the bounded outbox
|
|
34573
|
+
* dispatcher retries signalWithStart. The attempt-fenced claim consumes an
|
|
34574
|
+
* Agent Steer once; a real Pause is the typed blocker and may acknowledge this
|
|
34575
|
+
* revision because Resume commits a new one.
|
|
34164
34576
|
*
|
|
34165
34577
|
* An older sender may advance only its own revision; it cannot clear a claim or
|
|
34166
34578
|
* failure state belonging to a newer revision. The control -> workspace ->
|
|
@@ -34169,7 +34581,10 @@ export async function claimPendingSessionWorkflowWakes(
|
|
|
34169
34581
|
*/
|
|
34170
34582
|
export type SessionWorkflowWakeDeliveryResult =
|
|
34171
34583
|
| { action: "acknowledged" }
|
|
34172
|
-
| {
|
|
34584
|
+
| {
|
|
34585
|
+
action: "pending_admission";
|
|
34586
|
+
blocker: "pending_agent_steer" | "pending_quiescence";
|
|
34587
|
+
};
|
|
34173
34588
|
|
|
34174
34589
|
export async function markSessionWorkflowWakeDelivered(
|
|
34175
34590
|
db: Database,
|
|
@@ -34209,6 +34624,35 @@ export async function markSessionWorkflowWakeDelivered(
|
|
|
34209
34624
|
if (pendingAgentSteer) {
|
|
34210
34625
|
return { action: "pending_admission", blocker: "pending_agent_steer" } as const;
|
|
34211
34626
|
}
|
|
34627
|
+
const [pendingQuiescence] = await tx
|
|
34628
|
+
.select({ id: schema.sessionTurnAttempts.id })
|
|
34629
|
+
.from(schema.sessionTurnAttempts)
|
|
34630
|
+
.innerJoin(
|
|
34631
|
+
schema.sessionAttemptInterruptions,
|
|
34632
|
+
and(
|
|
34633
|
+
eq(
|
|
34634
|
+
schema.sessionAttemptInterruptions.workspaceId,
|
|
34635
|
+
schema.sessionTurnAttempts.workspaceId,
|
|
34636
|
+
),
|
|
34637
|
+
eq(
|
|
34638
|
+
schema.sessionAttemptInterruptions.sessionId,
|
|
34639
|
+
schema.sessionTurnAttempts.sessionId,
|
|
34640
|
+
),
|
|
34641
|
+
eq(schema.sessionAttemptInterruptions.attemptId, schema.sessionTurnAttempts.id),
|
|
34642
|
+
),
|
|
34643
|
+
)
|
|
34644
|
+
.where(
|
|
34645
|
+
and(
|
|
34646
|
+
eq(schema.sessionTurnAttempts.workspaceId, input.workspaceId),
|
|
34647
|
+
eq(schema.sessionTurnAttempts.sessionId, input.sessionId),
|
|
34648
|
+
isNull(schema.sessionTurnAttempts.quiescedAt),
|
|
34649
|
+
inArray(schema.sessionAttemptInterruptions.state, ["settled", "rejected_stale"]),
|
|
34650
|
+
),
|
|
34651
|
+
)
|
|
34652
|
+
.limit(1);
|
|
34653
|
+
if (pendingQuiescence) {
|
|
34654
|
+
return { action: "pending_admission", blocker: "pending_quiescence" } as const;
|
|
34655
|
+
}
|
|
34212
34656
|
}
|
|
34213
34657
|
const [row] = await tx
|
|
34214
34658
|
.update(schema.sessionWorkflowWakeOutbox)
|
|
@@ -34443,55 +34887,6 @@ export async function addSessionSystemUpdate(
|
|
|
34443
34887
|
return await addSessionSystemUpdateWithSourceMutation(db, input, async () => undefined);
|
|
34444
34888
|
}
|
|
34445
34889
|
|
|
34446
|
-
async function requeueInterruptedSessionSystemUpdatesForTurnTx(
|
|
34447
|
-
tx: Database,
|
|
34448
|
-
workspaceId: string,
|
|
34449
|
-
sessionId: string,
|
|
34450
|
-
turnId: string,
|
|
34451
|
-
): Promise<void> {
|
|
34452
|
-
await tx
|
|
34453
|
-
.update(schema.sessionSystemUpdates)
|
|
34454
|
-
.set({ state: "pending", deliveredTurnId: null, deliveredAt: null })
|
|
34455
|
-
.where(
|
|
34456
|
-
and(
|
|
34457
|
-
eq(schema.sessionSystemUpdates.workspaceId, workspaceId),
|
|
34458
|
-
eq(schema.sessionSystemUpdates.sessionId, sessionId),
|
|
34459
|
-
eq(schema.sessionSystemUpdates.deliveredTurnId, turnId),
|
|
34460
|
-
eq(schema.sessionSystemUpdates.state, "delivered"),
|
|
34461
|
-
),
|
|
34462
|
-
);
|
|
34463
|
-
}
|
|
34464
|
-
|
|
34465
|
-
/**
|
|
34466
|
-
* A failed internal-only inference must not manufacture another inference by
|
|
34467
|
-
* making its inputs immediately runnable again. Preserve ordinary internal
|
|
34468
|
-
* updates as deferred input for the next real prompt/new update. Goal
|
|
34469
|
-
* continuation notices are derivable from the durable goal and become terminal
|
|
34470
|
-
* so the goal evaluator can pause or synthesize the next valid continuation.
|
|
34471
|
-
*/
|
|
34472
|
-
async function deferFailedSessionSystemUpdatesForTurnTx(
|
|
34473
|
-
tx: Database,
|
|
34474
|
-
workspaceId: string,
|
|
34475
|
-
sessionId: string,
|
|
34476
|
-
turnId: string,
|
|
34477
|
-
): Promise<void> {
|
|
34478
|
-
await tx
|
|
34479
|
-
.update(schema.sessionSystemUpdates)
|
|
34480
|
-
.set({
|
|
34481
|
-
state: sql`case when ${schema.sessionSystemUpdates.payload} ->> 'type' = 'goal_continuation' then 'failed' else 'deferred' end`,
|
|
34482
|
-
deliveredTurnId: null,
|
|
34483
|
-
deliveredAt: null,
|
|
34484
|
-
})
|
|
34485
|
-
.where(
|
|
34486
|
-
and(
|
|
34487
|
-
eq(schema.sessionSystemUpdates.workspaceId, workspaceId),
|
|
34488
|
-
eq(schema.sessionSystemUpdates.sessionId, sessionId),
|
|
34489
|
-
eq(schema.sessionSystemUpdates.deliveredTurnId, turnId),
|
|
34490
|
-
eq(schema.sessionSystemUpdates.state, "delivered"),
|
|
34491
|
-
),
|
|
34492
|
-
);
|
|
34493
|
-
}
|
|
34494
|
-
|
|
34495
34890
|
/**
|
|
34496
34891
|
* Persist one internal update without fabricating a user prompt or queue row.
|
|
34497
34892
|
* Dedupe and any producer/outbox mutation commit in the same transaction.
|
|
@@ -34593,6 +34988,7 @@ export async function addSessionSystemUpdateWithSourceMutation(
|
|
|
34593
34988
|
}
|
|
34594
34989
|
|
|
34595
34990
|
const now = new Date();
|
|
34991
|
+
const eventPreview = internalUpdateEventMember(inserted);
|
|
34596
34992
|
const [event] = await tx
|
|
34597
34993
|
.insert(schema.sessionEvents)
|
|
34598
34994
|
.values({
|
|
@@ -34602,11 +34998,13 @@ export async function addSessionSystemUpdateWithSourceMutation(
|
|
|
34602
34998
|
sequence: session.lastSequence + 1,
|
|
34603
34999
|
type: "system.update.pending",
|
|
34604
35000
|
payload: sanitizeEventPayload({
|
|
34605
|
-
updateId:
|
|
34606
|
-
kind:
|
|
34607
|
-
classification:
|
|
34608
|
-
sourceId:
|
|
34609
|
-
|
|
35001
|
+
updateId: eventPreview.id,
|
|
35002
|
+
kind: eventPreview.kind,
|
|
35003
|
+
classification: eventPreview.classification,
|
|
35004
|
+
sourceId: eventPreview.sourceId,
|
|
35005
|
+
sourceIdTruncated: eventPreview.sourceIdTruncated,
|
|
35006
|
+
summary: eventPreview.summary,
|
|
35007
|
+
summaryTruncated: eventPreview.summaryTruncated,
|
|
34610
35008
|
}),
|
|
34611
35009
|
occurredAt: now,
|
|
34612
35010
|
})
|
|
@@ -34657,7 +35055,7 @@ export async function listOutstandingSessionSystemUpdates(
|
|
|
34657
35055
|
and(
|
|
34658
35056
|
eq(schema.sessionSystemUpdates.workspaceId, workspaceId),
|
|
34659
35057
|
eq(schema.sessionSystemUpdates.sessionId, sessionId),
|
|
34660
|
-
|
|
35058
|
+
eq(schema.sessionSystemUpdates.state, "pending"),
|
|
34661
35059
|
),
|
|
34662
35060
|
)
|
|
34663
35061
|
.orderBy(asc(schema.sessionSystemUpdates.createdAt), asc(schema.sessionSystemUpdates.id));
|
|
@@ -34713,6 +35111,7 @@ function mapSessionSystemUpdate(
|
|
|
34713
35111
|
lineage: row.lineage,
|
|
34714
35112
|
state: row.state as SessionSystemUpdateState,
|
|
34715
35113
|
deliveredTurnId: row.deliveredTurnId,
|
|
35114
|
+
deliveredHistoryItemId: row.deliveredHistoryItemId,
|
|
34716
35115
|
deliveredAt: row.deliveredAt?.toISOString() ?? null,
|
|
34717
35116
|
createdAt: row.createdAt.toISOString(),
|
|
34718
35117
|
};
|
|
@@ -35590,6 +35989,7 @@ function mapSession(
|
|
|
35590
35989
|
rigId: row.rigId ?? null,
|
|
35591
35990
|
rigVersionId: row.rigVersionId ?? null,
|
|
35592
35991
|
firstPartyMcpPermissions: (row.firstPartyMcpPermissions as Permission[] | null) ?? null,
|
|
35992
|
+
firstPartyMcpTools: (row.firstPartyMcpTools as FirstPartyMcpToolName[] | null) ?? null,
|
|
35593
35993
|
mcpServers,
|
|
35594
35994
|
parentSessionId: row.parentSessionId ?? null,
|
|
35595
35995
|
rootSessionId: row.rootSessionId,
|