@opengeni/db 0.13.4 → 0.14.3

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/src/schema.ts CHANGED
@@ -1,4 +1,8 @@
1
- import type { McpServerConnectionRef, SessionMcpApprovalPolicy } from "@opengeni/contracts";
1
+ import type {
2
+ FirstPartyMcpToolName,
3
+ McpServerConnectionRef,
4
+ SessionMcpApprovalPolicy,
5
+ } from "@opengeni/contracts";
2
6
  import { sql } from "drizzle-orm";
3
7
  import type { SessionToolPolicy } from "@opengeni/contracts";
4
8
  import type { HumanInputQuestion, HumanInputResponse } from "@opengeni/contracts";
@@ -838,10 +842,12 @@ export const sessions = pgTable(
838
842
  // Non-default first-party MCP token permissions (manager-style sessions);
839
843
  // null means the fixed worker default set in @opengeni/runtime.
840
844
  firstPartyMcpPermissions: jsonb("first_party_mcp_permissions").$type<string[]>(),
841
- // Durable tool-policy origin. NULL is retained for pre-migration rows;
842
- // mapSession exposes those rows as `legacy` instead of guessing omitted vs
843
- // explicit [].
844
- toolPolicy: jsonb("tool_policy").$type<SessionToolPolicy>(),
845
+ // Exact model-visible first-party tool selection. All catalogued tools are
846
+ // selected by default; [] intentionally selects no broad-server tools.
847
+ firstPartyMcpTools: jsonb("first_party_mcp_tools").$type<FirstPartyMcpToolName[]>().notNull(),
848
+ // Durable tool-policy origin. Migration 0136 removes the old null/legacy
849
+ // representation so every session has one explicit policy mode.
850
+ toolPolicy: jsonb("tool_policy").$type<SessionToolPolicy>().notNull(),
845
851
  // Optimistic-concurrency fence for durable session tool-policy writes.
846
852
  toolPolicyVersion: integer("tool_policy_version").notNull().default(1),
847
853
  // The manager session that spawned this one via session_create. Set only
@@ -1913,14 +1919,15 @@ export const sessionSystemUpdates = pgTable(
1913
1919
  summary: text("summary").notNull(),
1914
1920
  payload: jsonb("payload").$type<Record<string, unknown>>().notNull().default({}),
1915
1921
  lineage: jsonb("lineage").$type<Record<string, unknown>>().notNull().default({}),
1916
- // pending: eligible to start/attach to an inference; deferred: preserved
1917
- // after a failed internal-only inference but dormant until a real prompt or
1918
- // a genuinely new pending update arrives; delivered/cancelled/failed are
1919
- // terminal for that delivery attempt.
1922
+ // pending is visible queue truth; delivered means its exact model-memory
1923
+ // batch was durably claimed. Terminal cancellation/supersession is explicit.
1920
1924
  state: text("state").notNull().default("pending"),
1921
1925
  deliveredTurnId: uuid("delivered_turn_id").references(() => sessionTurns.id, {
1922
1926
  onDelete: "set null",
1923
1927
  }),
1928
+ // Migration owns the forward FK to session_history_items, declared later.
1929
+ // Every member of one claimed batch points at the exact model-memory row.
1930
+ deliveredHistoryItemId: uuid("delivered_history_item_id"),
1924
1931
  deliveredAt: timestamp("delivered_at", { withTimezone: true }),
1925
1932
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1926
1933
  },
@@ -1935,7 +1942,7 @@ export const sessionSystemUpdates = pgTable(
1935
1942
  ),
1936
1943
  stateValid: check(
1937
1944
  "system_updates_state_check",
1938
- sql`${table.state} in ('pending', 'deferred', 'delivered', 'cancelled', 'superseded', 'failed')`,
1945
+ sql`${table.state} in ('pending', 'delivered', 'cancelled', 'superseded', 'failed')`,
1939
1946
  ),
1940
1947
  dedupe: uniqueIndex("session_system_updates_dedupe_uq").on(
1941
1948
  table.workspaceId,
@@ -1948,6 +1955,17 @@ export const sessionSystemUpdates = pgTable(
1948
1955
  table.state,
1949
1956
  table.createdAt,
1950
1957
  ),
1958
+ onePendingSteer: uniqueIndex("session_system_updates_one_pending_steer_idx")
1959
+ .on(table.workspaceId, table.sessionId)
1960
+ .where(sql`${table.kind} = 'agent_steer_instruction' and ${table.state} = 'pending'`),
1961
+ deliveryHistoryValid: check(
1962
+ "session_system_updates_delivery_history_check",
1963
+ sql`(
1964
+ (${table.state} = 'delivered' and ${table.deliveredHistoryItemId} is not null)
1965
+ or
1966
+ (${table.state} <> 'delivered' and ${table.deliveredHistoryItemId} is null)
1967
+ )`,
1968
+ ),
1951
1969
  }),
1952
1970
  );
1953
1971
 
@@ -1,12 +1,9 @@
1
1
  import {
2
2
  metadataWithTurnExecutionPolicyV1,
3
3
  mergeResourceRefs,
4
- mergeToolRefs,
5
4
  turnExecutionPolicyAuditMetadata,
6
5
  type ReasoningEffort,
7
6
  type ResourceRef,
8
- type SessionToolPolicy,
9
- type ToolRef,
10
7
  type TurnExecutionPolicyV1,
11
8
  } from "@opengeni/contracts";
12
9
  import { and, asc, eq, inArray, sql } from "drizzle-orm";
@@ -279,17 +276,6 @@ export async function supersedeSessionCurrentDirectionInTransaction(
279
276
  updatedAt: now,
280
277
  })
281
278
  .where(eq(schema.sessionTurns.id, current.id));
282
- await db
283
- .update(schema.sessionSystemUpdates)
284
- .set({ state: "pending", deliveredTurnId: null, deliveredAt: null })
285
- .where(
286
- and(
287
- eq(schema.sessionSystemUpdates.workspaceId, input.workspaceId),
288
- eq(schema.sessionSystemUpdates.sessionId, input.sessionId),
289
- eq(schema.sessionSystemUpdates.deliveredTurnId, current.id),
290
- eq(schema.sessionSystemUpdates.state, "delivered"),
291
- ),
292
- );
293
279
  if (current.status === "waiting_capacity") {
294
280
  await db
295
281
  .update(schema.codexCapacityWaiters)
@@ -378,13 +364,7 @@ async function normalizeQueuePositions(
378
364
  }
379
365
 
380
366
  function draftIsNonEmpty(draft: ComposerDraftRow): boolean {
381
- return (
382
- draft.text.length > 0 ||
383
- draft.resources.length > 0 ||
384
- draft.tools.length > 0 ||
385
- draft.toolsProvided ||
386
- draft.sourceTurnId !== null
387
- );
367
+ return draft.text.length > 0 || draft.resources.length > 0 || draft.sourceTurnId !== null;
388
368
  }
389
369
 
390
370
  export async function getComposerDraftInTransaction(
@@ -421,8 +401,6 @@ export async function saveComposerDraftInTransaction(
421
401
  expectedRevision: number;
422
402
  text: string;
423
403
  resources: ResourceRef[];
424
- tools: ToolRef[];
425
- toolsProvided: boolean;
426
404
  model: string;
427
405
  reasoningEffort: ReasoningEffort;
428
406
  },
@@ -453,8 +431,8 @@ export async function saveComposerDraftInTransaction(
453
431
  revision,
454
432
  text: input.text,
455
433
  resources: input.resources,
456
- tools: input.tools,
457
- toolsProvided: input.toolsProvided,
434
+ tools: [],
435
+ toolsProvided: false,
458
436
  model: input.model,
459
437
  reasoningEffort: input.reasoningEffort,
460
438
  // A queue edit is still the same accepted work item. Preserve its frozen
@@ -825,8 +803,8 @@ export async function editQueuedTurnInTransaction(
825
803
  revision: nextDraftRevision,
826
804
  text: turn.prompt,
827
805
  resources: turn.resources,
828
- tools: turn.tools,
829
- toolsProvided: turn.toolsProvided,
806
+ tools: [],
807
+ toolsProvided: false,
830
808
  model: turn.model,
831
809
  reasoningEffort: turn.reasoningEffort,
832
810
  sourceTurnId: turn.id,
@@ -1142,8 +1120,6 @@ export async function submitHumanPromptInTransaction(
1142
1120
  text: string;
1143
1121
  turnInstructions?: string | null;
1144
1122
  resources: ResourceRef[];
1145
- tools: ToolRef[];
1146
- toolsProvided?: boolean;
1147
1123
  model?: string | null;
1148
1124
  reasoningEffort?: ReasoningEffort | null;
1149
1125
  reasoningEffortFallback: ReasoningEffort;
@@ -1174,8 +1150,6 @@ export async function submitHumanPromptInTransaction(
1174
1150
  text: input.text,
1175
1151
  turnInstructions: input.turnInstructions ?? null,
1176
1152
  resources: input.resources,
1177
- tools: input.tools,
1178
- toolsProvided: input.toolsProvided === true,
1179
1153
  model: input.model ?? null,
1180
1154
  reasoningEffort: input.reasoningEffort ?? null,
1181
1155
  source: input.source,
@@ -1283,16 +1257,12 @@ export async function submitHumanPromptInTransaction(
1283
1257
  canonicalSessionCommandHash({
1284
1258
  text: draft.text,
1285
1259
  resources: draft.resources,
1286
- tools: draft.tools,
1287
- toolsProvided: draft.toolsProvided,
1288
1260
  model: draft.model,
1289
1261
  reasoningEffort: draft.reasoningEffort,
1290
1262
  }) !==
1291
1263
  canonicalSessionCommandHash({
1292
1264
  text: input.text,
1293
1265
  resources: input.resources,
1294
- tools: input.tools,
1295
- toolsProvided: input.toolsProvided === true,
1296
1266
  model: input.model ?? session.model,
1297
1267
  reasoningEffort: input.reasoningEffort ?? input.reasoningEffortFallback,
1298
1268
  })
@@ -1412,11 +1382,6 @@ export async function submitHumanPromptInTransaction(
1412
1382
  payload: sanitizeEventPayload({
1413
1383
  text: input.text,
1414
1384
  ...(input.resources.length ? { resources: input.resources } : {}),
1415
- ...(input.toolsProvided === true
1416
- ? { tools: input.tools }
1417
- : input.tools.length
1418
- ? { tools: input.tools }
1419
- : {}),
1420
1385
  ...(input.model ? { model: input.model } : {}),
1421
1386
  ...(input.reasoningEffort ? { reasoningEffort: input.reasoningEffort } : {}),
1422
1387
  delivery: input.delivery,
@@ -1444,8 +1409,8 @@ export async function submitHumanPromptInTransaction(
1444
1409
  ? editedSourceTurnInstructions
1445
1410
  : (input.turnInstructions ?? null),
1446
1411
  resources: input.resources,
1447
- tools: input.tools,
1448
- toolsProvided: input.toolsProvided === true,
1412
+ tools: [],
1413
+ toolsProvided: false,
1449
1414
  model: input.model ?? session.model,
1450
1415
  reasoningEffort: input.reasoningEffort ?? input.reasoningEffortFallback,
1451
1416
  sandboxBackend: session.sandboxBackend,
@@ -1589,9 +1554,7 @@ export async function submitHumanPromptInTransaction(
1589
1554
  .update(schema.sessions)
1590
1555
  .set({
1591
1556
  resources: mergeResourceRefs(session.resources as ResourceRef[], input.resources),
1592
- tools: sessionToolPolicyIsFixed(session.toolPolicy)
1593
- ? session.tools
1594
- : mergeToolRefs(session.tools as ToolRef[], input.tools),
1557
+ tools: session.tools,
1595
1558
  activeTurnId: input.delivery === "steer" ? liveCurrentTurnId : session.activeTurnId,
1596
1559
  status: nextStatus,
1597
1560
  queueVersion,
@@ -1664,12 +1627,6 @@ export async function submitHumanPromptInTransaction(
1664
1627
  };
1665
1628
  }
1666
1629
 
1667
- function sessionToolPolicyIsFixed(value: unknown): boolean {
1668
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1669
- const mode = (value as Partial<SessionToolPolicy>).mode;
1670
- return mode === "workspace_default" || mode === "explicit" || mode === "inherited";
1671
- }
1672
-
1673
1630
  export async function sendAgentMessageInTransaction(
1674
1631
  db: Database,
1675
1632
  input: {
@@ -1927,7 +1884,7 @@ export async function steerAgentSessionInTransaction(
1927
1884
  controlRevision: resumed.revision,
1928
1885
  lastSequence: session.lastSequence,
1929
1886
  });
1930
- await db
1887
+ const supersededUpdates = await db
1931
1888
  .update(schema.sessionSystemUpdates)
1932
1889
  .set({ state: "superseded" })
1933
1890
  .where(
@@ -1935,9 +1892,10 @@ export async function steerAgentSessionInTransaction(
1935
1892
  eq(schema.sessionSystemUpdates.workspaceId, input.workspaceId),
1936
1893
  eq(schema.sessionSystemUpdates.sessionId, input.targetSessionId),
1937
1894
  eq(schema.sessionSystemUpdates.kind, "agent_steer_instruction"),
1938
- inArray(schema.sessionSystemUpdates.state, ["pending", "deferred"]),
1895
+ eq(schema.sessionSystemUpdates.state, "pending"),
1939
1896
  ),
1940
- );
1897
+ )
1898
+ .returning({ id: schema.sessionSystemUpdates.id });
1941
1899
  const now = new Date();
1942
1900
  const [update] = await db
1943
1901
  .insert(schema.sessionSystemUpdates)
@@ -1999,6 +1957,24 @@ export async function steerAgentSessionInTransaction(
1999
1957
  },
2000
1958
  occurredAt: now,
2001
1959
  },
1960
+ ...(supersededUpdates.length > 0
1961
+ ? [
1962
+ {
1963
+ accountId: input.accountId,
1964
+ workspaceId: input.workspaceId,
1965
+ sessionId: input.targetSessionId,
1966
+ sequence: ++sequence,
1967
+ type: "system.update.superseded" as const,
1968
+ payload: {
1969
+ updateIds: supersededUpdates.map((entry) => entry.id),
1970
+ count: supersededUpdates.length,
1971
+ replacementUpdateId: update.id,
1972
+ reason: "newer_agent_steer",
1973
+ },
1974
+ occurredAt: now,
1975
+ },
1976
+ ]
1977
+ : []),
2002
1978
  {
2003
1979
  accountId: input.accountId,
2004
1980
  workspaceId: input.workspaceId,