@opengeni/db 4.3.2 → 4.3.3-canary.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/{chunk-UVBPOGA7.js → chunk-232VC2LW.js} +2 -2
- package/dist/{chunk-DVSLOUEI.js → chunk-2ZMTQVTR.js} +2 -2
- package/dist/{chunk-AI4NC6XW.js → chunk-6TKL3GQO.js} +47 -1
- package/dist/chunk-6TKL3GQO.js.map +1 -0
- package/dist/{chunk-F4BIHJ7J.js → chunk-EYF6MH6C.js} +4 -4
- package/dist/{chunk-I4ITNMX2.js → chunk-GVZJ7XSR.js} +16 -4
- package/dist/{chunk-I4ITNMX2.js.map → chunk-GVZJ7XSR.js.map} +1 -1
- package/dist/{chunk-XBXANJQS.js → chunk-M7PSMVFF.js} +2 -2
- package/dist/{chunk-PFBUHOI5.js → chunk-QLA3UQNX.js} +3 -3
- package/dist/{chunk-YVYBAWBU.js → chunk-THJGMEK5.js} +53 -1
- package/dist/chunk-THJGMEK5.js.map +1 -0
- package/dist/{chunk-UUPY6IGP.js → chunk-UK57QA2H.js} +2 -2
- package/dist/codex-selection-diagnostics.d.ts +12 -0
- package/dist/connection-token-resolver.d.ts +2 -0
- package/dist/database.d.ts +7 -2
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/host-mcp-bindings.d.ts +3 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +345 -75
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-operations.d.ts +95 -0
- package/dist/mcp-operations.js +95 -0
- package/dist/mcp-operations.js.map +1 -0
- package/dist/plugin-packages.d.ts +1 -0
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.js +2 -2
- package/dist/runtime-posture.d.ts +3 -3
- package/dist/sandbox-transition-wait.d.ts +10 -0
- package/dist/schema.d.ts +517 -0
- package/dist/schema.js +3 -1
- package/dist/session-background-commands.js +4 -4
- package/dist/session-command-output.js +4 -4
- package/dist/session-event-slices.js +2 -2
- package/dist/session-mcp-progress.d.ts +8 -1
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0459_mcp_operations.sql +396 -0
- package/drizzle/0460_host_export_message_attribution.sql +177 -0
- package/package.json +10 -6
- package/src/codex-selection-diagnostics.ts +25 -0
- package/src/computer-sessions.ts +5 -1
- package/src/connection-token-resolver.ts +2 -0
- package/src/database.ts +23 -2
- package/src/host-mcp-bindings.ts +14 -1
- package/src/index.ts +370 -60
- package/src/mcp-operations.ts +189 -0
- package/src/plugin-packages.ts +3 -0
- package/src/provision-roles.ts +6 -0
- package/src/runtime-posture.ts +50 -0
- package/src/sandbox-transition-wait.ts +45 -0
- package/src/schema.ts +46 -0
- package/src/session-mcp-progress.ts +26 -5
- package/dist/chunk-AI4NC6XW.js.map +0 -1
- package/dist/chunk-YVYBAWBU.js.map +0 -1
- /package/dist/{chunk-UVBPOGA7.js.map → chunk-232VC2LW.js.map} +0 -0
- /package/dist/{chunk-DVSLOUEI.js.map → chunk-2ZMTQVTR.js.map} +0 -0
- /package/dist/{chunk-F4BIHJ7J.js.map → chunk-EYF6MH6C.js.map} +0 -0
- /package/dist/{chunk-XBXANJQS.js.map → chunk-M7PSMVFF.js.map} +0 -0
- /package/dist/{chunk-PFBUHOI5.js.map → chunk-QLA3UQNX.js.map} +0 -0
- /package/dist/{chunk-UUPY6IGP.js.map → chunk-UK57QA2H.js.map} +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { AttemptToolResult } from "@opengeni/contracts";
|
|
2
|
+
import { sql } from "drizzle-orm";
|
|
3
|
+
import { type Database, withRlsContext } from "./database";
|
|
4
|
+
import {
|
|
5
|
+
fromPostgresLosslessJson,
|
|
6
|
+
toPostgresLosslessJson,
|
|
7
|
+
LOSSLESS_CONTENT_CODEC_VERSION,
|
|
8
|
+
} from "./lossless-json";
|
|
9
|
+
|
|
10
|
+
/** Worker-authenticated invocation identity, never model/HTTP/request JSON.
|
|
11
|
+
* Possessing a DB handle or matching GUCs does NOT authenticate a caller.
|
|
12
|
+
* Bind this tuple only after verifying the worker invocation credential. SQL
|
|
13
|
+
* then derives the principal and rechecks canonical live disclosure authority. */
|
|
14
|
+
export type McpOperationAttempt = {
|
|
15
|
+
accountId: string;
|
|
16
|
+
workspaceId: string;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
turnId: string;
|
|
19
|
+
attemptId: string;
|
|
20
|
+
executionGeneration: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Structurally aligned with runtime CapturedMcpOperation, without a runtime dependency. */
|
|
24
|
+
export type CaptureMcpOperationInput = {
|
|
25
|
+
operationId: string;
|
|
26
|
+
sourceCallId?: string;
|
|
27
|
+
serverId: string;
|
|
28
|
+
originalTool: string;
|
|
29
|
+
observerTool: string;
|
|
30
|
+
destinationDigest: string;
|
|
31
|
+
argumentDigest: string;
|
|
32
|
+
authorityDigest: string;
|
|
33
|
+
};
|
|
34
|
+
export type McpOperationSelector =
|
|
35
|
+
| { operationId: string }
|
|
36
|
+
| { sourceTurnId: string; sourceCallId: string };
|
|
37
|
+
export type McpOperationRecord = Omit<CaptureMcpOperationInput, "sourceCallId"> & {
|
|
38
|
+
sourceCallId: string | null;
|
|
39
|
+
accountId: string;
|
|
40
|
+
workspaceId: string;
|
|
41
|
+
sessionId: string;
|
|
42
|
+
sourceTurnId: string;
|
|
43
|
+
sourceAttemptId: string;
|
|
44
|
+
sourceExecutionGeneration: number;
|
|
45
|
+
principalKind: "subject" | "service";
|
|
46
|
+
principalId: string;
|
|
47
|
+
/** captured means possibly dispatched, NEVER permission to replay a mutation. */
|
|
48
|
+
originalOutcome: "captured" | "completed" | "outcome_unknown";
|
|
49
|
+
originalResult: AttemptToolResult | null;
|
|
50
|
+
observationResult: AttemptToolResult | null;
|
|
51
|
+
receiptRevision: string | null;
|
|
52
|
+
receiptDigest: string | null;
|
|
53
|
+
};
|
|
54
|
+
export type McpOperationLookup =
|
|
55
|
+
| { status: "found"; operation: McpOperationRecord }
|
|
56
|
+
| { status: "not_found" | "ambiguous" };
|
|
57
|
+
|
|
58
|
+
async function command<T>(
|
|
59
|
+
db: Database,
|
|
60
|
+
attempt: McpOperationAttempt,
|
|
61
|
+
action: string,
|
|
62
|
+
payload: object,
|
|
63
|
+
): Promise<T> {
|
|
64
|
+
return withRlsContext(
|
|
65
|
+
db,
|
|
66
|
+
{ accountId: attempt.accountId, workspaceId: attempt.workspaceId },
|
|
67
|
+
async (tx) => {
|
|
68
|
+
const result =
|
|
69
|
+
await tx.execute(sql`select mcp_operation_command(${JSON.stringify(attempt)}::jsonb,
|
|
70
|
+
${action}::text, ${JSON.stringify(payload)}::jsonb) as value`);
|
|
71
|
+
const rows = Array.isArray(result) ? result : result.rows;
|
|
72
|
+
if (!rows?.[0]) throw new Error("MCP operation command returned no receipt");
|
|
73
|
+
return rows[0].value as T;
|
|
74
|
+
},
|
|
75
|
+
undefined,
|
|
76
|
+
"none", // SQL owns membership -> tenancy -> control -> session lock order.
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function captureMcpOperation(
|
|
81
|
+
db: Database,
|
|
82
|
+
attempt: McpOperationAttempt,
|
|
83
|
+
operation: CaptureMcpOperationInput,
|
|
84
|
+
) {
|
|
85
|
+
return command<"created" | "existing">(db, attempt, "capture", operation);
|
|
86
|
+
}
|
|
87
|
+
export async function settleOriginalMcpOperation(
|
|
88
|
+
db: Database,
|
|
89
|
+
attempt: McpOperationAttempt,
|
|
90
|
+
input:
|
|
91
|
+
| { operationId: string; outcome: "completed"; result: AttemptToolResult }
|
|
92
|
+
| { operationId: string; outcome: "outcome_unknown" },
|
|
93
|
+
): Promise<void> {
|
|
94
|
+
const payload =
|
|
95
|
+
input.outcome === "completed"
|
|
96
|
+
? {
|
|
97
|
+
...input,
|
|
98
|
+
result: toPostgresLosslessJson(AttemptToolResult.parse(input.result)),
|
|
99
|
+
resultCodecVersion: LOSSLESS_CONTENT_CODEC_VERSION,
|
|
100
|
+
}
|
|
101
|
+
: input;
|
|
102
|
+
const result = await command<{ status: string }>(db, attempt, "settle_original", payload);
|
|
103
|
+
if (result.status !== "settled") throw new Error("Original MCP operation not found");
|
|
104
|
+
}
|
|
105
|
+
export async function readMcpOperation(
|
|
106
|
+
db: Database,
|
|
107
|
+
attempt: McpOperationAttempt,
|
|
108
|
+
selector: McpOperationSelector,
|
|
109
|
+
): Promise<McpOperationLookup> {
|
|
110
|
+
const result = await command<McpOperationLookup>(db, attempt, "read", selector);
|
|
111
|
+
if (result.status !== "found") return result;
|
|
112
|
+
const { originalResultCodecVersion, observationResultCodecVersion, ...operation } =
|
|
113
|
+
result.operation as McpOperationRecord & {
|
|
114
|
+
originalResultCodecVersion: number | null;
|
|
115
|
+
observationResultCodecVersion: number | null;
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
status: "found",
|
|
119
|
+
operation: {
|
|
120
|
+
...operation,
|
|
121
|
+
originalResult: fromPostgresLosslessJson(
|
|
122
|
+
operation.originalResult,
|
|
123
|
+
originalResultCodecVersion,
|
|
124
|
+
),
|
|
125
|
+
observationResult: fromPostgresLosslessJson(
|
|
126
|
+
operation.observationResult,
|
|
127
|
+
observationResultCodecVersion,
|
|
128
|
+
),
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** Claims authorize only the read observer; expiry never authorizes a mutation. */
|
|
133
|
+
export function claimMcpOperationObservation(
|
|
134
|
+
db: Database,
|
|
135
|
+
attempt: McpOperationAttempt,
|
|
136
|
+
operationId: string,
|
|
137
|
+
) {
|
|
138
|
+
return command<
|
|
139
|
+
{ status: "claimed"; claimId: string } | { status: "busy" | "terminal" | "not_found" }
|
|
140
|
+
>(db, attempt, "claim_read", { operationId });
|
|
141
|
+
}
|
|
142
|
+
export function settleMcpOperationObservation(
|
|
143
|
+
db: Database,
|
|
144
|
+
attempt: McpOperationAttempt,
|
|
145
|
+
input: {
|
|
146
|
+
operationId: string;
|
|
147
|
+
claimId: string;
|
|
148
|
+
receiptRevision: string;
|
|
149
|
+
result: AttemptToolResult;
|
|
150
|
+
},
|
|
151
|
+
) {
|
|
152
|
+
// This atomically stores one receipt, not an autonomous session notification.
|
|
153
|
+
// Explicit operation_read is a new ordinary tool call: its existing result
|
|
154
|
+
// lifecycle delivers the evidence. Repeated explicit reads legitimately have
|
|
155
|
+
// distinct call outputs while the original timeout and receipt stay immutable.
|
|
156
|
+
return command<
|
|
157
|
+
| { status: "settled" | "existing" | "conflict"; receiptDigest: string }
|
|
158
|
+
| { status: "stale_claim" | "not_found" | "original_completed" }
|
|
159
|
+
>(db, attempt, "settle_observation", {
|
|
160
|
+
...input,
|
|
161
|
+
result: toPostgresLosslessJson(AttemptToolResult.parse(input.result)),
|
|
162
|
+
resultCodecVersion: LOSSLESS_CONTENT_CODEC_VERSION,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Unknown/pending/error cleanup only; never releases or replaces a receipt. */
|
|
167
|
+
export function releaseMcpOperationObservation(
|
|
168
|
+
db: Database,
|
|
169
|
+
attempt: McpOperationAttempt,
|
|
170
|
+
input: { operationId: string; claimId: string },
|
|
171
|
+
) {
|
|
172
|
+
return command<{ status: "released" | "stale_claim" | "terminal" | "not_found" }>(
|
|
173
|
+
db,
|
|
174
|
+
attempt,
|
|
175
|
+
"release_read",
|
|
176
|
+
input,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Bind only inside the trusted worker, after exact invocation authentication.
|
|
181
|
+
* Provider authorization and immutable binding fences remain the broker's job. */
|
|
182
|
+
export function createMcpOperationPersistence(db: Database, attempt: McpOperationAttempt) {
|
|
183
|
+
const frozen = { ...attempt };
|
|
184
|
+
return {
|
|
185
|
+
capture: (operation: CaptureMcpOperationInput) => captureMcpOperation(db, frozen, operation),
|
|
186
|
+
settleOriginal: (input: Parameters<typeof settleOriginalMcpOperation>[2]) =>
|
|
187
|
+
settleOriginalMcpOperation(db, frozen, input),
|
|
188
|
+
};
|
|
189
|
+
}
|
package/src/plugin-packages.ts
CHANGED
|
@@ -48,6 +48,7 @@ export type InstalledPluginPackageSummary = {
|
|
|
48
48
|
category: string;
|
|
49
49
|
tags: string[];
|
|
50
50
|
sourceUrl: string | null;
|
|
51
|
+
logoUrl: string | null;
|
|
51
52
|
manifestDigest: string;
|
|
52
53
|
installationVersion: number;
|
|
53
54
|
componentCount: number;
|
|
@@ -158,6 +159,7 @@ export async function listInstalledPluginPackages(
|
|
|
158
159
|
return rows.map((row) => {
|
|
159
160
|
const manifest = objectValue(row.manifest);
|
|
160
161
|
const sourceUrl = stringValue(manifest.sourceUrl);
|
|
162
|
+
const logoUrl = stringValue(objectValue(manifest.discovery).logoUrl);
|
|
161
163
|
const bom = pluginBom(manifest);
|
|
162
164
|
if (row.status !== "active" && row.status !== "needs_attention") {
|
|
163
165
|
throw new Error(`Unknown installed Plugin status: ${row.status}`);
|
|
@@ -173,6 +175,7 @@ export async function listInstalledPluginPackages(
|
|
|
173
175
|
? stringArray(manifest.tags)
|
|
174
176
|
: stringArray(row.tags),
|
|
175
177
|
sourceUrl: sourceUrl && safeHttpUrl(sourceUrl) ? sourceUrl : null,
|
|
178
|
+
logoUrl: logoUrl && safeHttpUrl(logoUrl) ? logoUrl : null,
|
|
176
179
|
manifestDigest: row.manifestDigest,
|
|
177
180
|
installationVersion: row.installationVersion,
|
|
178
181
|
componentCount: bom.length,
|
package/src/provision-roles.ts
CHANGED
|
@@ -618,6 +618,10 @@ DECLARE
|
|
|
618
618
|
BEGIN
|
|
619
619
|
EXECUTE format('REVOKE CREATE ON DATABASE %I FROM %I', current_database(), ${literal(role)});
|
|
620
620
|
IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = ${literal(schema)}) THEN
|
|
621
|
+
IF to_regprocedure(format('%I.mcp_operation_command(jsonb,text,jsonb)', ${literal(schema)})) IS NOT NULL THEN
|
|
622
|
+
EXECUTE format('REVOKE ALL ON FUNCTION %I.mcp_operation_command(jsonb,text,jsonb) FROM PUBLIC', ${literal(schema)});
|
|
623
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.mcp_operation_command(jsonb,text,jsonb) TO %I', ${literal(schema)}, ${literal(role)});
|
|
624
|
+
END IF;
|
|
621
625
|
EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', ${literal(schema)}, ${literal(role)});
|
|
622
626
|
EXECUTE format('REVOKE CREATE ON SCHEMA %I FROM %I', ${literal(schema)}, ${literal(role)});
|
|
623
627
|
EXECUTE format('REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA %I FROM %I', ${literal(schema)}, ${literal(role)});
|
|
@@ -2120,6 +2124,8 @@ BEGIN
|
|
|
2120
2124
|
EXECUTE format('REVOKE CREATE ON SCHEMA opengeni_private FROM %I', ${literal(role)});
|
|
2121
2125
|
EXECUTE format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA opengeni_private TO %I', ${literal(role)});
|
|
2122
2126
|
FOREACH routine_signature IN ARRAY ARRAY[
|
|
2127
|
+
'guard_mcp_operation_immutable()',
|
|
2128
|
+
'mcp_operation_command_scoped(jsonb,text,jsonb)',
|
|
2123
2129
|
'guard_workspace_owned_skill_head_delete()',
|
|
2124
2130
|
'guard_workspace_owned_skill_history_delete()'
|
|
2125
2131
|
] LOOP
|
package/src/runtime-posture.ts
CHANGED
|
@@ -51,7 +51,24 @@ const AUTOMATIC_SESSION_TITLE_FANOUT_OUTBOX_TABLE = "automatic_session_title_fan
|
|
|
51
51
|
const AUTOMATIC_SESSION_TITLE_QUARANTINE_FENCE_ROUTINE =
|
|
52
52
|
"acquire_automatic_session_title_quarantine_fences_v1(integer)";
|
|
53
53
|
|
|
54
|
+
const MCP_OPERATION_CAPABILITY_ROUTINE = "mcp_operation_command(jsonb, text, jsonb)";
|
|
55
|
+
const MCP_OPERATION_AUTHORITY_TABLES = [
|
|
56
|
+
"mcp_operations",
|
|
57
|
+
"sessions",
|
|
58
|
+
"session_turns",
|
|
59
|
+
"session_turn_attempts",
|
|
60
|
+
"workspaces",
|
|
61
|
+
"workspace_inference_controls",
|
|
62
|
+
"organization_memberships",
|
|
63
|
+
"workspace_memberships",
|
|
64
|
+
"external_identity_links",
|
|
65
|
+
"external_link_turn_authorities",
|
|
66
|
+
"host_mcp_turn_authorities",
|
|
67
|
+
"scheduled_task_runs",
|
|
68
|
+
] as const;
|
|
54
69
|
const OWNER_INTERNAL_PRIVATE_ROUTINES = new Set<string>([
|
|
70
|
+
"guard_mcp_operation_immutable()",
|
|
71
|
+
"mcp_operation_command_scoped(jsonb, text, jsonb)",
|
|
55
72
|
"guard_workspace_owned_skill_head_delete()",
|
|
56
73
|
"guard_workspace_owned_skill_history_delete()",
|
|
57
74
|
...ARTIFACT_OUTBOX_CAPABILITY_ROUTINES,
|
|
@@ -557,6 +574,7 @@ const XAI_AUTHORITY_TABLES = [
|
|
|
557
574
|
] as const;
|
|
558
575
|
|
|
559
576
|
export const RUNTIME_TARGET_SCHEMA_CAPABILITY_ROUTINES = [
|
|
577
|
+
MCP_OPERATION_CAPABILITY_ROUTINE,
|
|
560
578
|
"skill_apply_lifecycle(uuid, uuid, jsonb, jsonb)",
|
|
561
579
|
COMPANY_BRAIN_CONTEXT_INSPECTION_ROUTINE,
|
|
562
580
|
COMPANY_BRAIN_CONTEXT_SELECTION_ROUTINE,
|
|
@@ -807,6 +825,7 @@ export const FORCE_RLS_TABLES = [
|
|
|
807
825
|
"managed_auth_login_transactions",
|
|
808
826
|
"managed_auth_session_set_operations",
|
|
809
827
|
"managed_auth_session_sets",
|
|
828
|
+
"mcp_operations",
|
|
810
829
|
"memory_slack_publication_configurations",
|
|
811
830
|
"memory_slack_publication_receipts",
|
|
812
831
|
"memory_slack_publications",
|
|
@@ -1381,6 +1400,7 @@ export const PROTECTED_NO_DIRECT_DML_TABLES = [
|
|
|
1381
1400
|
"managed_auth_login_transactions",
|
|
1382
1401
|
"managed_auth_session_set_operations",
|
|
1383
1402
|
"managed_auth_session_sets",
|
|
1403
|
+
"mcp_operations",
|
|
1384
1404
|
"organization_company_profile_agent_policies",
|
|
1385
1405
|
"organization_company_profile_agent_policy_events",
|
|
1386
1406
|
"organization_invitation_binding_events",
|
|
@@ -2231,6 +2251,19 @@ export function evaluateRuntimeDatabasePosture(
|
|
|
2231
2251
|
);
|
|
2232
2252
|
}
|
|
2233
2253
|
}
|
|
2254
|
+
} else if (routine.name === MCP_OPERATION_CAPABILITY_ROUTINE) {
|
|
2255
|
+
const missing = MCP_OPERATION_AUTHORITY_TABLES.filter((name) => !tableByName.has(name));
|
|
2256
|
+
if (missing.length > 0) {
|
|
2257
|
+
violations.push(`MCP operation authority tables are missing: ${missing.join(", ")}`);
|
|
2258
|
+
} else if (
|
|
2259
|
+
MCP_OPERATION_AUTHORITY_TABLES.some(
|
|
2260
|
+
(name) => tableByName.get(name)!.owner !== routine.owner,
|
|
2261
|
+
)
|
|
2262
|
+
) {
|
|
2263
|
+
violations.push(
|
|
2264
|
+
`MCP operation capability ${routine.name} authority table owners do not match`,
|
|
2265
|
+
);
|
|
2266
|
+
}
|
|
2234
2267
|
} else if (routine.name === KNOWLEDGE_SOURCE_SYNC_LOCK_AUTHORITY_ROUTINE) {
|
|
2235
2268
|
const missingAuthorityTables = KNOWLEDGE_SOURCE_SYNC_LOCK_AUTHORITY_TABLES.filter(
|
|
2236
2269
|
(tableName) => !tableByName.has(tableName),
|
|
@@ -3385,6 +3418,23 @@ export function evaluateRuntimeDatabasePosture(
|
|
|
3385
3418
|
violations.push(`runtime role owns private routine ${routine.name}`);
|
|
3386
3419
|
}
|
|
3387
3420
|
const ownerInternalRoutine = OWNER_INTERNAL_PRIVATE_ROUTINES.has(routine.name);
|
|
3421
|
+
if (
|
|
3422
|
+
[
|
|
3423
|
+
"guard_mcp_operation_immutable()",
|
|
3424
|
+
"mcp_operation_command_scoped(jsonb, text, jsonb)",
|
|
3425
|
+
].includes(routine.name)
|
|
3426
|
+
) {
|
|
3427
|
+
if (routine.execute || routine.publicExecute) {
|
|
3428
|
+
violations.push(
|
|
3429
|
+
`runtime or PUBLIC has forbidden EXECUTE on MCP operation internal routine ${routine.name}`,
|
|
3430
|
+
);
|
|
3431
|
+
}
|
|
3432
|
+
if (routine.owner !== tableByName.get("mcp_operations")?.owner) {
|
|
3433
|
+
violations.push(
|
|
3434
|
+
`MCP operation internal routine ${routine.name} owner does not match ledger owner`,
|
|
3435
|
+
);
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3388
3438
|
if (
|
|
3389
3439
|
[
|
|
3390
3440
|
"guard_workspace_owned_skill_head_delete()",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS,
|
|
3
|
+
SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS,
|
|
4
|
+
} from "@opengeni/config";
|
|
5
|
+
|
|
6
|
+
/** Observational only: expiring a caller's wait never revokes a capture fence. */
|
|
7
|
+
export class SandboxTransitionWaitBudget {
|
|
8
|
+
private observedCapture = false;
|
|
9
|
+
private currentDeadline: number;
|
|
10
|
+
private readonly hardDeadline: number;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly waitMs: number,
|
|
14
|
+
startedAt: number,
|
|
15
|
+
) {
|
|
16
|
+
this.hardDeadline = startedAt + SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS;
|
|
17
|
+
this.currentDeadline = Math.min(this.hardDeadline, startedAt + waitMs);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get deadline(): number {
|
|
21
|
+
return this.currentDeadline;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
observeCapture(remainingMs: number | null | undefined, now: number): void {
|
|
25
|
+
if (
|
|
26
|
+
this.waitMs === 0 ||
|
|
27
|
+
this.observedCapture ||
|
|
28
|
+
remainingMs === null ||
|
|
29
|
+
remainingMs === undefined ||
|
|
30
|
+
!Number.isSafeInteger(remainingMs) ||
|
|
31
|
+
remainingMs < 0 ||
|
|
32
|
+
remainingMs > SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS
|
|
33
|
+
)
|
|
34
|
+
return;
|
|
35
|
+
|
|
36
|
+
// Honor the first observed child's frozen timeout across rolling settings
|
|
37
|
+
// changes. Neither an expired claim (remaining=0) nor a replacement capture
|
|
38
|
+
// can replenish the grace/budget on every poll and starve this caller.
|
|
39
|
+
this.observedCapture = true;
|
|
40
|
+
this.currentDeadline = Math.min(
|
|
41
|
+
this.hardDeadline,
|
|
42
|
+
Math.max(this.currentDeadline, now + remainingMs + SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/schema.ts
CHANGED
|
@@ -6928,6 +6928,52 @@ export const sessionRealtimeContextProjections = pgTable(
|
|
|
6928
6928
|
// First-class ownership for one accepted execution attempt. A workflow may
|
|
6929
6929
|
// preallocate id, but this row is inserted only by the activity transaction
|
|
6930
6930
|
// that actually claims the logical turn and registers its exact dispatch.
|
|
6931
|
+
// Protected EXECUTE-only ledger. SQL owns live-attempt/principal derivation,
|
|
6932
|
+
// immutable settlement, bounds, RLS and source ownership constraints.
|
|
6933
|
+
export const mcpOperations = pgTable(
|
|
6934
|
+
"mcp_operations",
|
|
6935
|
+
{
|
|
6936
|
+
operationId: uuid("operation_id").primaryKey(),
|
|
6937
|
+
accountId: uuid("account_id").notNull(),
|
|
6938
|
+
workspaceId: uuid("workspace_id").notNull(),
|
|
6939
|
+
sessionId: uuid("session_id").notNull(),
|
|
6940
|
+
sourceTurnId: uuid("source_turn_id").notNull(),
|
|
6941
|
+
sourceAttemptId: uuid("source_attempt_id").notNull(),
|
|
6942
|
+
sourceExecutionGeneration: integer("source_execution_generation").notNull(),
|
|
6943
|
+
sourceCallId: text("source_call_id"),
|
|
6944
|
+
principalKind: text("principal_kind").notNull(),
|
|
6945
|
+
principalId: text("principal_id").notNull(),
|
|
6946
|
+
principalMembershipId: uuid("principal_membership_id"),
|
|
6947
|
+
principalMembershipRevision: bigint("principal_membership_revision", { mode: "number" }),
|
|
6948
|
+
serverId: text("server_id").notNull(),
|
|
6949
|
+
originalTool: text("original_tool").notNull(),
|
|
6950
|
+
observerTool: text("observer_tool").notNull(),
|
|
6951
|
+
argumentDigest: text("argument_digest").notNull(),
|
|
6952
|
+
destinationDigest: text("destination_digest").notNull(),
|
|
6953
|
+
authorityDigest: text("authority_digest").notNull(),
|
|
6954
|
+
originalOutcome: text("original_outcome").notNull().default("captured"),
|
|
6955
|
+
originalResult: jsonb("original_result"),
|
|
6956
|
+
originalResultCodecVersion: integer("original_result_codec_version"),
|
|
6957
|
+
observationResult: jsonb("observation_result"),
|
|
6958
|
+
observationResultCodecVersion: integer("observation_result_codec_version"),
|
|
6959
|
+
receiptRevision: text("receipt_revision"),
|
|
6960
|
+
receiptDigest: text("receipt_digest"),
|
|
6961
|
+
observationClaimId: uuid("observation_claim_id"),
|
|
6962
|
+
observationClaimAttemptId: uuid("observation_claim_attempt_id"),
|
|
6963
|
+
observationClaimExpiresAt: timestamp("observation_claim_expires_at", { withTimezone: true }),
|
|
6964
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
6965
|
+
observedAt: timestamp("observed_at", { withTimezone: true }),
|
|
6966
|
+
},
|
|
6967
|
+
(table) => ({
|
|
6968
|
+
source: index("mcp_operations_source_idx").on(
|
|
6969
|
+
table.workspaceId,
|
|
6970
|
+
table.sessionId,
|
|
6971
|
+
table.sourceTurnId,
|
|
6972
|
+
table.sourceCallId,
|
|
6973
|
+
),
|
|
6974
|
+
}),
|
|
6975
|
+
);
|
|
6976
|
+
|
|
6931
6977
|
export const sessionTurnAttempts = pgTable(
|
|
6932
6978
|
"session_turn_attempts",
|
|
6933
6979
|
{
|
|
@@ -11,8 +11,12 @@ export const SESSION_MCP_PROGRESS_CHARS = 600;
|
|
|
11
11
|
// so a prefix cannot split the last delivered surrogate pair. Eight base64
|
|
12
12
|
// characters encode three complete UTF-16 units, so a cut encoded prefix is
|
|
13
13
|
// independently decodable by the canonical codec (no partial bytes/units).
|
|
14
|
-
export
|
|
15
|
-
LOSSLESS_JSON_STRING_PREFIX.length + Math.ceil(((
|
|
14
|
+
export function sessionTextStoragePrefixChars(maxChars: number): number {
|
|
15
|
+
return LOSSLESS_JSON_STRING_PREFIX.length + Math.ceil(((maxChars + 1) * 2) / 3) * 8;
|
|
16
|
+
}
|
|
17
|
+
export const SESSION_MCP_PROGRESS_STORAGE_CHARS = sessionTextStoragePrefixChars(
|
|
18
|
+
SESSION_MCP_PROGRESS_CHARS,
|
|
19
|
+
);
|
|
16
20
|
|
|
17
21
|
// Match the canonical decoder's nonempty, round-tripping base64 of an even
|
|
18
22
|
// byte count. Each 8-character block is 6 bytes. The final block contributes
|
|
@@ -38,12 +42,13 @@ export function sessionMcpProgressScalarIsEncodedSql(
|
|
|
38
42
|
)`;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
/** Project only the selected scalar, never the rest of
|
|
42
|
-
export function
|
|
45
|
+
/** Project only the selected scalar, never the rest of its event payload. */
|
|
46
|
+
export function projectSessionTextPrefix(
|
|
43
47
|
storedPrefix: string | null,
|
|
44
48
|
storedChars: number | null,
|
|
45
49
|
codecVersion: number | null,
|
|
46
50
|
scalarIsEncoded: boolean,
|
|
51
|
+
maxChars: number,
|
|
47
52
|
): { text: string | null; originalChars: number | null; textTruncated?: true } {
|
|
48
53
|
if (storedPrefix === null) return { text: null, originalChars: null };
|
|
49
54
|
const decoded = scalarIsEncoded
|
|
@@ -60,8 +65,24 @@ export function projectSessionMcpProgressText(
|
|
|
60
65
|
? storedChars
|
|
61
66
|
: chars.length;
|
|
62
67
|
return {
|
|
63
|
-
text: chars.slice(0,
|
|
68
|
+
text: chars.slice(0, maxChars).join(""),
|
|
64
69
|
originalChars,
|
|
65
70
|
...(encodedPrefixWasCut ? { textTruncated: true } : {}),
|
|
66
71
|
};
|
|
67
72
|
}
|
|
73
|
+
|
|
74
|
+
/** Existing MCP progress budget, using the shared logical-text prefix projection. */
|
|
75
|
+
export function projectSessionMcpProgressText(
|
|
76
|
+
storedPrefix: string | null,
|
|
77
|
+
storedChars: number | null,
|
|
78
|
+
codecVersion: number | null,
|
|
79
|
+
scalarIsEncoded: boolean,
|
|
80
|
+
): { text: string | null; originalChars: number | null; textTruncated?: true } {
|
|
81
|
+
return projectSessionTextPrefix(
|
|
82
|
+
storedPrefix,
|
|
83
|
+
storedChars,
|
|
84
|
+
codecVersion,
|
|
85
|
+
scalarIsEncoded,
|
|
86
|
+
SESSION_MCP_PROGRESS_CHARS,
|
|
87
|
+
);
|
|
88
|
+
}
|