@opengeni/db 5.0.1 → 6.0.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/automations.d.ts +0 -2
- package/dist/canonical-human-identities.d.ts +4 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-components.d.ts +5 -13
- package/dist/capability-integrations.d.ts +23 -8
- package/dist/capability-reconciliation.d.ts +5 -0
- package/dist/capability-reconciliation.js +21 -0
- package/dist/capability-reconciliation.js.map +1 -0
- package/dist/{chunk-6T2GGYWE.js → chunk-4UJCXBR2.js} +4 -5800
- package/dist/chunk-4UJCXBR2.js.map +1 -0
- package/dist/{chunk-MWMXWHJD.js → chunk-7JCDSMX2.js} +2 -2
- package/dist/chunk-AJA2ZIUW.js +219 -0
- package/dist/chunk-AJA2ZIUW.js.map +1 -0
- package/dist/chunk-CDAT6EKW.js +247 -0
- package/dist/chunk-CDAT6EKW.js.map +1 -0
- package/dist/{chunk-DPONPYJD.js → chunk-CJI6G254.js} +70 -18
- package/dist/chunk-CJI6G254.js.map +1 -0
- package/dist/chunk-G32NVMRJ.js +276 -0
- package/dist/chunk-G32NVMRJ.js.map +1 -0
- package/dist/chunk-GABXCG3N.js +5811 -0
- package/dist/chunk-GABXCG3N.js.map +1 -0
- package/dist/{chunk-ERKVSPDX.js → chunk-LVY33EWL.js} +3 -3
- package/dist/{chunk-45CP2YKY.js → chunk-MAMBI2HR.js} +9 -3
- package/dist/{chunk-45CP2YKY.js.map → chunk-MAMBI2HR.js.map} +1 -1
- package/dist/{chunk-F73UKJY6.js → chunk-MN5PZMEK.js} +91 -108
- package/dist/chunk-MN5PZMEK.js.map +1 -0
- package/dist/chunk-PJI4MUZ7.js +131 -0
- package/dist/chunk-PJI4MUZ7.js.map +1 -0
- package/dist/{chunk-T6BWIGWJ.js → chunk-RIYHP2WU.js} +2 -2
- package/dist/{chunk-7HMK3B45.js → chunk-RL2MUXM3.js} +16 -7
- package/dist/chunk-RL2MUXM3.js.map +1 -0
- package/dist/{chunk-HQDVI2DB.js → chunk-SYKHLPTK.js} +4 -3
- package/dist/{chunk-HQDVI2DB.js.map → chunk-SYKHLPTK.js.map} +1 -1
- package/dist/{chunk-T6YLHJLS.js → chunk-XIXVVBW2.js} +198 -51
- package/dist/chunk-XIXVVBW2.js.map +1 -0
- package/dist/chunk-ZPJ6V2VG.js +156 -0
- package/dist/chunk-ZPJ6V2VG.js.map +1 -0
- package/dist/codex-capacity-recovery.d.ts +12 -0
- package/dist/codex-failure-eligibility.d.ts +9 -0
- package/dist/company-profile-schema.d.ts +4 -2
- package/dist/connect-attempts.d.ts +15 -0
- package/dist/connect-attempts.js +25 -0
- package/dist/connect-attempts.js.map +1 -0
- package/dist/connection-authority.d.ts +7 -63
- package/dist/connection-refresh-lock.d.ts +4 -0
- package/dist/connection-token-resolver.d.ts +8 -43
- package/dist/database-timing.d.ts +10 -0
- package/dist/database.d.ts +4 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +9 -0
- package/dist/editable-artifacts.js +7 -5
- package/dist/index.d.ts +447 -78
- package/dist/index.js +12705 -12872
- package/dist/index.js.map +1 -1
- package/dist/insights-usage-bundle.d.ts +3 -0
- package/dist/integration-bindings.d.ts +1 -1
- package/dist/integration-facets.d.ts +18 -1
- package/dist/knowledge-entries-schema.d.ts +2 -0
- package/dist/knowledge-entries.d.ts +1 -0
- package/dist/knowledge-indexing.d.ts +1 -0
- package/dist/live-session-attempt.d.ts +35 -137
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-account-bindings.d.ts +4 -0
- package/dist/mcp-oauth.d.ts +11 -0
- package/dist/mcp-operations.js +2 -2
- package/dist/migrate.js +1 -1
- package/dist/organization-integration-policy.d.ts +51 -0
- package/dist/organization-integration-policy.js +18 -0
- package/dist/organization-integration-policy.js.map +1 -0
- package/dist/organization-usage.d.ts +20 -0
- package/dist/plugin-packages.d.ts +9 -12
- package/dist/pr-review.d.ts +0 -11
- package/dist/preference-registry-schema.d.ts +17 -0
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.d.ts +124 -6
- package/dist/retained-provider-commands.js +29 -97
- package/dist/retained-provider-commands.js.map +1 -1
- package/dist/retired-session-create-metadata.d.ts +4 -0
- package/dist/runtime-posture.d.ts +7 -5
- package/dist/schema.d.ts +876 -656
- package/dist/schema.js +5 -7
- package/dist/session-admission-block.d.ts +32 -0
- package/dist/session-attempt-writers.d.ts +4 -0
- package/dist/session-background-commands.js +5 -4
- package/dist/session-command-output.js +8 -126
- package/dist/session-command-output.js.map +1 -1
- package/dist/session-control.d.ts +5 -1
- package/dist/session-event-slices.d.ts +10 -0
- package/dist/session-event-slices.js +8 -222
- package/dist/session-event-slices.js.map +1 -1
- package/dist/session-file-attachments.d.ts +28 -0
- package/dist/session-goal-reports.d.ts +43 -0
- package/dist/session-mcp-approval.d.ts +4 -0
- package/dist/session-mcp-credential-rotation.js +5 -4
- package/dist/session-mcp-credential-rotation.js.map +1 -1
- package/dist/session-message-search.d.ts +22 -0
- package/dist/session-queue-commands.d.ts +2 -6
- package/dist/session-retry.d.ts +36 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/skill-source-release-impact.d.ts +18 -0
- package/dist/skill-source-release.d.ts +9 -0
- package/dist/skills.d.ts +7 -1
- package/dist/user-resource-authority.d.ts +0 -8
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +5 -4
- package/dist/workspace-tool-defaults.js.map +1 -1
- package/drizzle/0468_knowledge_relationship_projection.sql +30 -0
- package/drizzle/0469_knowledge_source_discovery.sql +85 -0
- package/drizzle/0470_organization_integration_policy.sql +151 -0
- package/drizzle/0471_usage_events_account_recent_index.sql +4 -0
- package/drizzle/0472_usage_events_workspace_recent_index.sql +4 -0
- package/drizzle/0473_organization_usage_analytical_capability.sql +237 -0
- package/drizzle/0474_goal_report_requirements.sql +156 -0
- package/drizzle/0475_session_event_history_statistics.sql +15 -0
- package/drizzle/0476_session_event_history_policy_planning.sql +29 -0
- package/drizzle/0477_managed_sign_in_methods.sql +303 -0
- package/drizzle/0478_sender_owned_connections.sql +2772 -0
- package/drizzle/0479_connection_create_idempotency.sql +16 -0
- package/drizzle/0480_private_session_organization_key_administration.sql +154 -0
- package/drizzle/0481_personal_connection_visibility_cleanup.sql +45 -0
- package/drizzle/0482_remove_packs.sql +417 -0
- package/drizzle/0483_preclaim_admission_block.sql +85 -0
- package/drizzle/0484_insights_usage_projection.sql +211 -0
- package/drizzle/0485_inherited_mcp_approval_policies.sql +6 -0
- package/drizzle/0486_reasoning_configuration_message_forks.sql +35 -0
- package/drizzle/0487_skill_catalog_message_forks.sql +26 -0
- package/drizzle/0488_permanent_skill_removal.sql +207 -0
- package/drizzle/0489_modal_command_byte_offsets.sql +17 -0
- package/drizzle/0490_periodic_capture_attempt_cadence.sql +6 -0
- package/drizzle/0491_warm_capture_holder_reclamation.sql +57 -0
- package/drizzle/0492_codex_accepted_source_authority.sql +249 -0
- package/drizzle/0493_stopping_command_error_containment.sql +58 -0
- package/drizzle/0494_mcp_account_bindings.sql +334 -0
- package/drizzle/0495_consented_sandbox_recovery.sql +220 -0
- package/drizzle/0496_supervised_command_settlement.sql +386 -0
- package/drizzle/0497_modal_inventory_owner_read_capability.sql +112 -0
- package/drizzle/0498_enrollment_membership_fence.sql +87 -0
- package/drizzle/0499_session_attachment_access.sql +224 -0
- package/drizzle/0500_session_execution_epoch.sql +37 -0
- package/drizzle/0501_session_sharing_execution.sql +591 -0
- package/drizzle/0502_active_message_boundary_forks.sql +30 -0
- package/package.json +17 -5
- package/src/automations.ts +17 -73
- package/src/canonical-human-identities.ts +17 -5
- package/src/capability-components.ts +7 -24
- package/src/capability-integrations.ts +301 -21
- package/src/capability-reconciliation.ts +19 -0
- package/src/codex-capacity-recovery.ts +47 -0
- package/src/codex-failure-eligibility.ts +93 -0
- package/src/connect-attempts.ts +49 -18
- package/src/connection-authority.ts +12 -158
- package/src/connection-refresh-lock.ts +22 -0
- package/src/connection-token-resolver.ts +58 -530
- package/src/database-timing.ts +43 -0
- package/src/database.ts +61 -26
- package/src/editable-artifacts.ts +1 -1
- package/src/index.ts +3578 -1273
- package/src/insights-model-bundle.ts +19 -7
- package/src/insights-usage-bundle.ts +16 -19
- package/src/integration-bindings.ts +2 -7
- package/src/integration-facets.ts +73 -6
- package/src/knowledge-entries.ts +8 -1
- package/src/live-session-attempt.ts +0 -22
- package/src/mcp-account-bindings.ts +12 -0
- package/src/mcp-oauth.ts +26 -0
- package/src/organization-integration-policy.ts +235 -0
- package/src/organization-usage.ts +83 -0
- package/src/plugin-packages.ts +226 -42
- package/src/pr-review.ts +7 -69
- package/src/preference-registry-schema.ts +1 -0
- package/src/provision-roles.ts +50 -37
- package/src/retained-provider-commands.ts +286 -9
- package/src/retired-session-create-metadata.ts +16 -0
- package/src/runtime-posture.ts +217 -13
- package/src/schema.ts +104 -120
- package/src/session-admission-block.ts +29 -0
- package/src/session-attempt-writers.ts +48 -0
- package/src/session-control.ts +21 -5
- package/src/session-event-slices.ts +76 -26
- package/src/session-file-attachments.ts +66 -0
- package/src/session-goal-reports.ts +255 -0
- package/src/session-mcp-approval.ts +30 -0
- package/src/session-message-search.ts +348 -0
- package/src/session-queue-commands.ts +60 -36
- package/src/session-retry.ts +319 -0
- package/src/skill-config-migration.ts +11 -2
- package/src/skill-source-release-impact.ts +33 -0
- package/src/skill-source-release.ts +67 -26
- package/src/skills.ts +44 -6
- package/src/user-resource-authority.ts +0 -56
- package/dist/chunk-6T2GGYWE.js.map +0 -1
- package/dist/chunk-7HMK3B45.js.map +0 -1
- package/dist/chunk-DPONPYJD.js.map +0 -1
- package/dist/chunk-F73UKJY6.js.map +0 -1
- package/dist/chunk-T6YLHJLS.js.map +0 -1
- package/dist/host-mcp-binding-match.d.ts +0 -5
- package/dist/host-mcp-bindings.d.ts +0 -79
- package/dist/host-mcp-resolvers.d.ts +0 -47
- package/dist/host-mcp-task-authority.d.ts +0 -43
- package/dist/host-selection-identity.d.ts +0 -6
- package/dist/pack-components.d.ts +0 -83
- package/dist/pack-installations.d.ts +0 -86
- package/src/host-mcp-binding-match.ts +0 -34
- package/src/host-mcp-bindings.ts +0 -777
- package/src/host-mcp-resolvers.ts +0 -266
- package/src/host-mcp-task-authority.ts +0 -384
- package/src/host-selection-identity.ts +0 -29
- package/src/pack-components.ts +0 -980
- package/src/pack-installations.ts +0 -950
- /package/dist/{chunk-MWMXWHJD.js.map → chunk-7JCDSMX2.js.map} +0 -0
- /package/dist/{chunk-ERKVSPDX.js.map → chunk-LVY33EWL.js.map} +0 -0
- /package/dist/{chunk-T6BWIGWJ.js.map → chunk-RIYHP2WU.js.map} +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type SessionGoalReportRequirement } from "@opengeni/contracts";
|
|
2
|
+
import { type Database } from "./database.js";
|
|
3
|
+
import { type PersistedEditableArtifactActor } from "./editable-artifacts.js";
|
|
4
|
+
/** Versioned, server-owned metadata. Malformed persisted data fails closed. */
|
|
5
|
+
export declare const GOAL_REPORT_REQUIREMENTS_KEY = "reportRequirementsV1";
|
|
6
|
+
export declare function goalReportRequirements(metadata: Record<string, unknown>): SessionGoalReportRequirement[];
|
|
7
|
+
export declare function appendGoalReportRequirements(metadata: Record<string, unknown>, declarations?: readonly SessionGoalReportRequirement[]): Record<string, unknown>;
|
|
8
|
+
export type ReportArtifactActor = Extract<PersistedEditableArtifactActor, {
|
|
9
|
+
kind: "agent";
|
|
10
|
+
}>;
|
|
11
|
+
/** Trusted application-only recorder, called after native query/decode. There is
|
|
12
|
+
* no public route/tool accepting this proof body. The runtime SQL role remains
|
|
13
|
+
* trusted: it can insert receipt-shaped rows, so stored proof is not a native
|
|
14
|
+
* execution attestation against arbitrary application-role SQL. UPDATE and
|
|
15
|
+
* direct DELETE are fenced; legitimate session-retention cascades remove proof.
|
|
16
|
+
*/
|
|
17
|
+
export declare function recordNativeDocumentInspection(db: Database, input: {
|
|
18
|
+
scope: {
|
|
19
|
+
accountId: string;
|
|
20
|
+
workspaceId: string;
|
|
21
|
+
};
|
|
22
|
+
actor: ReportArtifactActor;
|
|
23
|
+
sessionId: string;
|
|
24
|
+
artifact: {
|
|
25
|
+
id: string;
|
|
26
|
+
modality: string;
|
|
27
|
+
headSequence: number;
|
|
28
|
+
stateHash: string;
|
|
29
|
+
};
|
|
30
|
+
queryHash: string;
|
|
31
|
+
queryKind: string;
|
|
32
|
+
}): Promise<string>;
|
|
33
|
+
/** No provider calls here: receipt, access, locked current head and completion
|
|
34
|
+
* share one transaction. Receipts from previous attempts may prove inspection,
|
|
35
|
+
* but the completing attempt must still have live read authority. */
|
|
36
|
+
export declare function verifyGoalReportDeliveries(tx: Database, input: {
|
|
37
|
+
accountId: string;
|
|
38
|
+
workspaceId: string;
|
|
39
|
+
sessionId: string;
|
|
40
|
+
metadata: Record<string, unknown>;
|
|
41
|
+
deliveries: unknown;
|
|
42
|
+
actor?: ReportArtifactActor;
|
|
43
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SessionMcpApprovalPolicy } from "@opengeni/contracts";
|
|
2
|
+
import { type Database } from "./database.js";
|
|
3
|
+
/** Read only the immutable active-attempt policy, never a mutable session fallback. */
|
|
4
|
+
export declare function getSessionAttemptMcpApprovalPolicies(db: Database, workspaceId: string, sessionId: string, attemptId: string): Promise<Record<string, SessionMcpApprovalPolicy>>;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lockSessionEventWriteRows
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-GABXCG3N.js";
|
|
4
|
+
import "./chunk-LVY33EWL.js";
|
|
5
|
+
import "./chunk-4UJCXBR2.js";
|
|
5
6
|
import {
|
|
6
7
|
rawRows,
|
|
7
8
|
setSubjectRlsContext,
|
|
8
9
|
withRlsContext
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CJI6G254.js";
|
|
10
11
|
import {
|
|
11
12
|
apiKeys,
|
|
12
13
|
sessionCommandReceipts,
|
|
13
14
|
sessionMcpServers,
|
|
14
15
|
sessions
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-MN5PZMEK.js";
|
|
16
17
|
import "./chunk-S2TZ3F4L.js";
|
|
17
18
|
import "./chunk-PZ5AY32C.js";
|
|
18
19
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/session-mcp-credential-rotation.ts"],"sourcesContent":["import { and, eq, inArray, sql } from \"drizzle-orm\";\nimport { RotateSessionMcpCredentialsReceipt, SessionTurnStatus } from \"@opengeni/contracts\";\nimport { rawRows, setSubjectRlsContext, withRlsContext, type Database } from \"./database\";\nimport { lockSessionEventWriteRows } from \"./session-control\";\nimport * as schema from \"./schema\";\n\nconst action = \"session.mcp.credentials.rotate\";\nconst liveTurnStatuses = SessionTurnStatus.exclude([\n \"completed\",\n \"failed\",\n \"cancelled\",\n \"superseded\",\n \"withdrawn_for_edit\",\n]).options;\n\nexport class SessionMcpCredentialRotationError extends Error {\n constructor(\n readonly code:\n | \"invalid_request\"\n | \"not_found\"\n | \"not_quiescent\"\n | \"version_conflict\"\n | \"destination_conflict\"\n | \"brokered_server\"\n | \"operation_reuse\"\n | \"receipt_key_unavailable\"\n | \"authority_revoked\",\n ) {\n super(code);\n this.name = \"SessionMcpCredentialRotationError\";\n }\n}\n\nexport type AtomicSessionMcpCredentialRotationInput = {\n accountId: string;\n workspaceId: string;\n sessionId: string;\n subjectId: string;\n actorType: \"human\" | \"service\";\n operationKey: string;\n requestDigest: string;\n digestKeyTag: string;\n updates: Array<{\n id: string;\n expectedCredentialVersion: number;\n expectedServerUrl: string;\n headersEncrypted: Record<string, string>;\n }>;\n /** Trusted request authorizer, mandatory even for a committed receipt replay.\n * Revalidate the original authenticated subject and permissions on this tx. */\n authorize: (tx: Database) => Promise<void>;\n};\n\n/** Existing command receipts own durable idempotency; this is not a new queue\n * command. No session/event/history/attempt/control/wake rows are mutated. */\nexport async function rotateSessionMcpCredentialsAtomically(\n db: Database,\n input: AtomicSessionMcpCredentialRotationInput,\n): Promise<RotateSessionMcpCredentialsReceipt> {\n if (\n !input.updates.length ||\n input.updates.length > 64 ||\n new Set(input.updates.map((update) => update.id)).size !== input.updates.length ||\n !/^[a-f0-9]{64}$/.test(input.requestDigest) ||\n !/^[a-f0-9]{64}$/.test(input.digestKeyTag)\n ) {\n throw new SessionMcpCredentialRotationError(\"invalid_request\");\n }\n return withRlsContext(\n db,\n input,\n async (tx) => {\n // Membership before tenancy/control/session is the canonical claim/removal\n // order. External continuation authorization reacquires this exclusive\n // membership fence, so taking shared first would introduce an upgrade.\n // Do not upgrade a previously acquired shared tenancy lock.\n await tx.execute(sql`select pg_advisory_xact_lock(hashtextextended(\n ${`organization-membership:${input.accountId}`}, 0))`);\n await tx.execute(sql`select pg_advisory_xact_lock_shared(hashtextextended(\n ${`session-tenancy:${input.workspaceId}`}, 0))`);\n await setSubjectRlsContext(tx, input.subjectId);\n const [target] = await tx\n .select({ id: schema.sessions.id })\n .from(schema.sessions)\n .where(\n and(\n eq(schema.sessions.accountId, input.accountId),\n eq(schema.sessions.workspaceId, input.workspaceId),\n eq(schema.sessions.id, input.sessionId),\n ),\n );\n if (!target) throw new SessionMcpCredentialRotationError(\"not_found\");\n const locks = await lockSessionEventWriteRows(tx, {\n workspaceId: input.workspaceId,\n controlLock: \"share\",\n sessionIds: [input.sessionId],\n });\n const session = locks.sessions[0];\n if (!session || session.accountId !== input.accountId) {\n throw new SessionMcpCredentialRotationError(\"not_found\");\n }\n // Hold a current API key through commit. The ordinary access resolver's\n // last-used write alone does not recheck a revocation racing its first read.\n // Take UPDATE directly because fresh resolution writes lastUsedAt.\n if (input.subjectId.startsWith(\"api_key:\")) {\n const keyId = input.subjectId.slice(\"api_key:\".length);\n const [key] = await tx\n .select({ id: schema.apiKeys.id, permissions: schema.apiKeys.permissions })\n .from(schema.apiKeys)\n .where(\n and(\n eq(schema.apiKeys.id, keyId),\n eq(schema.apiKeys.accountId, input.accountId),\n sql`(${schema.apiKeys.workspaceId} is null or ${schema.apiKeys.workspaceId} = ${input.workspaceId}::uuid)`,\n sql`${schema.apiKeys.revokedAt} is null`,\n sql`(${schema.apiKeys.expiresAt} is null or ${schema.apiKeys.expiresAt} > clock_timestamp())`,\n ),\n )\n .for(\"update\");\n if (\n !key ||\n (!key.permissions.includes(\"workspace:admin\") &&\n (!key.permissions.includes(\"sessions:control\") ||\n !key.permissions.includes(\"mcp_servers:attach\")))\n )\n throw new SessionMcpCredentialRotationError(\"authority_revoked\");\n }\n await input.authorize(tx);\n const [prior] = await tx\n .select()\n .from(schema.sessionCommandReceipts)\n .where(\n and(\n eq(schema.sessionCommandReceipts.accountId, input.accountId),\n eq(schema.sessionCommandReceipts.workspaceId, input.workspaceId),\n eq(schema.sessionCommandReceipts.targetSessionId, input.sessionId),\n eq(schema.sessionCommandReceipts.actorType, input.actorType),\n eq(schema.sessionCommandReceipts.actorSubjectId, input.subjectId),\n eq(schema.sessionCommandReceipts.action, action),\n eq(schema.sessionCommandReceipts.operationKey, input.operationKey),\n ),\n );\n if (prior) {\n if (prior.result.digestKeyTag !== input.digestKeyTag)\n throw new SessionMcpCredentialRotationError(\"receipt_key_unavailable\");\n if (prior.canonicalRequestHash !== input.requestDigest)\n throw new SessionMcpCredentialRotationError(\"operation_reuse\");\n return RotateSessionMcpCredentialsReceipt.parse(prior.result.receipt);\n }\n // Session lock serializes admission, claim, realtime start, interruption\n // settlement, and attempt-owned provider admission. Dormant goals/schedules\n // and unrelated viewers are deliberately not credential consumers.\n // Retained run snapshots and tool receipts need a live turn/attempt to\n // consume credentials; their historical presence alone is not activity.\n const [busy] = await rawRows<{ present: boolean }>(\n tx,\n sql`select (\n exists(select 1 from session_turns where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and status in (${sql.join(\n liveTurnStatuses.map((status) => sql`${status}`),\n sql`, `,\n )}))\n or exists(select 1 from session_turn_attempts a where a.workspace_id = ${input.workspaceId}::uuid\n and a.session_id = ${input.sessionId}::uuid and (a.state <> 'closed' or\n (a.quiesced_at is null and exists(select 1 from session_attempt_interruptions i\n where i.workspace_id = a.workspace_id and i.attempt_id = a.id))))\n or exists(select 1 from session_system_updates where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and state = 'pending')\n or exists(select 1 from sandbox_workspace_mutation_admissions where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and attempt_id is not null and settled_at is null)\n or exists(select 1 from session_realtime_modes where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and state = 'active')\n or exists(select 1 from session_realtime_connections where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and state not in ('failed', 'closed'))\n ) as present`,\n );\n if (!busy || busy.present) throw new SessionMcpCredentialRotationError(\"not_quiescent\");\n const rows = await tx\n .select()\n .from(schema.sessionMcpServers)\n .where(\n and(\n eq(schema.sessionMcpServers.workspaceId, input.workspaceId),\n eq(schema.sessionMcpServers.sessionId, input.sessionId),\n inArray(\n schema.sessionMcpServers.serverId,\n input.updates.map((update) => update.id),\n ),\n ),\n )\n .orderBy(schema.sessionMcpServers.serverId)\n .for(\"update\");\n for (const update of input.updates) {\n const row = rows.find((server) => server.serverId === update.id);\n if (!row || row.accountId !== input.accountId)\n throw new SessionMcpCredentialRotationError(\"not_found\");\n if (row.connectionRef) throw new SessionMcpCredentialRotationError(\"brokered_server\");\n if (row.url !== update.expectedServerUrl)\n throw new SessionMcpCredentialRotationError(\"destination_conflict\");\n if (\n !Number.isInteger(update.expectedCredentialVersion) ||\n update.expectedCredentialVersion < 1 ||\n update.expectedCredentialVersion >= 2_147_483_647 ||\n row.credentialVersion !== update.expectedCredentialVersion\n )\n throw new SessionMcpCredentialRotationError(\"version_conflict\");\n }\n const receipt: RotateSessionMcpCredentialsReceipt = {\n operationKey: input.operationKey,\n sessionId: input.sessionId,\n appliedAt: new Date().toISOString(),\n servers: input.updates.map((update) => ({\n id: update.id,\n credentialVersion: update.expectedCredentialVersion + 1,\n })),\n };\n for (const update of input.updates) {\n await tx\n .update(schema.sessionMcpServers)\n .set({\n headersEncrypted: update.headersEncrypted,\n credentialVersion: update.expectedCredentialVersion + 1,\n updatedAt: new Date(receipt.appliedAt),\n })\n .where(\n and(\n eq(schema.sessionMcpServers.workspaceId, input.workspaceId),\n eq(schema.sessionMcpServers.sessionId, input.sessionId),\n eq(schema.sessionMcpServers.serverId, update.id),\n ),\n );\n }\n await tx.insert(schema.sessionCommandReceipts).values({\n accountId: input.accountId,\n workspaceId: input.workspaceId,\n actorType: input.actorType,\n actorSubjectId: input.subjectId,\n action,\n targetSessionId: input.sessionId,\n operationKey: input.operationKey,\n canonicalRequestHash: input.requestDigest,\n result: { digestKeyTag: input.digestKeyTag, receipt },\n });\n return receipt;\n },\n undefined,\n \"none\",\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/session-mcp-credential-rotation.ts"],"sourcesContent":["import { and, eq, inArray, sql } from \"drizzle-orm\";\nimport { RotateSessionMcpCredentialsReceipt, SessionTurnStatus } from \"@opengeni/contracts\";\nimport { rawRows, setSubjectRlsContext, withRlsContext, type Database } from \"./database\";\nimport { lockSessionEventWriteRows } from \"./session-control\";\nimport * as schema from \"./schema\";\n\nconst action = \"session.mcp.credentials.rotate\";\nconst liveTurnStatuses = SessionTurnStatus.exclude([\n \"completed\",\n \"failed\",\n \"cancelled\",\n \"superseded\",\n \"withdrawn_for_edit\",\n]).options;\n\nexport class SessionMcpCredentialRotationError extends Error {\n constructor(\n readonly code:\n | \"invalid_request\"\n | \"not_found\"\n | \"not_quiescent\"\n | \"version_conflict\"\n | \"destination_conflict\"\n | \"brokered_server\"\n | \"operation_reuse\"\n | \"receipt_key_unavailable\"\n | \"authority_revoked\",\n ) {\n super(code);\n this.name = \"SessionMcpCredentialRotationError\";\n }\n}\n\nexport type AtomicSessionMcpCredentialRotationInput = {\n accountId: string;\n workspaceId: string;\n sessionId: string;\n subjectId: string;\n actorType: \"human\" | \"service\";\n operationKey: string;\n requestDigest: string;\n digestKeyTag: string;\n updates: Array<{\n id: string;\n expectedCredentialVersion: number;\n expectedServerUrl: string;\n headersEncrypted: Record<string, string>;\n }>;\n /** Trusted request authorizer, mandatory even for a committed receipt replay.\n * Revalidate the original authenticated subject and permissions on this tx. */\n authorize: (tx: Database) => Promise<void>;\n};\n\n/** Existing command receipts own durable idempotency; this is not a new queue\n * command. No session/event/history/attempt/control/wake rows are mutated. */\nexport async function rotateSessionMcpCredentialsAtomically(\n db: Database,\n input: AtomicSessionMcpCredentialRotationInput,\n): Promise<RotateSessionMcpCredentialsReceipt> {\n if (\n !input.updates.length ||\n input.updates.length > 64 ||\n new Set(input.updates.map((update) => update.id)).size !== input.updates.length ||\n !/^[a-f0-9]{64}$/.test(input.requestDigest) ||\n !/^[a-f0-9]{64}$/.test(input.digestKeyTag)\n ) {\n throw new SessionMcpCredentialRotationError(\"invalid_request\");\n }\n return withRlsContext(\n db,\n input,\n async (tx) => {\n // Membership before tenancy/control/session is the canonical claim/removal\n // order. External continuation authorization reacquires this exclusive\n // membership fence, so taking shared first would introduce an upgrade.\n // Do not upgrade a previously acquired shared tenancy lock.\n await tx.execute(sql`select pg_advisory_xact_lock(hashtextextended(\n ${`organization-membership:${input.accountId}`}, 0))`);\n await tx.execute(sql`select pg_advisory_xact_lock_shared(hashtextextended(\n ${`session-tenancy:${input.workspaceId}`}, 0))`);\n await setSubjectRlsContext(tx, input.subjectId);\n const [target] = await tx\n .select({ id: schema.sessions.id })\n .from(schema.sessions)\n .where(\n and(\n eq(schema.sessions.accountId, input.accountId),\n eq(schema.sessions.workspaceId, input.workspaceId),\n eq(schema.sessions.id, input.sessionId),\n ),\n );\n if (!target) throw new SessionMcpCredentialRotationError(\"not_found\");\n const locks = await lockSessionEventWriteRows(tx, {\n workspaceId: input.workspaceId,\n controlLock: \"share\",\n sessionIds: [input.sessionId],\n });\n const session = locks.sessions[0];\n if (!session || session.accountId !== input.accountId) {\n throw new SessionMcpCredentialRotationError(\"not_found\");\n }\n // Hold a current API key through commit. The ordinary access resolver's\n // last-used write alone does not recheck a revocation racing its first read.\n // Take UPDATE directly because fresh resolution writes lastUsedAt.\n if (input.subjectId.startsWith(\"api_key:\")) {\n const keyId = input.subjectId.slice(\"api_key:\".length);\n const [key] = await tx\n .select({ id: schema.apiKeys.id, permissions: schema.apiKeys.permissions })\n .from(schema.apiKeys)\n .where(\n and(\n eq(schema.apiKeys.id, keyId),\n eq(schema.apiKeys.accountId, input.accountId),\n sql`(${schema.apiKeys.workspaceId} is null or ${schema.apiKeys.workspaceId} = ${input.workspaceId}::uuid)`,\n sql`${schema.apiKeys.revokedAt} is null`,\n sql`(${schema.apiKeys.expiresAt} is null or ${schema.apiKeys.expiresAt} > clock_timestamp())`,\n ),\n )\n .for(\"update\");\n if (\n !key ||\n (!key.permissions.includes(\"workspace:admin\") &&\n (!key.permissions.includes(\"sessions:control\") ||\n !key.permissions.includes(\"mcp_servers:attach\")))\n )\n throw new SessionMcpCredentialRotationError(\"authority_revoked\");\n }\n await input.authorize(tx);\n const [prior] = await tx\n .select()\n .from(schema.sessionCommandReceipts)\n .where(\n and(\n eq(schema.sessionCommandReceipts.accountId, input.accountId),\n eq(schema.sessionCommandReceipts.workspaceId, input.workspaceId),\n eq(schema.sessionCommandReceipts.targetSessionId, input.sessionId),\n eq(schema.sessionCommandReceipts.actorType, input.actorType),\n eq(schema.sessionCommandReceipts.actorSubjectId, input.subjectId),\n eq(schema.sessionCommandReceipts.action, action),\n eq(schema.sessionCommandReceipts.operationKey, input.operationKey),\n ),\n );\n if (prior) {\n if (prior.result.digestKeyTag !== input.digestKeyTag)\n throw new SessionMcpCredentialRotationError(\"receipt_key_unavailable\");\n if (prior.canonicalRequestHash !== input.requestDigest)\n throw new SessionMcpCredentialRotationError(\"operation_reuse\");\n return RotateSessionMcpCredentialsReceipt.parse(prior.result.receipt);\n }\n // Session lock serializes admission, claim, realtime start, interruption\n // settlement, and attempt-owned provider admission. Dormant goals/schedules\n // and unrelated viewers are deliberately not credential consumers.\n // Retained run snapshots and tool receipts need a live turn/attempt to\n // consume credentials; their historical presence alone is not activity.\n const [busy] = await rawRows<{ present: boolean }>(\n tx,\n sql`select (\n exists(select 1 from session_turns where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and status in (${sql.join(\n liveTurnStatuses.map((status) => sql`${status}`),\n sql`, `,\n )}))\n or exists(select 1 from session_turn_attempts a where a.workspace_id = ${input.workspaceId}::uuid\n and a.session_id = ${input.sessionId}::uuid and (a.state <> 'closed' or\n (a.quiesced_at is null and exists(select 1 from session_attempt_interruptions i\n where i.workspace_id = a.workspace_id and i.attempt_id = a.id))))\n or exists(select 1 from session_system_updates where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and state = 'pending')\n or exists(select 1 from sandbox_workspace_mutation_admissions where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and attempt_id is not null and settled_at is null)\n or exists(select 1 from session_realtime_modes where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and state = 'active')\n or exists(select 1 from session_realtime_connections where workspace_id = ${input.workspaceId}::uuid\n and session_id = ${input.sessionId}::uuid and state not in ('failed', 'closed'))\n ) as present`,\n );\n if (!busy || busy.present) throw new SessionMcpCredentialRotationError(\"not_quiescent\");\n const rows = await tx\n .select()\n .from(schema.sessionMcpServers)\n .where(\n and(\n eq(schema.sessionMcpServers.workspaceId, input.workspaceId),\n eq(schema.sessionMcpServers.sessionId, input.sessionId),\n inArray(\n schema.sessionMcpServers.serverId,\n input.updates.map((update) => update.id),\n ),\n ),\n )\n .orderBy(schema.sessionMcpServers.serverId)\n .for(\"update\");\n for (const update of input.updates) {\n const row = rows.find((server) => server.serverId === update.id);\n if (!row || row.accountId !== input.accountId)\n throw new SessionMcpCredentialRotationError(\"not_found\");\n if (row.connectionRef) throw new SessionMcpCredentialRotationError(\"brokered_server\");\n if (row.url !== update.expectedServerUrl)\n throw new SessionMcpCredentialRotationError(\"destination_conflict\");\n if (\n !Number.isInteger(update.expectedCredentialVersion) ||\n update.expectedCredentialVersion < 1 ||\n update.expectedCredentialVersion >= 2_147_483_647 ||\n row.credentialVersion !== update.expectedCredentialVersion\n )\n throw new SessionMcpCredentialRotationError(\"version_conflict\");\n }\n const receipt: RotateSessionMcpCredentialsReceipt = {\n operationKey: input.operationKey,\n sessionId: input.sessionId,\n appliedAt: new Date().toISOString(),\n servers: input.updates.map((update) => ({\n id: update.id,\n credentialVersion: update.expectedCredentialVersion + 1,\n })),\n };\n for (const update of input.updates) {\n await tx\n .update(schema.sessionMcpServers)\n .set({\n headersEncrypted: update.headersEncrypted,\n credentialVersion: update.expectedCredentialVersion + 1,\n updatedAt: new Date(receipt.appliedAt),\n })\n .where(\n and(\n eq(schema.sessionMcpServers.workspaceId, input.workspaceId),\n eq(schema.sessionMcpServers.sessionId, input.sessionId),\n eq(schema.sessionMcpServers.serverId, update.id),\n ),\n );\n }\n await tx.insert(schema.sessionCommandReceipts).values({\n accountId: input.accountId,\n workspaceId: input.workspaceId,\n actorType: input.actorType,\n actorSubjectId: input.subjectId,\n action,\n targetSessionId: input.sessionId,\n operationKey: input.operationKey,\n canonicalRequestHash: input.requestDigest,\n result: { digestKeyTag: input.digestKeyTag, receipt },\n });\n return receipt;\n },\n undefined,\n \"none\",\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SQL } from "drizzle-orm";
|
|
2
|
+
import type { SessionMessageSearchRequest, SessionMessageSearchResponse } from "@opengeni/contracts";
|
|
3
|
+
import type { Database } from "./database.js";
|
|
4
|
+
export declare class SessionMessageSearchCursorError extends Error {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
/** ECMAScript Unicode simple case folding, not locale-sensitive lowercasing.
|
|
8
|
+
* Escaping every regexp metacharacter makes %, _, backslash, etc. literal too.
|
|
9
|
+
* RegExp indices refer to original UTF-16 text (no lowercased-offset drift).
|
|
10
|
+
*/
|
|
11
|
+
export declare function sessionMessageLiteralPattern(query: string): RegExp;
|
|
12
|
+
export declare function sessionMessageSearchSnippet(text: string, start: number, length: number): {
|
|
13
|
+
text: string;
|
|
14
|
+
matchStart: number;
|
|
15
|
+
matchEnd: number;
|
|
16
|
+
};
|
|
17
|
+
/** Internal executor: caller holds subject RLS, session-tenancy and membership
|
|
18
|
+
* fences and supplies the ordinary session list filters. Never call as auth.
|
|
19
|
+
* Work/transfer is bounded by 32 scalar windows per request. No history cutoff:
|
|
20
|
+
* an unfinished scan ALWAYS returns an advancing cursor, even with zero hits.
|
|
21
|
+
*/
|
|
22
|
+
export declare function scanSessionMessages(db: Database, workspaceId: string, request: SessionMessageSearchRequest, filters: SQL[], authorityIdentity: unknown, signal?: AbortSignal): Promise<SessionMessageSearchResponse>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ResourceRef, type McpPersonalConnectionDelegation, type DraftTimelineAnnotation, type LatencyMode, type ReasoningEffort, type SessionEvent, type SessionPromptRouting, type SessionTurn, type TurnExecutionPolicyV1, type TimelineAnnotation, type PersonalResourceAttachmentIntent } from "@opengeni/contracts";
|
|
1
|
+
import { ResourceRef, type McpPersonalConnectionDelegation, type McpConnectionAccountBinding, type DraftTimelineAnnotation, type LatencyMode, type ReasoningEffort, type SessionEvent, type SessionPromptRouting, type SessionTurn, type TurnExecutionPolicyV1, type TimelineAnnotation, type PersonalResourceAttachmentIntent } from "@opengeni/contracts";
|
|
2
2
|
import { type Database, type SessionActivityDatabase } from "./database.js";
|
|
3
3
|
import { type SessionCommandActor, type SessionCommandReceiptRow } from "./session-control.js";
|
|
4
4
|
import * as schema from "./schema.js";
|
|
@@ -222,6 +222,7 @@ export declare function submitHumanPromptInTransaction(db: SessionActivityDataba
|
|
|
222
222
|
/** Record the admitted run's durable usage fact in this transaction. */
|
|
223
223
|
recordAgentRunUsage?: boolean;
|
|
224
224
|
personalConnectionDelegations?: McpPersonalConnectionDelegation[];
|
|
225
|
+
mcpAccountBindings?: McpConnectionAccountBinding[] | null;
|
|
225
226
|
personalResourceAttachment?: PersonalResourceAttachmentIntent;
|
|
226
227
|
mcpCredentialUpdates?: Array<{
|
|
227
228
|
id: string;
|
|
@@ -232,11 +233,6 @@ export declare function submitHumanPromptInTransaction(db: SessionActivityDataba
|
|
|
232
233
|
beforeFreshPromptCommit?: (tx: Database) => Promise<void>;
|
|
233
234
|
/** Backend-only authority capture, atomic with the newly accepted turn. */
|
|
234
235
|
captureTurnAuthority?: (tx: Database, turnId: string) => Promise<void>;
|
|
235
|
-
selectedHostMcpDelegations?: Array<{
|
|
236
|
-
serverId: string;
|
|
237
|
-
delegationId: string;
|
|
238
|
-
generation: number;
|
|
239
|
-
}>;
|
|
240
236
|
/** Request-scoped callers bound the control prefix wait; lifecycle callers omit it. */
|
|
241
237
|
controlLockTimeoutMs?: number;
|
|
242
238
|
}): Promise<SubmitHumanPromptResult>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type SessionRetryRequest, type SessionRetryResponse, type TurnExecutionPolicyV1 } from "@opengeni/contracts";
|
|
2
|
+
import { type Database, type SessionActivityDatabase } from "./database.js";
|
|
3
|
+
import * as schema from "./schema.js";
|
|
4
|
+
export declare class SessionRetryConflictError extends Error {
|
|
5
|
+
readonly code: "RETRY_STALE_FAILURE" | "RETRY_EXECUTION_UNRESOLVED" | "RETRY_PAUSED" | "RETRY_SANDBOX_RECOVERY_REQUIRED" | "RETRY_UNSUPPORTED_FAILURE";
|
|
6
|
+
constructor(code: "RETRY_STALE_FAILURE" | "RETRY_EXECUTION_UNRESOLVED" | "RETRY_PAUSED" | "RETRY_SANDBOX_RECOVERY_REQUIRED" | "RETRY_UNSUPPORTED_FAILURE", message: string);
|
|
7
|
+
}
|
|
8
|
+
/** Authorized receipt reads precede mutable model/billing admission checks. */
|
|
9
|
+
export declare function getSessionRetryReceiptInTransaction(db: Database, input: {
|
|
10
|
+
workspaceId: string;
|
|
11
|
+
sessionId: string;
|
|
12
|
+
subjectId: string;
|
|
13
|
+
request: SessionRetryRequest;
|
|
14
|
+
}): Promise<SessionRetryResponse | null>;
|
|
15
|
+
type EffectiveSandboxRecoveryBlocked = (db: Database, session: typeof schema.sessions.$inferSelect) => Promise<boolean>;
|
|
16
|
+
/** The root composition binds the canonical effective-route guard. Keep this
|
|
17
|
+
* leaf independent of the root barrel; there is no permissive default. */
|
|
18
|
+
export declare function createRetryFailedSessionInTransaction(sessionEffectiveSandboxRecoveryBlocked: EffectiveSandboxRecoveryBlocked): (db: SessionActivityDatabase, input: Parameters<typeof retryFailedSessionInTransaction>[1]) => Promise<{
|
|
19
|
+
outcome: "accepted" | "replayed";
|
|
20
|
+
turnId: string;
|
|
21
|
+
failureEventId: string;
|
|
22
|
+
} & {
|
|
23
|
+
eventIds: string[];
|
|
24
|
+
}>;
|
|
25
|
+
/** Caller owns the tenant/activity transaction and authorization. No provider I/O. */
|
|
26
|
+
declare function retryFailedSessionInTransaction(db: SessionActivityDatabase, input: {
|
|
27
|
+
accountId: string;
|
|
28
|
+
workspaceId: string;
|
|
29
|
+
sessionId: string;
|
|
30
|
+
subjectId: string;
|
|
31
|
+
request: SessionRetryRequest;
|
|
32
|
+
executionPolicy: TurnExecutionPolicyV1;
|
|
33
|
+
}, sessionEffectiveSandboxRecoveryBlocked: EffectiveSandboxRecoveryBlocked): Promise<SessionRetryResponse & {
|
|
34
|
+
eventIds: string[];
|
|
35
|
+
}>;
|
|
36
|
+
export {};
|
package/dist/session-tenancy.js
CHANGED
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
replayAppliedSessionFork,
|
|
15
15
|
sessionTenancyProductActivated,
|
|
16
16
|
transitionSessionVisibility
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-RIYHP2WU.js";
|
|
18
|
+
import "./chunk-CJI6G254.js";
|
|
19
|
+
import "./chunk-MN5PZMEK.js";
|
|
20
20
|
import "./chunk-S2TZ3F4L.js";
|
|
21
21
|
import "./chunk-PZ5AY32C.js";
|
|
22
22
|
export {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type SkillSourceReleaseHead = {
|
|
2
|
+
id: string;
|
|
3
|
+
account_id: string;
|
|
4
|
+
scope: string;
|
|
5
|
+
scope_workspace_id: string | null;
|
|
6
|
+
scope_version: number;
|
|
7
|
+
status: string;
|
|
8
|
+
active_revision_id: string | null;
|
|
9
|
+
provenance_source: string | null;
|
|
10
|
+
title: string | null;
|
|
11
|
+
};
|
|
12
|
+
type SkillRetentionReason = "customized" | "re_scoped";
|
|
13
|
+
/** One decision for both the user-visible impact and committed source release. */
|
|
14
|
+
export declare function classifySkillSourceRelease(head: SkillSourceReleaseHead, workspaceId: string): {
|
|
15
|
+
disposition: "inactive" | "retained" | "removed";
|
|
16
|
+
retentionReasons: SkillRetentionReason[];
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SkillActor } from "@opengeni/contracts";
|
|
2
2
|
import { type Database } from "./database.js";
|
|
3
|
+
import { type SkillSourceReleaseHead } from "./skill-source-release-impact.js";
|
|
3
4
|
export type SkillSourceReleaseReceipt = {
|
|
4
5
|
skillId: string;
|
|
5
6
|
revisionId: string | null;
|
|
@@ -7,10 +8,16 @@ export type SkillSourceReleaseReceipt = {
|
|
|
7
8
|
eventId: string | null;
|
|
8
9
|
warning: string | null;
|
|
9
10
|
};
|
|
11
|
+
/** Caller supplies workspace/subject RLS; never bypass registry visibility for a preview. */
|
|
12
|
+
export declare function readSkillSourceReleaseHeads(db: Database, workspaceId: string, facetInstallationIds: readonly string[]): Promise<SkillSourceReleaseHead[]>;
|
|
10
13
|
export declare class SkillSourceRemovalAuthorityError extends Error {
|
|
11
14
|
readonly code = "skill_source_removal_requires_human";
|
|
12
15
|
constructor();
|
|
13
16
|
}
|
|
17
|
+
/** Never disclose the identity of a head absent from the caller's locked snapshot. */
|
|
18
|
+
export declare class SkillSourceReleaseSnapshotChangedError extends Error {
|
|
19
|
+
constructor();
|
|
20
|
+
}
|
|
14
21
|
/**
|
|
15
22
|
* Called inside the source-owner removal transaction, with source installations
|
|
16
23
|
* locked. Distribution identity/history stays bound after uninstall, so reinstall
|
|
@@ -20,4 +27,6 @@ export declare function releaseOrphanedSkillHeads(db: Database, input: {
|
|
|
20
27
|
workspaceId: string;
|
|
21
28
|
facetInstallationIds: readonly string[];
|
|
22
29
|
skillActor?: SkillActor;
|
|
30
|
+
/** Exact heads already locked before the caller compared removal impact. */
|
|
31
|
+
lockedHeadIds?: readonly string[];
|
|
23
32
|
}): Promise<SkillSourceReleaseReceipt[]>;
|
package/dist/skills.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ export type SkillReadContext = {
|
|
|
10
10
|
/** Trusted subject enables the existing user-tier visibility; agents share workspace reads. */
|
|
11
11
|
subjectId?: string;
|
|
12
12
|
};
|
|
13
|
+
/** Scope only, for authorizing an exact human deletion retry after the head is gone.
|
|
14
|
+
* The lifecycle still verifies the complete request fingerprint before replay.
|
|
15
|
+
*/
|
|
16
|
+
export declare function readSkillRemovalScope(db: Database, context: SkillReadContext & {
|
|
17
|
+
subjectId: string;
|
|
18
|
+
}, skillId: string, operationId: string): Promise<"workspace" | "user" | null>;
|
|
13
19
|
/** Recheck the host-bound attempt before any agent-facing Skill read/search. */
|
|
14
20
|
export declare function assertSkillReadAttempt(db: Database, context: SkillReadContext & {
|
|
15
21
|
actor: Extract<SkillActor, {
|
|
@@ -52,4 +58,4 @@ export declare function confirmSkillHumanResponse(db: Database, input: {
|
|
|
52
58
|
requestId: string;
|
|
53
59
|
}): Promise<SkillWriteReceipt | null>;
|
|
54
60
|
/** Current projection is separate from the immutable original write receipt. */
|
|
55
|
-
export declare function skillReviewResolution(db: Database, context: SkillReadContext, review: SkillReviewReference): Promise<"pending" | "activated" | "declined" | "superseded" | "unavailable">;
|
|
61
|
+
export declare function skillReviewResolution(db: Database, context: SkillReadContext, review: SkillReviewReference): Promise<"pending" | "activated" | "removed" | "declined" | "superseded" | "unavailable">;
|
|
@@ -71,14 +71,6 @@ export declare function issueSelfUserResourceGrant(db: Database, input: {
|
|
|
71
71
|
expectedAuthorityEpoch?: number | null;
|
|
72
72
|
workspaceSharedAcknowledged: boolean;
|
|
73
73
|
}): Promise<UserResourceAuthorityGrant>;
|
|
74
|
-
export declare function issueSelfLocalConnectionUseGrant(db: Database, input: {
|
|
75
|
-
accountId: string;
|
|
76
|
-
workspaceId: string;
|
|
77
|
-
subjectId: string;
|
|
78
|
-
authorityId: string;
|
|
79
|
-
context: "user_private" | "workspace_shared";
|
|
80
|
-
workspaceSharedAcknowledged: boolean;
|
|
81
|
-
}): Promise<UserResourceAuthorityGrant>;
|
|
82
74
|
export declare function revokeSelfUserResourceGrant(db: Database, input: {
|
|
83
75
|
accountId: string;
|
|
84
76
|
workspaceId: string;
|
package/dist/video-generation.js
CHANGED
|
@@ -29,9 +29,9 @@ import {
|
|
|
29
29
|
settleVideoGenerationFailure,
|
|
30
30
|
settleVideoGenerationReady,
|
|
31
31
|
updateWorkspaceVideoGenerationPolicy
|
|
32
|
-
} from "./chunk-
|
|
33
|
-
import "./chunk-
|
|
34
|
-
import "./chunk-
|
|
32
|
+
} from "./chunk-LVY33EWL.js";
|
|
33
|
+
import "./chunk-CJI6G254.js";
|
|
34
|
+
import "./chunk-MN5PZMEK.js";
|
|
35
35
|
import "./chunk-S2TZ3F4L.js";
|
|
36
36
|
import "./chunk-PZ5AY32C.js";
|
|
37
37
|
export {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lockWorkspaceInferenceControl
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-GABXCG3N.js";
|
|
4
|
+
import "./chunk-LVY33EWL.js";
|
|
5
|
+
import "./chunk-4UJCXBR2.js";
|
|
5
6
|
import {
|
|
6
7
|
withWorkspaceRls
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-CJI6G254.js";
|
|
9
|
+
import "./chunk-MN5PZMEK.js";
|
|
9
10
|
import "./chunk-S2TZ3F4L.js";
|
|
10
11
|
import "./chunk-PZ5AY32C.js";
|
|
11
12
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workspace-tool-defaults.ts"],"sourcesContent":["import { WorkspaceSessionToolDefaultsPatch, type Workspace } from \"@opengeni/contracts\";\nimport { sql } from \"drizzle-orm\";\nimport { type Database, withWorkspaceRls } from \"./database\";\nimport { lockWorkspaceInferenceControl } from \"./session-control\";\n\ntype SettingsStore = {\n requireWorkspace(db: Database, workspaceId: string): Promise<Workspace>;\n updateWorkspaceSettings(\n db: Database,\n workspaceId: string,\n patch: Record<string, unknown>,\n options: { controlLockTimeoutMs?: number },\n ): Promise<Workspace>;\n};\n\n/** Independent nested patches; omission preserves and null restores inheritance. */\nexport async function updateWorkspaceSettingsWithToolDefaults(\n db: Database,\n workspaceId: string,\n patch: Record<string, unknown>,\n store: SettingsStore,\n options: { controlLockTimeoutMs?: number } = {},\n): Promise<Workspace> {\n if (patch.sessionToolDefaults === undefined)\n return store.updateWorkspaceSettings(db, workspaceId, patch, options);\n const tools = WorkspaceSessionToolDefaultsPatch.parse(patch.sessionToolDefaults);\n const { sessionToolDefaults: _tools, ...other } = patch;\n return withWorkspaceRls(db, workspaceId, async (scoped) =>\n scoped.transaction(async (tx) => {\n const transaction = tx as unknown as Database;\n await lockWorkspaceInferenceControl(transaction, workspaceId, \"update\", {\n ...(options.controlLockTimeoutMs !== undefined\n ? { lockTimeoutMs: options.controlLockTimeoutMs }\n : {}),\n });\n if (Object.keys(other).length)\n await store.updateWorkspaceSettings(transaction, workspaceId, other, options);\n await transaction.execute(sql`\n update workspaces set settings = jsonb_set(settings, '{sessionToolDefaults}',\n jsonb_strip_nulls(coalesce(settings->'sessionToolDefaults', '{}'::jsonb) || ${JSON.stringify(tools)}::jsonb)),\n updated_at = now()\n where id = ${workspaceId}::uuid\n `);\n return store.requireWorkspace(transaction, workspaceId);\n }),\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/workspace-tool-defaults.ts"],"sourcesContent":["import { WorkspaceSessionToolDefaultsPatch, type Workspace } from \"@opengeni/contracts\";\nimport { sql } from \"drizzle-orm\";\nimport { type Database, withWorkspaceRls } from \"./database\";\nimport { lockWorkspaceInferenceControl } from \"./session-control\";\n\ntype SettingsStore = {\n requireWorkspace(db: Database, workspaceId: string): Promise<Workspace>;\n updateWorkspaceSettings(\n db: Database,\n workspaceId: string,\n patch: Record<string, unknown>,\n options: { controlLockTimeoutMs?: number },\n ): Promise<Workspace>;\n};\n\n/** Independent nested patches; omission preserves and null restores inheritance. */\nexport async function updateWorkspaceSettingsWithToolDefaults(\n db: Database,\n workspaceId: string,\n patch: Record<string, unknown>,\n store: SettingsStore,\n options: { controlLockTimeoutMs?: number } = {},\n): Promise<Workspace> {\n if (patch.sessionToolDefaults === undefined)\n return store.updateWorkspaceSettings(db, workspaceId, patch, options);\n const tools = WorkspaceSessionToolDefaultsPatch.parse(patch.sessionToolDefaults);\n const { sessionToolDefaults: _tools, ...other } = patch;\n return withWorkspaceRls(db, workspaceId, async (scoped) =>\n scoped.transaction(async (tx) => {\n const transaction = tx as unknown as Database;\n await lockWorkspaceInferenceControl(transaction, workspaceId, \"update\", {\n ...(options.controlLockTimeoutMs !== undefined\n ? { lockTimeoutMs: options.controlLockTimeoutMs }\n : {}),\n });\n if (Object.keys(other).length)\n await store.updateWorkspaceSettings(transaction, workspaceId, other, options);\n await transaction.execute(sql`\n update workspaces set settings = jsonb_set(settings, '{sessionToolDefaults}',\n jsonb_strip_nulls(coalesce(settings->'sessionToolDefaults', '{}'::jsonb) || ${JSON.stringify(tools)}::jsonb)),\n updated_at = now()\n where id = ${workspaceId}::uuid\n `);\n return store.requireWorkspace(transaction, workspaceId);\n }),\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAAA,SAAS,yCAAyD;AAClE,SAAS,WAAW;AAepB,eAAsB,wCACpB,IACA,aACA,OACA,OACA,UAA6C,CAAC,GAC1B;AACpB,MAAI,MAAM,wBAAwB;AAChC,WAAO,MAAM,wBAAwB,IAAI,aAAa,OAAO,OAAO;AACtE,QAAM,QAAQ,kCAAkC,MAAM,MAAM,mBAAmB;AAC/E,QAAM,EAAE,qBAAqB,QAAQ,GAAG,MAAM,IAAI;AAClD,SAAO;AAAA,IAAiB;AAAA,IAAI;AAAA,IAAa,OAAO,WAC9C,OAAO,YAAY,OAAO,OAAO;AAC/B,YAAM,cAAc;AACpB,YAAM,8BAA8B,aAAa,aAAa,UAAU;AAAA,QACtE,GAAI,QAAQ,yBAAyB,SACjC,EAAE,eAAe,QAAQ,qBAAqB,IAC9C,CAAC;AAAA,MACP,CAAC;AACD,UAAI,OAAO,KAAK,KAAK,EAAE;AACrB,cAAM,MAAM,wBAAwB,aAAa,aAAa,OAAO,OAAO;AAC9E,YAAM,YAAY,QAAQ;AAAA;AAAA,sFAEsD,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA,mBAExF,WAAW;AAAA,KACzB;AACC,aAAO,MAAM,iBAAiB,aAAa,WAAW;AAAA,IACxD,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Preserve the read capability, ACL/evidence checks and pagination contract.
|
|
3
|
+
-- A join from jsonb_array_elements (estimated at 100 rows) can hash against the
|
|
4
|
+
-- whole account and evaluate the VOLATILE recursive visibility predicate before
|
|
5
|
+
-- matching an endpoint. A scalar subquery pins each check to its exact identity:
|
|
6
|
+
-- absent endpoints remain hidden, duplicates and original array order survive.
|
|
7
|
+
CREATE OR REPLACE FUNCTION knowledge_entry_visible_body(p_account uuid,p_body jsonb,p_pending boolean)
|
|
8
|
+
RETURNS jsonb LANGUAGE sql VOLATILE SET search_path FROM CURRENT AS $$
|
|
9
|
+
SELECT p_body || jsonb_build_object(
|
|
10
|
+
'groupIds',coalesce((SELECT jsonb_agg(g.value ORDER BY g.ordinality)
|
|
11
|
+
FROM jsonb_array_elements(p_body->'groupIds') WITH ORDINALITY g(value,ordinality)
|
|
12
|
+
WHERE (SELECT NOT e.archived AND knowledge_revision_visible(p_account,e.id,
|
|
13
|
+
coalesce(e.published_revision_id,CASE WHEN p_pending THEN e.latest_revision_id END),p_pending)
|
|
14
|
+
FROM knowledge_entries e
|
|
15
|
+
WHERE e.account_id=p_account AND e.id=(g.value#>>'{}')::uuid)), '[]'::jsonb),
|
|
16
|
+
'relationships',coalesce((SELECT jsonb_agg(l.value ORDER BY l.ordinality)
|
|
17
|
+
FROM jsonb_array_elements(p_body->'relationships') WITH ORDINALITY l(value,ordinality)
|
|
18
|
+
WHERE (SELECT NOT e.archived AND knowledge_revision_visible(p_account,e.id,
|
|
19
|
+
coalesce(e.published_revision_id,CASE WHEN p_pending THEN e.latest_revision_id END),p_pending)
|
|
20
|
+
FROM knowledge_entries e
|
|
21
|
+
WHERE e.account_id=p_account AND e.id=(l.value->>'entryId')::uuid)), '[]'::jsonb))
|
|
22
|
+
$$;
|
|
23
|
+
|
|
24
|
+
-- CREATE OR REPLACE replaces proconfig too. Preserve 0461's final hardening,
|
|
25
|
+
-- including the actual embedded target schema and pg_temp explicitly last.
|
|
26
|
+
DO $secure$
|
|
27
|
+
BEGIN
|
|
28
|
+
EXECUTE format('ALTER FUNCTION %I.knowledge_entry_visible_body(uuid,jsonb,boolean) SET search_path = %I, pg_catalog, pg_temp',
|
|
29
|
+
current_schema(),current_schema());
|
|
30
|
+
END $secure$;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Source retention and discovery are separate. Preserve exact historical bodies,
|
|
3
|
+
-- decisions, evidence links and originals. Classify historical incidental sources
|
|
4
|
+
-- only by typed preparation identity / conversation provenance, never their text.
|
|
5
|
+
-- Explicit purpose on a new revision overrides this compatibility classification.
|
|
6
|
+
-- New source purpose metadata is rejected by pre-0469 strict readers. Drain
|
|
7
|
+
-- every API/control/turn runtime login before activation; never restart an old
|
|
8
|
+
-- binary after the first purpose-bearing revision is written.
|
|
9
|
+
SET LOCAL lock_timeout = '5s';
|
|
10
|
+
SET LOCAL statement_timeout = '5min';
|
|
11
|
+
DO $drain$
|
|
12
|
+
DECLARE roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
13
|
+
BEGIN
|
|
14
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' THEN
|
|
15
|
+
RAISE EXCEPTION '0469 requires explicit application database roles' USING ERRCODE='55000';
|
|
16
|
+
END IF;
|
|
17
|
+
IF jsonb_array_length(roles) NOT BETWEEN 1 AND 16 OR EXISTS (
|
|
18
|
+
SELECT 1 FROM jsonb_array_elements(roles) item WHERE jsonb_typeof(item) <> 'string'
|
|
19
|
+
OR octet_length(item #>> '{}') NOT BETWEEN 1 AND 63
|
|
20
|
+
OR item #>> '{}' <> btrim(item #>> '{}')
|
|
21
|
+
) THEN RAISE EXCEPTION '0469 received invalid application roles' USING ERRCODE='55000'; END IF;
|
|
22
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a
|
|
23
|
+
JOIN jsonb_array_elements_text(roles) r ON r.value=a.usename
|
|
24
|
+
WHERE a.datname=current_database() AND a.pid<>pg_backend_pid()) THEN
|
|
25
|
+
RAISE EXCEPTION '0469 requires drained application sessions' USING ERRCODE='55000';
|
|
26
|
+
END IF;
|
|
27
|
+
END $drain$;
|
|
28
|
+
|
|
29
|
+
DO $migration$
|
|
30
|
+
DECLARE definition text; anchor text; replacement text;
|
|
31
|
+
BEGIN
|
|
32
|
+
definition := pg_get_functiondef('knowledge_entry_read(uuid,uuid,jsonb,jsonb)'::regprocedure);
|
|
33
|
+
anchor := $old$ AND (NOT(p_request ? 'kind') OR r.body->>'kind'=p_request->>'kind')$old$;
|
|
34
|
+
replacement := anchor || $new$
|
|
35
|
+
-- Apply before scoring and pagination. Evidence access and review remain
|
|
36
|
+
-- governed by the unchanged recursive revision/source visibility checks.
|
|
37
|
+
AND (operation<>'list' OR coalesce(p_request->>'view','published')<>'published'
|
|
38
|
+
OR coalesce((p_request->>'includeEvidence')::boolean,false)
|
|
39
|
+
OR r.body->>'kind'<>'source'
|
|
40
|
+
OR coalesce(r.body#>>'{source,purpose}',CASE
|
|
41
|
+
WHEN e.prepared_file_id IS NOT NULL THEN 'evidence'
|
|
42
|
+
WHEN r.body#>>'{source,kind}'='conversation'
|
|
43
|
+
AND e.legacy_memory_id IS NULL AND e.legacy_document_id IS NULL
|
|
44
|
+
AND e.legacy_claim_id IS NULL AND e.legacy_document_version_id IS NULL THEN 'evidence'
|
|
45
|
+
ELSE 'reference' END)<>'evidence')$new$;
|
|
46
|
+
IF (length(definition)-length(replace(definition,anchor,'')))/length(anchor) <> 1 THEN
|
|
47
|
+
RAISE EXCEPTION 'Knowledge discovery candidate anchor changed';
|
|
48
|
+
END IF;
|
|
49
|
+
EXECUTE replace(definition,anchor,replacement);
|
|
50
|
+
|
|
51
|
+
definition := pg_get_functiondef('knowledge_validate_source(uuid,uuid,jsonb)'::regprocedure);
|
|
52
|
+
anchor := $old$ IF NOT knowledge_source_visible(p_account,p_body) THEN$old$;
|
|
53
|
+
replacement := $new$ IF (p_body->'source') ? 'purpose' AND (
|
|
54
|
+
jsonb_typeof(p_body#>'{source,purpose}') IS DISTINCT FROM 'string'
|
|
55
|
+
OR p_body#>>'{source,purpose}' NOT IN ('evidence','reference')) THEN
|
|
56
|
+
RAISE EXCEPTION 'Invalid source purpose' USING ERRCODE='22023'; END IF;
|
|
57
|
+
$new$ || anchor;
|
|
58
|
+
IF (length(definition)-length(replace(definition,anchor,'')))/length(anchor) <> 1 THEN
|
|
59
|
+
RAISE EXCEPTION 'Knowledge source validation anchor changed';
|
|
60
|
+
END IF;
|
|
61
|
+
EXECUTE replace(definition,anchor,replacement);
|
|
62
|
+
|
|
63
|
+
definition := pg_get_functiondef('knowledge_entry_prepare_file(uuid,uuid,jsonb,jsonb)'::regprocedure);
|
|
64
|
+
anchor := $old$ body:=jsonb_build_object('title',p_request->'title','kind','source','content',p_request->'content',$old$;
|
|
65
|
+
replacement := $new$ IF p_request ? 'purpose' AND (
|
|
66
|
+
jsonb_typeof(p_request->'purpose') IS DISTINCT FROM 'string'
|
|
67
|
+
OR p_request->>'purpose' NOT IN ('evidence','reference')) THEN
|
|
68
|
+
RAISE EXCEPTION 'Invalid source purpose' USING ERRCODE='22023'; END IF;
|
|
69
|
+
$new$ || anchor;
|
|
70
|
+
IF (length(definition)-length(replace(definition,anchor,'')))/length(anchor) <> 1 THEN
|
|
71
|
+
RAISE EXCEPTION 'Knowledge preparation validation anchor changed';
|
|
72
|
+
END IF;
|
|
73
|
+
definition := replace(definition,anchor,replacement);
|
|
74
|
+
anchor := $old$'retention','full_text','capturedAt',clock_timestamp())$old$;
|
|
75
|
+
replacement := $new$'retention',CASE WHEN p_request->>'content'='' THEN 'reference' ELSE 'full_text' END,
|
|
76
|
+
'purpose',coalesce(p_request->>'purpose','evidence'),'capturedAt',clock_timestamp())$new$;
|
|
77
|
+
IF (length(definition)-length(replace(definition,anchor,'')))/length(anchor) <> 1 THEN
|
|
78
|
+
RAISE EXCEPTION 'Knowledge preparation source anchor changed';
|
|
79
|
+
END IF;
|
|
80
|
+
EXECUTE replace(definition,anchor,replacement);
|
|
81
|
+
END $migration$;
|
|
82
|
+
|
|
83
|
+
-- pg_get_functiondef retains each existing owner, security mode and hardened
|
|
84
|
+
-- search_path. CREATE OR REPLACE retains existing ACLs. No new read capability,
|
|
85
|
+
-- runtime table grant or alternate lifecycle is introduced by this migration.
|