@opengeni/db 6.0.0 → 6.0.1

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.
Files changed (74) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/capability-reconciliation.js +2 -2
  3. package/dist/child-read-reconciliation.d.ts +35 -0
  4. package/dist/{chunk-CJI6G254.js → chunk-4CCFFLDZ.js} +2 -2
  5. package/dist/{chunk-7JCDSMX2.js → chunk-4RDUZ5UV.js} +2 -2
  6. package/dist/{chunk-SYKHLPTK.js → chunk-6ORJPCYW.js} +2 -2
  7. package/dist/{chunk-GABXCG3N.js → chunk-CSVHCF3R.js} +11 -6
  8. package/dist/chunk-CSVHCF3R.js.map +1 -0
  9. package/dist/{chunk-CDAT6EKW.js → chunk-FULLE5CF.js} +3 -3
  10. package/dist/{chunk-MN5PZMEK.js → chunk-GSB4ISHE.js} +905 -801
  11. package/dist/chunk-GSB4ISHE.js.map +1 -0
  12. package/dist/{chunk-XIXVVBW2.js → chunk-H7BC444C.js} +3 -1
  13. package/dist/chunk-H7BC444C.js.map +1 -0
  14. package/dist/{chunk-LVY33EWL.js → chunk-I7HQL6FK.js} +3 -3
  15. package/dist/{chunk-RIYHP2WU.js → chunk-JILAWE26.js} +2 -2
  16. package/dist/{chunk-ZPJ6V2VG.js → chunk-LO6BVXXH.js} +2 -2
  17. package/dist/{chunk-PJI4MUZ7.js → chunk-LSLNMENV.js} +4 -4
  18. package/dist/{chunk-RL2MUXM3.js → chunk-PGIFHEBA.js} +2 -2
  19. package/dist/{chunk-G32NVMRJ.js → chunk-RXH5CFG3.js} +5 -5
  20. package/dist/{chunk-AJA2ZIUW.js → chunk-XK6HXON2.js} +2 -2
  21. package/dist/connect-attempts.js +3 -3
  22. package/dist/editable-artifact-durable-export.js +2 -2
  23. package/dist/editable-artifacts.js +3 -3
  24. package/dist/index.d.ts +31 -0
  25. package/dist/index.js +6651 -6244
  26. package/dist/index.js.map +1 -1
  27. package/dist/integration-oauth-pending-states.d.ts +14 -0
  28. package/dist/managed-auth-session-sets.js +3 -3
  29. package/dist/mcp-operations.js +2 -2
  30. package/dist/new-session-drafts.d.ts +2 -1
  31. package/dist/organization-integration-policy.js +3 -3
  32. package/dist/provision-roles.js +1 -1
  33. package/dist/retained-provider-commands.js +6 -6
  34. package/dist/runtime-posture.d.ts +2 -2
  35. package/dist/schema.d.ts +151 -8
  36. package/dist/schema.js +3 -1
  37. package/dist/session-background-commands.js +4 -4
  38. package/dist/session-command-output.js +5 -5
  39. package/dist/session-event-slices.js +3 -3
  40. package/dist/session-mcp-credential-rotation.js +4 -4
  41. package/dist/session-meaningful-events.d.ts +15 -0
  42. package/dist/session-tenancy.js +3 -3
  43. package/dist/skills.d.ts +2 -0
  44. package/dist/video-generation.js +3 -3
  45. package/dist/workspace-tool-defaults.js +4 -4
  46. package/drizzle/0503_session_meaningful_attention.sql +81 -0
  47. package/drizzle/0504_skill_folder_limits.sql +26 -0
  48. package/drizzle/0505_workspace_control_revision_frontier.sql +37 -0
  49. package/drizzle/0506_background_command_text.sql +3 -0
  50. package/drizzle/0507_integration_oauth_pending_states.sql +27 -0
  51. package/package.json +5 -5
  52. package/src/child-read-reconciliation.ts +170 -0
  53. package/src/index.ts +439 -60
  54. package/src/integration-oauth-pending-states.ts +52 -0
  55. package/src/new-session-drafts.ts +4 -2
  56. package/src/runtime-posture.ts +2 -0
  57. package/src/schema.ts +35 -3
  58. package/src/session-background-commands.ts +8 -2
  59. package/src/session-meaningful-events.ts +97 -0
  60. package/src/skills.ts +14 -1
  61. package/dist/chunk-GABXCG3N.js.map +0 -1
  62. package/dist/chunk-MN5PZMEK.js.map +0 -1
  63. package/dist/chunk-XIXVVBW2.js.map +0 -1
  64. /package/dist/{chunk-CJI6G254.js.map → chunk-4CCFFLDZ.js.map} +0 -0
  65. /package/dist/{chunk-7JCDSMX2.js.map → chunk-4RDUZ5UV.js.map} +0 -0
  66. /package/dist/{chunk-SYKHLPTK.js.map → chunk-6ORJPCYW.js.map} +0 -0
  67. /package/dist/{chunk-CDAT6EKW.js.map → chunk-FULLE5CF.js.map} +0 -0
  68. /package/dist/{chunk-LVY33EWL.js.map → chunk-I7HQL6FK.js.map} +0 -0
  69. /package/dist/{chunk-RIYHP2WU.js.map → chunk-JILAWE26.js.map} +0 -0
  70. /package/dist/{chunk-ZPJ6V2VG.js.map → chunk-LO6BVXXH.js.map} +0 -0
  71. /package/dist/{chunk-PJI4MUZ7.js.map → chunk-LSLNMENV.js.map} +0 -0
  72. /package/dist/{chunk-RL2MUXM3.js.map → chunk-PGIFHEBA.js.map} +0 -0
  73. /package/dist/{chunk-G32NVMRJ.js.map → chunk-RXH5CFG3.js.map} +0 -0
  74. /package/dist/{chunk-AJA2ZIUW.js.map → chunk-XK6HXON2.js.map} +0 -0
@@ -0,0 +1,52 @@
1
+ import { and, eq, gt, sql } from "drizzle-orm";
2
+ import { type Database, withRlsContext } from "./database";
3
+ import { integrationOauthPendingStates } from "./schema";
4
+
5
+ type PendingStateScope = {
6
+ accountId: string;
7
+ workspaceId: string;
8
+ };
9
+
10
+ export async function storeIntegrationOAuthPendingState(
11
+ db: Database,
12
+ input: PendingStateScope & {
13
+ id: string;
14
+ stateEncrypted: string;
15
+ expiresAt: Date;
16
+ },
17
+ ): Promise<void> {
18
+ await withRlsContext(db, input, async (scopedDb) => {
19
+ // A bounded per-workspace sweep prevents abandoned browser grants from
20
+ // accumulating without adding a process-local timer to API instances.
21
+ await scopedDb.execute(sql`delete from integration_oauth_pending_states
22
+ where id in (
23
+ select id from integration_oauth_pending_states
24
+ where account_id = ${input.accountId}::uuid
25
+ and workspace_id = ${input.workspaceId}::uuid
26
+ and expires_at <= clock_timestamp()
27
+ order by expires_at, id limit 128
28
+ )`);
29
+ await scopedDb.insert(integrationOauthPendingStates).values(input);
30
+ });
31
+ }
32
+
33
+ export async function loadIntegrationOAuthPendingState(
34
+ db: Database,
35
+ input: PendingStateScope & { id: string },
36
+ ): Promise<string | null> {
37
+ return withRlsContext(db, input, async (scopedDb) => {
38
+ const [row] = await scopedDb
39
+ .select({ stateEncrypted: integrationOauthPendingStates.stateEncrypted })
40
+ .from(integrationOauthPendingStates)
41
+ .where(
42
+ and(
43
+ eq(integrationOauthPendingStates.id, input.id),
44
+ eq(integrationOauthPendingStates.accountId, input.accountId),
45
+ eq(integrationOauthPendingStates.workspaceId, input.workspaceId),
46
+ gt(integrationOauthPendingStates.expiresAt, sql`clock_timestamp()`),
47
+ ),
48
+ )
49
+ .limit(1);
50
+ return row?.stateEncrypted ?? null;
51
+ });
52
+ }
@@ -365,7 +365,9 @@ export async function saveNewSessionDraftInTransaction(
365
365
  throw new NewSessionDraftConflictError(raced?.revision ?? 0);
366
366
  }
367
367
 
368
- function safeRepositoryResource(resource: RepositoryResourceRef): RepositoryResourceRef {
368
+ export function reusableNewSessionRepositoryResource(
369
+ resource: RepositoryResourceRef,
370
+ ): RepositoryResourceRef {
369
371
  // A repository's URI/ref/mount and GitHub identity are ordinary selection
370
372
  // state. Credential bindings, connection refs, access intent, and generic
371
373
  // provider ids are per-session authorization/runtime state and must not seed
@@ -574,7 +576,7 @@ export async function seedNewSessionDraftInTransaction(
574
576
  if (!raw || typeof raw !== "object" || (raw as { kind?: unknown }).kind !== "repository") {
575
577
  return [];
576
578
  }
577
- return [safeRepositoryResource(raw as RepositoryResourceRef)];
579
+ return [reusableNewSessionRepositoryResource(raw as RepositoryResourceRef)];
578
580
  });
579
581
  const [seeded] = await db
580
582
  .update(schema.newSessionDrafts)
@@ -835,6 +835,7 @@ export const FORCE_RLS_TABLES = [
835
835
  "integration_facet_binding_owners",
836
836
  "integration_facet_bindings",
837
837
  "integration_facet_definitions",
838
+ "integration_oauth_pending_states",
838
839
  "integration_oauth_state_nonces",
839
840
  "integration_spec_revisions",
840
841
  "integration_tools",
@@ -1184,6 +1185,7 @@ export const RUNTIME_FULL_DML_TABLES = [
1184
1185
  "integration_facet_binding_owners",
1185
1186
  "integration_facet_bindings",
1186
1187
  "integration_oauth_clients",
1188
+ "integration_oauth_pending_states",
1187
1189
  "integration_oauth_state_nonces",
1188
1190
  "knowledge_source_sync_index_obligations",
1189
1191
  "knowledge_source_sync_item_outcomes",
package/src/schema.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { StoredSessionAdmissionBlock } from "./session-admission-block";
2
+ import { meaningfulSessionEventSql } from "./session-meaningful-events";
2
3
  import type {
3
4
  SandboxProviderCommand,
4
5
  CommandSupervisionReceipt,
@@ -3716,6 +3717,31 @@ export const integrationOauthStateNonces = pgTable(
3716
3717
  }),
3717
3718
  );
3718
3719
 
3720
+ export const integrationOauthPendingStates = pgTable(
3721
+ "integration_oauth_pending_states",
3722
+ {
3723
+ id: uuid("id").primaryKey(),
3724
+ accountId: uuid("account_id")
3725
+ .notNull()
3726
+ .references(() => managedAccounts.id, { onDelete: "cascade" }),
3727
+ workspaceId: uuid("workspace_id").notNull(),
3728
+ stateEncrypted: text("state_encrypted").notNull(),
3729
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
3730
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
3731
+ },
3732
+ (table) => ({
3733
+ workspaceAccount: foreignKey({
3734
+ name: "integration_oauth_pending_states_workspace_account_fk",
3735
+ columns: [table.workspaceId, table.accountId],
3736
+ foreignColumns: [workspaces.id, workspaces.accountId],
3737
+ }).onDelete("cascade"),
3738
+ expiry: index("integration_oauth_pending_states_expiry_idx").on(
3739
+ table.workspaceId,
3740
+ table.expiresAt,
3741
+ ),
3742
+ }),
3743
+ );
3744
+
3719
3745
  export const hostMcpBindings = pgTable(
3720
3746
  "host_mcp_bindings",
3721
3747
  {
@@ -5379,10 +5405,12 @@ export const sessionPins = pgTable(
5379
5405
  pinned: boolean("pinned").notNull().default(true),
5380
5406
  pinnedAt: timestamp("pinned_at", { withTimezone: true }).defaultNow(),
5381
5407
  version: integer("version").notNull().default(1),
5382
- // A session is unread for this subject whenever its durable event sequence
5383
- // has advanced beyond this explicit acknowledgment fence. Merely opening a
5384
- // route never changes the fence.
5408
+ // Compare the meaningful event frontier, not the raw durable cursor.
5409
+ // Merely opening a route never changes this per-subject fence.
5385
5410
  acknowledgedSequence: integer("acknowledged_sequence").notNull().default(0),
5411
+ // A replay at/before this raw event position cannot consume explicit intent.
5412
+ // A genuinely newer proven read, or an explicit mark-read, removes it.
5413
+ manuallyUnreadThrough: integer("manually_unread_through"),
5386
5414
  activelyWorking: boolean("actively_working").notNull().default(false),
5387
5415
  attentionVersion: integer("attention_version").notNull().default(0),
5388
5416
  archived: boolean("archived").notNull().default(false),
@@ -8824,6 +8852,9 @@ export const sessionEvents = pgTable(
8824
8852
  table.type,
8825
8853
  table.sequence,
8826
8854
  ),
8855
+ meaningfulAttention: index("session_events_meaningful_attention_idx")
8856
+ .on(table.workspaceId, table.sessionId, table.sequence)
8857
+ .where(meaningfulSessionEventSql("session_events")),
8827
8858
  workspaceTurnType: index("session_events_workspace_turn_type_idx")
8828
8859
  .on(table.workspaceId, table.turnId, table.type)
8829
8860
  .where(sql`${table.turnId} is not null`),
@@ -10235,6 +10266,7 @@ export const sessionBackgroundCommands = pgTable(
10235
10266
  connectionInstanceId: text("connection_instance_id"),
10236
10267
  opId: text("op_id"),
10237
10268
  commandPreview: text("command_preview").notNull().default(""),
10269
+ commandText: text("command_text"),
10238
10270
  cancelRequestedAt: timestamp("cancel_requested_at", { withTimezone: true }),
10239
10271
  cancelRequestedBy: text("cancel_requested_by"),
10240
10272
  exitCode: integer("exit_code"),
@@ -75,8 +75,11 @@ type ConnectedMachineBackgroundCommandClaimRow = {
75
75
  };
76
76
 
77
77
  function commandPreview(value: string): string {
78
- const normalized = value.replace(/\s+/g, " ").trim();
79
- return Array.from(normalized).slice(0, 512).join("");
78
+ if (value.length <= 512) return value;
79
+ let preview = value.slice(0, 511);
80
+ // Do not split a UTF-16 surrogate pair at the preview boundary.
81
+ if (/[\uD800-\uDBFF]$/.test(preview)) preview = preview.slice(0, -1);
82
+ return `${preview}…`;
80
83
  }
81
84
 
82
85
  const commandObservationUnavailable = sql<boolean>`
@@ -138,6 +141,7 @@ function mapCommand(
138
141
  ? { observationStatus: "unavailable" as const }
139
142
  : {}),
140
143
  commandPreview: row.commandPreview,
144
+ ...(row.commandText !== null ? { commandText: row.commandText } : {}),
141
145
  cancelRequestedAt: row.cancelRequestedAt?.toISOString() ?? null,
142
146
  exitCode: row.exitCode ?? null,
143
147
  settlementReason: row.settlementReason ?? null,
@@ -271,6 +275,7 @@ export async function insertManagedSessionBackgroundCommandInTransaction(
271
275
  state: "running",
272
276
  retainedProcessId: input.retainedProcessId,
273
277
  commandPreview: commandPreview(input.command),
278
+ commandText: input.command,
274
279
  launchTurnId: input.turnId ?? null,
275
280
  launchAttemptId: input.attemptId ?? null,
276
281
  launchExecutionGeneration: input.executionGeneration ?? null,
@@ -339,6 +344,7 @@ export async function insertConnectedMachineSessionBackgroundCommandInTransactio
339
344
  connectionInstanceId: input.connectionInstanceId,
340
345
  opId: input.opId,
341
346
  commandPreview: commandPreview(input.command),
347
+ commandText: input.command,
342
348
  launchTurnId: input.turnId ?? null,
343
349
  launchAttemptId: input.attemptId ?? null,
344
350
  launchExecutionGeneration: input.executionGeneration ?? null,
@@ -0,0 +1,97 @@
1
+ import { Column, getTableName, is, sql, type SQL, type SQLWrapper } from "drizzle-orm";
2
+
3
+ function correlatedReference(reference: SQLWrapper): SQLWrapper {
4
+ // Drizzle's single-table SELECT projection removes Column table qualifiers,
5
+ // including inside nested SQL. Here that would bind workspace_id/session_id
6
+ // to the inner event row and turn correlation into a tautology. Identifiers
7
+ // retain the exact outer table (or alias) through that projection rewrite.
8
+ return is(reference, Column)
9
+ ? sql`${sql.identifier(getTableName(reference.table))}.${sql.identifier(reference.name)}`
10
+ : reference;
11
+ }
12
+
13
+ /** Attention is conversational output or an actionable boundary, not activity.
14
+ * Keep this allow-list shared by rail, tree and consumption queries. */
15
+ export const MEANINGFUL_SESSION_EVENT_TYPES = [
16
+ "agent.message.completed",
17
+ "turn.completed",
18
+ "turn.failed",
19
+ "session.requiresAction",
20
+ "session.humanInput.requested",
21
+ "tool.auth_needed",
22
+ "credential.auth_needed",
23
+ "goal.completed",
24
+ "goal.paused",
25
+ "goal.progress",
26
+ "goal.rewrite.proposed",
27
+ "rig.setup.failed",
28
+ "sandbox.operation.failed",
29
+ "sandbox.box.lost",
30
+ "workspace.revision.degraded",
31
+ "machine.op.failed",
32
+ "machine.link.lost",
33
+ "session.event.envelope_omitted",
34
+ ] as const;
35
+
36
+ export function meaningfulSessionEventSql(alias: string): SQL {
37
+ const e = sql.identifier(alias);
38
+ // These are code-owned literals, not request values. Literal predicates let
39
+ // PostgreSQL use the matching partial index even with a generic cached plan.
40
+ return sql`${e}.type in (${sql.join(
41
+ MEANINGFUL_SESSION_EVENT_TYPES.map((type) => sql.raw(`'${type}'`)),
42
+ sql`, `,
43
+ )})
44
+ and ${e}.duplicate_of_event_id is null
45
+ and (${e}.turn_association is null or ${e}.turn_association = 'current')
46
+ and (${e}.type <> 'agent.message.completed' or coalesce(${e}.payload ->> 'text', '') <> '')
47
+ and (${e}.type <> 'turn.completed' or (
48
+ not (${e}.payload ?| array['maintenance', 'segmentLimit'])
49
+ and coalesce(nullif(${e}.payload -> 'output', 'null'::jsonb), ${e}.payload -> 'result') is not null
50
+ and coalesce(nullif(${e}.payload -> 'output', 'null'::jsonb), ${e}.payload -> 'result') not in ('null'::jsonb, '""'::jsonb)
51
+ ))`;
52
+ }
53
+
54
+ /** A stored audit preview can still need attention, but cannot stand in for
55
+ * consumption of the original content, even when the reader returned it whole. */
56
+ export function completeMeaningfulSessionEventSql(alias: string): SQL {
57
+ const e = sql.identifier(alias);
58
+ return sql`${meaningfulSessionEventSql(alias)}
59
+ and coalesce(${e}.payload -> 'truncation' ->> 'truncated', 'false') <> 'true'
60
+ and coalesce(${e}.payload ->> 'sourceOmitted', 'false') <> 'true'`;
61
+ }
62
+
63
+ /** One reverse partial-index probe, independent of the raw-delta tail length.
64
+ * Derivation also repairs historical bookkeeping-only dots without changing
65
+ * personal state or rewriting append-only events. */
66
+ export function meaningfulSessionSequenceSql(
67
+ workspaceId: SQLWrapper,
68
+ sessionId: SQLWrapper,
69
+ ): SQL<number> {
70
+ return sql<number>`coalesce((
71
+ select meaningful.sequence from session_events meaningful
72
+ where meaningful.workspace_id = ${correlatedReference(workspaceId)} and meaningful.session_id = ${correlatedReference(sessionId)}
73
+ and ${meaningfulSessionEventSql("meaningful")}
74
+ order by meaningful.sequence desc limit 1
75
+ ), 0)`;
76
+ }
77
+
78
+ /** Bound indexed candidate rows BEFORE testing payload size/completeness. Without
79
+ * this boundary, a long run of oversized answers can cause an unbounded scan. */
80
+ export function childLifecycleEvidenceCandidatesSql(
81
+ workspaceId: SQLWrapper,
82
+ sessionId: SQLWrapper,
83
+ ): SQL {
84
+ return sql`with candidates as materialized (
85
+ select meaningful.* from session_events meaningful
86
+ where meaningful.workspace_id = ${workspaceId} and meaningful.session_id = ${sessionId}
87
+ and ${meaningfulSessionEventSql("meaningful")}
88
+ order by meaningful.sequence desc limit 32
89
+ )
90
+ select candidates.sequence, candidates.type, candidates.payload,
91
+ candidates.payload_codec_version as "payloadCodecVersion" from candidates
92
+ where ${completeMeaningfulSessionEventSql("candidates")}
93
+ -- Inspection cap only: the 8 KiB evidence budget applies after logical
94
+ -- decoding in boundedChildLifecycleEvidence, not to this stored encoding.
95
+ and octet_length(candidates.payload::text) <= 65536
96
+ order by candidates.sequence desc`;
97
+ }
package/src/skills.ts CHANGED
@@ -222,6 +222,8 @@ export async function listSkillRecords(
222
222
  revisionId?: string;
223
223
  limit?: number;
224
224
  metadataOnly?: boolean;
225
+ /** Pending revisions authored by this exact session, independent of event retention. */
226
+ sessionId?: string;
225
227
  after?: { stableKey: string; id: string };
226
228
  } = {},
227
229
  ): Promise<SkillRecord[]> {
@@ -260,7 +262,7 @@ export async function listSkillRecords(
260
262
  SELECT candidate.* FROM preference_registry_revisions candidate
261
263
  WHERE candidate.preference_id=h.id AND candidate.account_id=h.account_id
262
264
  AND (${options.revisionId ?? null}::uuid IS NULL OR candidate.id=${options.revisionId ?? null}::uuid)
263
- ORDER BY (candidate.id=h.active_revision_id) DESC NULLS LAST,candidate.revision DESC LIMIT 1
265
+ ORDER BY (${options.sessionId ?? null}::uuid IS NULL AND candidate.id=h.active_revision_id) DESC NULLS LAST,candidate.revision DESC LIMIT 1
264
266
  ) r ON true
265
267
  LEFT JOIN skill_source_bindings b ON b.preference_id=h.id AND b.account_id=h.account_id
266
268
  AND b.workspace_id=${context.workspaceId}::uuid
@@ -269,6 +271,17 @@ export async function listSkillRecords(
269
271
  OR (h.scope='user' AND h.scope_subject_id=${context.subjectId ?? null}))
270
272
  AND (${options.skillId ?? null}::uuid IS NULL OR h.id=${options.skillId ?? null}::uuid)
271
273
  AND (${options.revisionId ?? null}::uuid IS NULL OR r.id IS NOT NULL)
274
+ AND (${options.sessionId ?? null}::uuid IS NULL OR EXISTS (
275
+ SELECT 1 FROM skill_write_receipts origin
276
+ WHERE origin.account_id=h.account_id AND origin.workspace_id=${context.workspaceId}::uuid
277
+ AND origin.actor->>'kind'='agent' AND origin.actor->>'sessionId'=${options.sessionId ?? null}
278
+ AND origin.receipt->>'skillId'=h.id::text AND origin.receipt->>'revisionId'=r.id::text
279
+ AND origin.receipt->>'outcome'='pending'
280
+ AND coalesce(origin.receipt->>'pendingReason','approval')='approval'
281
+ AND NOT EXISTS (SELECT 1 FROM preference_registry_events settled
282
+ WHERE settled.account_id=h.account_id AND settled.preference_id=h.id
283
+ AND settled.new_revision_id=r.id AND settled.type IN ('activated','corrected','rejected'))
284
+ ))
272
285
  AND (${options.after?.id ?? null}::uuid IS NULL OR
273
286
  (h.stable_key,h.id) > (${options.after?.stableKey ?? ""},${options.after?.id ?? null}::uuid))
274
287
  ORDER BY h.stable_key,h.id LIMIT ${Math.min(Math.max(options.limit ?? 128, 1), 1000)}