@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.
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-reconciliation.js +2 -2
- package/dist/child-read-reconciliation.d.ts +35 -0
- package/dist/{chunk-CJI6G254.js → chunk-4CCFFLDZ.js} +2 -2
- package/dist/{chunk-7JCDSMX2.js → chunk-4RDUZ5UV.js} +2 -2
- package/dist/{chunk-SYKHLPTK.js → chunk-6ORJPCYW.js} +2 -2
- package/dist/{chunk-GABXCG3N.js → chunk-CSVHCF3R.js} +11 -6
- package/dist/chunk-CSVHCF3R.js.map +1 -0
- package/dist/{chunk-CDAT6EKW.js → chunk-FULLE5CF.js} +3 -3
- package/dist/{chunk-MN5PZMEK.js → chunk-GSB4ISHE.js} +905 -801
- package/dist/chunk-GSB4ISHE.js.map +1 -0
- package/dist/{chunk-XIXVVBW2.js → chunk-H7BC444C.js} +3 -1
- package/dist/chunk-H7BC444C.js.map +1 -0
- package/dist/{chunk-LVY33EWL.js → chunk-I7HQL6FK.js} +3 -3
- package/dist/{chunk-RIYHP2WU.js → chunk-JILAWE26.js} +2 -2
- package/dist/{chunk-ZPJ6V2VG.js → chunk-LO6BVXXH.js} +2 -2
- package/dist/{chunk-PJI4MUZ7.js → chunk-LSLNMENV.js} +4 -4
- package/dist/{chunk-RL2MUXM3.js → chunk-PGIFHEBA.js} +2 -2
- package/dist/{chunk-G32NVMRJ.js → chunk-RXH5CFG3.js} +5 -5
- package/dist/{chunk-AJA2ZIUW.js → chunk-XK6HXON2.js} +2 -2
- package/dist/connect-attempts.js +3 -3
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +31 -0
- package/dist/index.js +6651 -6244
- package/dist/index.js.map +1 -1
- package/dist/integration-oauth-pending-states.d.ts +14 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-operations.js +2 -2
- package/dist/new-session-drafts.d.ts +2 -1
- package/dist/organization-integration-policy.js +3 -3
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.js +6 -6
- package/dist/runtime-posture.d.ts +2 -2
- package/dist/schema.d.ts +151 -8
- package/dist/schema.js +3 -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 +4 -4
- package/dist/session-meaningful-events.d.ts +15 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/skills.d.ts +2 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0503_session_meaningful_attention.sql +81 -0
- package/drizzle/0504_skill_folder_limits.sql +26 -0
- package/drizzle/0505_workspace_control_revision_frontier.sql +37 -0
- package/drizzle/0506_background_command_text.sql +3 -0
- package/drizzle/0507_integration_oauth_pending_states.sql +27 -0
- package/package.json +5 -5
- package/src/child-read-reconciliation.ts +170 -0
- package/src/index.ts +439 -60
- package/src/integration-oauth-pending-states.ts +52 -0
- package/src/new-session-drafts.ts +4 -2
- package/src/runtime-posture.ts +2 -0
- package/src/schema.ts +35 -3
- package/src/session-background-commands.ts +8 -2
- package/src/session-meaningful-events.ts +97 -0
- package/src/skills.ts +14 -1
- package/dist/chunk-GABXCG3N.js.map +0 -1
- package/dist/chunk-MN5PZMEK.js.map +0 -1
- package/dist/chunk-XIXVVBW2.js.map +0 -1
- /package/dist/{chunk-CJI6G254.js.map → chunk-4CCFFLDZ.js.map} +0 -0
- /package/dist/{chunk-7JCDSMX2.js.map → chunk-4RDUZ5UV.js.map} +0 -0
- /package/dist/{chunk-SYKHLPTK.js.map → chunk-6ORJPCYW.js.map} +0 -0
- /package/dist/{chunk-CDAT6EKW.js.map → chunk-FULLE5CF.js.map} +0 -0
- /package/dist/{chunk-LVY33EWL.js.map → chunk-I7HQL6FK.js.map} +0 -0
- /package/dist/{chunk-RIYHP2WU.js.map → chunk-JILAWE26.js.map} +0 -0
- /package/dist/{chunk-ZPJ6V2VG.js.map → chunk-LO6BVXXH.js.map} +0 -0
- /package/dist/{chunk-PJI4MUZ7.js.map → chunk-LSLNMENV.js.map} +0 -0
- /package/dist/{chunk-RL2MUXM3.js.map → chunk-PGIFHEBA.js.map} +0 -0
- /package/dist/{chunk-G32NVMRJ.js.map → chunk-RXH5CFG3.js.map} +0 -0
- /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
|
|
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 [
|
|
579
|
+
return [reusableNewSessionRepositoryResource(raw as RepositoryResourceRef)];
|
|
578
580
|
});
|
|
579
581
|
const [seeded] = await db
|
|
580
582
|
.update(schema.newSessionDrafts)
|
package/src/runtime-posture.ts
CHANGED
|
@@ -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
|
-
//
|
|
5383
|
-
//
|
|
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
|
-
|
|
79
|
-
|
|
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)}
|