@opengeni/db 0.14.3 → 0.16.2
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/{chunk-ELMUCYZF.js → chunk-4RUK5CON.js} +183 -42
- package/dist/chunk-4RUK5CON.js.map +1 -0
- package/dist/chunk-HZD7KVAR.js +4648 -0
- package/dist/chunk-HZD7KVAR.js.map +1 -0
- package/dist/{chunk-Y5WZZVQK.js → chunk-SK7YYHBI.js} +5 -2
- package/dist/chunk-SK7YYHBI.js.map +1 -0
- package/dist/codex-token-resolver.d.ts +64 -0
- package/dist/connection-token-resolver.d.ts +90 -0
- package/dist/environment-crypto.d.ts +11 -0
- package/dist/event-payload-sanitizer.d.ts +32 -0
- package/dist/index.d.ts +6217 -10
- package/dist/index.js +6930 -2708
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +157 -0
- package/dist/memory-domain.d.ts +44 -0
- package/dist/migrate.d.ts +5 -7
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +49 -0
- package/dist/persistence-errors.d.ts +69 -0
- package/dist/preference-registry-schema.d.ts +1147 -0
- package/dist/preference-registry.d.ts +878 -0
- package/dist/provision-roles.d.ts +4 -6489
- package/dist/provision-roles.js +1 -1
- package/dist/role-relationships.d.ts +35 -0
- package/dist/runtime-posture-cli.d.ts +1 -0
- package/dist/runtime-posture.d.ts +103 -0
- package/dist/schema.d.ts +23533 -3
- package/dist/schema.js +17 -1
- package/dist/session-control.d.ts +331 -0
- package/dist/session-queue-commands.d.ts +186 -0
- package/dist/session-tool-call-settlement.d.ts +32 -0
- package/dist/turn-initiator.d.ts +28 -0
- package/dist/workspace-instruction-policies-schema.d.ts +735 -0
- package/dist/workspace-instruction-policies.d.ts +64 -0
- package/drizzle/0136_unified_session_tool_policy.sql +1 -1
- package/drizzle/0137_preference_registry.sql +1347 -0
- package/drizzle/0138_sandbox_checkpoint_artifacts_and_deadlines.sql +1074 -0
- package/drizzle/0139_codex_provider_artifact_invalidations.sql +51 -0
- package/drizzle/0140_sandbox_restore_and_reaper_fences.sql +276 -0
- package/drizzle/0142_sandbox_archive_capture_gate.sql +80 -0
- package/drizzle/0143_session_codex_compaction_mode.sql +20 -0
- package/drizzle/0144_sandbox_viewer_force_drain_gate.sql +95 -0
- package/drizzle/0145_model_call_facts.sql +96 -0
- package/drizzle/0146_slack_bot_delete_idempotency.sql +105 -0
- package/drizzle/0147_draft_latency_mode.sql +12 -0
- package/drizzle/0148_session_turn_latency_mode.sql +12 -0
- package/package.json +6 -6
- package/src/index.ts +4995 -421
- package/src/insights.ts +837 -0
- package/src/migrate.ts +9 -1
- package/src/new-session-drafts.ts +4 -0
- package/src/preference-registry-schema.ts +170 -0
- package/src/preference-registry.ts +1220 -0
- package/src/provision-roles.ts +95 -22
- package/src/role-relationships.ts +132 -0
- package/src/runtime-posture.ts +28 -26
- package/src/schema.ts +478 -5
- package/src/session-queue-commands.ts +39 -6
- package/dist/chunk-DW24CKCT.js +0 -4046
- package/dist/chunk-DW24CKCT.js.map +0 -1
- package/dist/chunk-ELMUCYZF.js.map +0 -1
- package/dist/chunk-Y5WZZVQK.js.map +0 -1
- package/dist/schema-DhkRhcuQ.d.ts +0 -22666
|
@@ -99,7 +99,10 @@ async function executeMigrationFile(sql, file, sqlText) {
|
|
|
99
99
|
}
|
|
100
100
|
const concurrentIndex = parseConcurrentIndexMigration(file, sqlText);
|
|
101
101
|
if (!concurrentIndex) {
|
|
102
|
-
await sql.unsafe(
|
|
102
|
+
await sql.unsafe(
|
|
103
|
+
`SELECT pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
|
|
104
|
+
${sqlText}`
|
|
105
|
+
);
|
|
103
106
|
return;
|
|
104
107
|
}
|
|
105
108
|
await sql`select set_config('lock_timeout', ${concurrentIndex.lockTimeout}, false)`;
|
|
@@ -200,4 +203,4 @@ export {
|
|
|
200
203
|
migrate,
|
|
201
204
|
runMigrations
|
|
202
205
|
};
|
|
203
|
-
//# sourceMappingURL=chunk-
|
|
206
|
+
//# sourceMappingURL=chunk-SK7YYHBI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/migrate.ts"],"sourcesContent":["import { readdir, readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport postgres from \"postgres\";\n\nconst DEFAULT_DATABASE_URL = \"postgres://opengeni:opengeni@127.0.0.1:5432/opengeni\";\nconst DEFAULT_MAX_NESTED_AGENT_DEPTH = 3;\nconst MAX_NESTED_AGENT_DEPTH = 2_147_483_647;\nconst batchedBackfillDirective =\n /^-- opengeni:batched-backfill batch-size=(\\d+) lock-timeout=(\\d+(?:ms|s|min)) statement-timeout=(\\d+(?:ms|s|min))$/;\nconst concurrentIndexDirective = /^-- opengeni:concurrent-index lock-timeout=(\\d+(?:ms|s|min))$/;\nconst concurrentIndexStatement =\n /^CREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+CONCURRENTLY\\s+(?:(IF\\s+NOT\\s+EXISTS)\\s+)?(?:\"((?:[^\"]|\"\")+)\"|([A-Za-z_][A-Za-z0-9_]*))\\s+ON\\b/is;\nconst governedLegacyConcurrentIndexMigrations = new Set([\n \"0066_session_interruption_attempt_lookup.sql\",\n \"0070_session_event_type_sequence_lookup.sql\",\n \"0071_session_event_monitoring_tail.sql\",\n \"0072_sessions_workspace_created_id_idx.sql\",\n \"0073_sessions_workspace_updated_id_idx.sql\",\n \"0075_sessions_workspace_activity_revision_idx.sql\",\n \"0077_session_attempt_latest_lookup.sql\",\n]);\n\nexport interface ConcurrentIndexMigration {\n indexName: string;\n lockTimeout: string;\n skipWhenValid: boolean;\n statement: string;\n}\n\nexport type MigrationRuntimeOptions = {\n maxNestedAgentDepth?: number;\n};\n\ntype DeploymentDepthPolicy = {\n maxNestedAgentDepth: number;\n source: \"deployment\" | \"default\";\n};\n\n/** A bare Postgres identifier (schema/role name) safe to interpolate into DDL. */\nfunction assertIdentifier(name: string, value: string): string {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {\n throw new Error(`${name} is not a valid Postgres identifier: ${value}`);\n }\n return value;\n}\n\n/**\n * Most migration files intentionally execute as one implicit transaction.\n * PostgreSQL forbids CREATE INDEX CONCURRENTLY there, so a migration may opt\n * into one narrowly validated transactionless statement with:\n *\n * -- opengeni:concurrent-index lock-timeout=5s\n * CREATE [UNIQUE] INDEX CONCURRENTLY IF NOT EXISTS ...;\n *\n * The directive is deliberately not a generic \"no transaction\" escape hatch:\n * only one idempotent concurrent-index statement is accepted, lock acquisition\n * is always bounded, and an invalid artifact left by a failed concurrent build\n * is removed before retry. Seven governed historical migrations predate the\n * IF NOT EXISTS rule; only those exact filenames may use their immutable bare\n * statements, and the runner makes their retries idempotent by skipping an\n * already-valid index. This keeps additive large-table indexes online without\n * rewriting shipped history or making arbitrary partially-applied migration\n * scripts possible.\n */\nexport function parseConcurrentIndexMigration(\n file: string,\n sqlText: string,\n): ConcurrentIndexMigration | null {\n const lines = sqlText.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\");\n const firstLine = lines[0]?.trim() ?? \"\";\n const deploymentPrefixed = /^-- deployment-mode: (?:rolling|maintenance)$/.test(firstLine);\n const directiveIndex = deploymentPrefixed ? 1 : 0;\n const directiveLine = lines[directiveIndex]?.trim() ?? \"\";\n const directive = concurrentIndexDirective.exec(directiveLine);\n if (!directive) {\n if (directiveLine.startsWith(\"-- opengeni:\")) {\n throw new Error(`Unsupported OpenGeni migration directive in ${file}`);\n }\n return null;\n }\n\n const lockTimeout = directive[1]!;\n const statement = lines\n .slice(directiveIndex + 1)\n .join(\"\\n\")\n .trim();\n const withoutTrailingSemicolon = statement.endsWith(\";\")\n ? statement.slice(0, -1).trimEnd()\n : statement;\n const parsedStatement = concurrentIndexStatement.exec(withoutTrailingSemicolon);\n if (!parsedStatement || withoutTrailingSemicolon.includes(\";\")) {\n throw new Error(\n `${file}: opengeni:concurrent-index requires exactly one CREATE [UNIQUE] INDEX CONCURRENTLY IF NOT EXISTS statement with an unqualified index name`,\n );\n }\n const idempotentInSql = parsedStatement[1] !== undefined;\n if (!idempotentInSql && !governedLegacyConcurrentIndexMigrations.has(file)) {\n throw new Error(\n `${file}: opengeni:concurrent-index requires IF NOT EXISTS; bare statements are supported only for governed historical migrations`,\n );\n }\n return {\n indexName: (parsedStatement[2] ?? parsedStatement[3]!).replaceAll('\"\"', '\"'),\n lockTimeout,\n skipWhenValid: !idempotentInSql,\n statement,\n };\n}\n\nfunction parseBatchedBackfillMigration(\n file: string,\n sqlText: string,\n): { batchSize: number; lockTimeout: string; statementTimeout: string; statement: string } | null {\n const lines = sqlText.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\");\n const directiveIndex = /^-- deployment-mode: (?:rolling|maintenance)$/.test(\n lines[0]?.trim() ?? \"\",\n )\n ? 1\n : 0;\n const directive = batchedBackfillDirective.exec(lines[directiveIndex]?.trim() ?? \"\");\n if (!directive) return null;\n const statement = lines\n .slice(directiveIndex + 1)\n .filter((line) => !line.trim().startsWith(\"--\"))\n .join(\"\\n\")\n .trim();\n const withoutTrailingSemicolon = statement.endsWith(\";\")\n ? statement.slice(0, -1).trimEnd()\n : statement;\n const batchSize = Number(directive[1]!);\n if (\n !Number.isSafeInteger(batchSize) ||\n batchSize < 1 ||\n batchSize > 10_000 ||\n !/^WITH\\b/is.test(withoutTrailingSemicolon) ||\n !/\\bUPDATE\\b/is.test(withoutTrailingSemicolon) ||\n !/\\bRETURNING\\b/is.test(withoutTrailingSemicolon) ||\n !new RegExp(`\\\\bLIMIT\\\\s+${batchSize}\\\\b`, \"i\").test(withoutTrailingSemicolon) ||\n withoutTrailingSemicolon.includes(\";\")\n ) {\n throw new Error(\n `${file}: opengeni:batched-backfill requires one bounded WITH ... UPDATE ... RETURNING statement whose LIMIT matches batch-size`,\n );\n }\n return {\n batchSize,\n lockTimeout: directive[2]!,\n statementTimeout: directive[3]!,\n statement,\n };\n}\n\nasync function executeMigrationFile(\n sql: postgres.Sql,\n file: string,\n sqlText: string,\n): Promise<void> {\n const batchedBackfill = parseBatchedBackfillMigration(file, sqlText);\n if (batchedBackfill) {\n await sql`select set_config('lock_timeout', ${batchedBackfill.lockTimeout}, false)`;\n await sql`select set_config('statement_timeout', ${batchedBackfill.statementTimeout}, false)`;\n try {\n for (;;) {\n const result = await sql.unsafe(batchedBackfill.statement);\n if (result.length === 0) break;\n }\n } finally {\n await sql`select set_config('statement_timeout', '0', false)`;\n await sql`select set_config('lock_timeout', '0', false)`;\n }\n return;\n }\n const concurrentIndex = parseConcurrentIndexMigration(file, sqlText);\n if (!concurrentIndex) {\n // Migration SQL is the only writer allowed to cross protocol generations.\n // Keep the authority transaction-local and inside the same PostgreSQL simple\n // query as the migration body: setting it in a prior statement would end\n // that implicit transaction and silently lose the LOCAL value. This also\n // makes a fresh database capable of applying maintenance migration 0138\n // without a process-global PGOPTIONS escape hatch.\n await sql.unsafe(\n `SELECT pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);\\n${sqlText}`,\n );\n return;\n }\n\n await sql`select set_config('lock_timeout', ${concurrentIndex.lockTimeout}, false)`;\n try {\n const [existing] = await sql<Array<{ valid: boolean; ready: boolean }>>`\n select i.indisvalid as valid, i.indisready as ready\n from pg_catalog.pg_class c\n join pg_catalog.pg_namespace n on n.oid = c.relnamespace\n join pg_catalog.pg_index i on i.indexrelid = c.oid\n where n.nspname = current_schema() and c.relname = ${concurrentIndex.indexName}\n `;\n if (existing && (!existing.valid || !existing.ready)) {\n const quotedIndexName = `\"${concurrentIndex.indexName.replaceAll('\"', '\"\"')}\"`;\n await sql.unsafe(`DROP INDEX CONCURRENTLY ${quotedIndexName}`);\n } else if (existing && concurrentIndex.skipWhenValid) {\n return;\n }\n await sql.unsafe(concurrentIndex.statement);\n } finally {\n await sql`select set_config('lock_timeout', '0', false)`;\n }\n}\n\nfunction deploymentDepthPolicy(\n options: MigrationRuntimeOptions | undefined,\n): DeploymentDepthPolicy {\n const raw =\n options === undefined\n ? process.env.OPENGENI_MAX_NESTED_AGENT_DEPTH?.trim() || undefined\n : options.maxNestedAgentDepth;\n if (raw === undefined) {\n return { maxNestedAgentDepth: DEFAULT_MAX_NESTED_AGENT_DEPTH, source: \"default\" };\n }\n const value = typeof raw === \"number\" ? raw : Number(raw);\n if (\n !Number.isSafeInteger(value) ||\n value < 0 ||\n value > MAX_NESTED_AGENT_DEPTH ||\n (typeof raw === \"string\" && !/^(0|[1-9][0-9]*)$/.test(raw))\n ) {\n throw new Error(\n `OPENGENI_MAX_NESTED_AGENT_DEPTH must be a non-negative 32-bit integer: ${raw}`,\n );\n }\n return { maxNestedAgentDepth: value, source: \"deployment\" };\n}\n\nasync function persistDeploymentDepthPolicy(\n sql: postgres.Sql,\n policy: DeploymentDepthPolicy,\n): Promise<void> {\n const [relation] = await sql<{ exists: boolean }[]>`\n select to_regclass('nested_agent_depth_configuration') is not null as exists\n `;\n if (!relation?.exists) return;\n await sql`\n insert into \"nested_agent_depth_configuration\" (\n \"singleton\", \"max_nested_agent_depth\", \"policy_source\", \"updated_at\"\n ) values (true, ${policy.maxNestedAgentDepth}, ${policy.source}, now())\n on conflict (\"singleton\") do update\n set \"max_nested_agent_depth\" = excluded.\"max_nested_agent_depth\",\n \"policy_source\" = excluded.\"policy_source\",\n \"updated_at\" = now()\n where \"nested_agent_depth_configuration\".\"max_nested_agent_depth\"\n is distinct from excluded.\"max_nested_agent_depth\"\n or \"nested_agent_depth_configuration\".\"policy_source\"\n is distinct from excluded.\"policy_source\"\n `;\n}\n\n/**\n * Apply the OpenGeni SQL migration chain.\n *\n * STANDALONE (default, unchanged): `migrate()` / `migrate(databaseUrl)` runs the\n * whole chain with NO search_path manipulation, so every unqualified\n * table/index/policy lands in the server default schema (`public`). This is the\n * byte-for-byte historical behavior — the migration test suite calls\n * `migrate(DB_URL)` and is unaffected.\n *\n * EMBEDDED SCHEMA MODE: pass a `schema` (or set\n * `OPENGENI_DB_SCHEMA`). The migrate session then `CREATE SCHEMA IF NOT EXISTS`\n * for both `<schema>` and `opengeni_private`, and sets\n * `search_path = \"<schema>\", \"opengeni_private\", \"public\"`, so EVERY unqualified\n * DDL statement lands in the dedicated schema with NO per-statement SQL rewrite\n * (the schema-isolation contract). Two things make this work and stay idempotent:\n * 1. The policy-existence guards in the migration SQL use `current_schema()`\n * (not a hardcoded `'public'`) — so a re-run finds the policy it already\n * created in `<schema>` and DROP/CREATEs idempotently instead of failing\n * with \"policy already exists\". (This guard substitution is the migrate-\n * time enabler for the runtime search_path approach; without it the SDK\n * entry point silently fails on re-run — the migration replay hazard.)\n * 2. `public` stays LAST on the path so `gen_random_uuid()` (pgcrypto) and the\n * `vector` type — both installed into `public` by 0000 — still resolve. The\n * `opengeni_private.*` helpers are always called with an absolute prefix.\n *\n * `OPENGENI_DB_SCHEMA` defaults UNSET → `public` → standalone, so the default\n * binding never regresses.\n */\nexport async function migrate(\n databaseUrl = process.env.OPENGENI_MIGRATIONS_DATABASE_URL ??\n process.env.OPENGENI_DATABASE_URL ??\n DEFAULT_DATABASE_URL,\n schema: string | undefined = process.env.OPENGENI_DB_SCHEMA?.trim() || undefined,\n runtimeOptions?: MigrationRuntimeOptions,\n): Promise<void> {\n const migrationsDir = join(dirname(fileURLToPath(import.meta.url)), \"../drizzle\");\n const files = (await readdir(migrationsDir)).filter((file) => file.endsWith(\".sql\")).sort();\n const depthPolicy = deploymentDepthPolicy(runtimeOptions);\n const sql = postgres(databaseUrl, { max: 1 });\n try {\n // Serialize concurrent migrate() runs; the session-level lock is released\n // when the connection closes.\n await sql`SELECT pg_advisory_lock(727458)`;\n if (schema) {\n assertIdentifier(\"OPENGENI_DB_SCHEMA\", schema);\n await sql.unsafe(`CREATE SCHEMA IF NOT EXISTS \"${schema}\"`);\n // opengeni_private is also created by 0001 with an absolute prefix, but the\n // session search_path must already resolve it for the policy predicates\n // and the SECURITY DEFINER functions that inherit the caller's path.\n await sql.unsafe(`CREATE SCHEMA IF NOT EXISTS \"opengeni_private\"`);\n await sql.unsafe(`SET search_path = \"${schema}\", \"opengeni_private\", \"public\"`);\n }\n await sql`select set_config('opengeni.max_nested_agent_depth', ${String(depthPolicy.maxNestedAgentDepth)}, false)`;\n await sql`select set_config('opengeni.nested_agent_depth_policy_source', ${depthPolicy.source}, false)`;\n await sql.unsafe(\n `CREATE TABLE IF NOT EXISTS \"schema_migrations\" (\"name\" text PRIMARY KEY, \"applied_at\" timestamptz NOT NULL DEFAULT now())`,\n );\n const appliedRows = await sql`SELECT \"name\" FROM \"schema_migrations\"`;\n const applied = new Set(appliedRows.map((row) => row.name as string));\n for (const file of files) {\n if (applied.has(file)) {\n continue;\n }\n const sqlText = await readFile(join(migrationsDir, file), \"utf8\");\n await executeMigrationFile(sql, file, sqlText);\n await sql`INSERT INTO \"schema_migrations\" (\"name\") VALUES (${file}) ON CONFLICT DO NOTHING`;\n }\n // Reconcile even when all migration names were already recorded. This is\n // the only supported way to change deployment policy in a live database.\n await persistDeploymentDepthPolicy(sql, depthPolicy);\n } finally {\n await sql.end();\n }\n}\n\n/**\n * SDK entry point (Step I): run the migration chain over a host-supplied admin\n * connection string against an explicit target schema. This is the embedded\n * topology's named entry — a host calls `runMigrations(adminConnection,\n * targetSchema)` from its own provisioning code instead of relying on env vars.\n * `targetSchema` undefined → `public` → standalone behavior. Thin wrapper over\n * `migrate` so there is one migration engine.\n */\nexport async function runMigrations(\n adminConnection: string,\n targetSchema?: string,\n runtimeOptions?: MigrationRuntimeOptions,\n): Promise<void> {\n await migrate(adminConnection, targetSchema, runtimeOptions);\n}\n\nif (import.meta.main) {\n await migrate();\n console.log(\"Applied Drizzle SQL migrations.\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
|
|
2
|
+
import { type CodexTokenSnapshot, type CodexUsagePayload, type CodexFetch, type CodexRateLimitResetCreditsDetails, type ResetCreditFetchFailureReason, refreshCodexToken } from "@opengeni/codex";
|
|
3
|
+
import { encryptEnvironmentValue } from "./environment-crypto";
|
|
4
|
+
import { loadCodexCredentialForRun, recordCodexTokenRefresh, setCodexCredentialStatus, withCodexCredentialRefreshLock, type Database } from "./index";
|
|
5
|
+
export type CodexTokenDeadlineClock = {
|
|
6
|
+
setTimeout: (callback: () => void, delayMs: number) => ReturnType<typeof globalThis.setTimeout>;
|
|
7
|
+
clearTimeout: (handle: ReturnType<typeof globalThis.setTimeout>) => void;
|
|
8
|
+
};
|
|
9
|
+
export type CodexTokenDeadlineOptions = {
|
|
10
|
+
timeoutMs?: number | undefined;
|
|
11
|
+
signal?: AbortSignal | undefined;
|
|
12
|
+
clock?: CodexTokenDeadlineClock | undefined;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Bound a refresh promise without abandoning its rejection handler when the
|
|
16
|
+
* deadline or cancellation wins. The provider promise is observed exactly
|
|
17
|
+
* once, while the observer itself always fulfills, so a late provider failure
|
|
18
|
+
* cannot become an unhandled rejection or replace the authoritative outcome.
|
|
19
|
+
*/
|
|
20
|
+
export declare function withCodexTokenDeadline<T>(operation: Promise<T>, options?: CodexTokenDeadlineOptions): Promise<T>;
|
|
21
|
+
export type CodexAuthDeps = {
|
|
22
|
+
loadCredential: typeof loadCodexCredentialForRun;
|
|
23
|
+
recordRefresh: typeof recordCodexTokenRefresh;
|
|
24
|
+
setStatus: typeof setCodexCredentialStatus;
|
|
25
|
+
refresh: typeof refreshCodexToken;
|
|
26
|
+
encrypt: typeof encryptEnvironmentValue;
|
|
27
|
+
keyBytes: typeof environmentsEncryptionKeyBytes;
|
|
28
|
+
withRefreshLock: typeof withCodexCredentialRefreshLock;
|
|
29
|
+
};
|
|
30
|
+
export declare function buildCodexTokenResolver(db: Database, settings: Settings, workspaceId: string, credentialId: string, deps?: CodexAuthDeps): {
|
|
31
|
+
getToken: () => Promise<CodexTokenSnapshot>;
|
|
32
|
+
refresh: () => Promise<CodexTokenSnapshot>;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* THE single per-account usage path both the api route and an (optional) worker
|
|
36
|
+
* poll call, so the refresh discipline and the cache-write can never drift.
|
|
37
|
+
*
|
|
38
|
+
* 1. resolve a REFRESHING bearer for THIS account (proactive staleness refresh,
|
|
39
|
+
* single-flight, (id,version) CAS-persist) — this is what stops an idle
|
|
40
|
+
* account's expired JWT from 401-ing the usage read.
|
|
41
|
+
* 2. fetch GET /wham/usage with that bearer.
|
|
42
|
+
* 3. normalize (§3) into the P2/P3 contract.
|
|
43
|
+
* 4. on any windows present, write the five usage-cache columns (the TTL clock).
|
|
44
|
+
*
|
|
45
|
+
* A refresh that stamps needs_relogin returns { status:"error", reason } and never
|
|
46
|
+
* hits the provider; a transient refresh error returns a plain error payload.
|
|
47
|
+
*/
|
|
48
|
+
export declare function fetchCodexUsageForAccount(db: Database, settings: Settings, workspaceId: string, credentialId: string, fetchImpl?: CodexFetch): Promise<CodexUsagePayload>;
|
|
49
|
+
export type CodexRateLimitResetCreditsAccountResult = {
|
|
50
|
+
ok: true;
|
|
51
|
+
status: number;
|
|
52
|
+
details: CodexRateLimitResetCreditsDetails;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
status: number;
|
|
56
|
+
reason: ResetCreditFetchFailureReason | "needs_relogin";
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Fresh detailed reset-credit inventory for one exact workspace credential.
|
|
60
|
+
* The token is refreshed through the same resolver as usage and never escapes
|
|
61
|
+
* this server-side function. Detailed rows are returned to the route only and
|
|
62
|
+
* are never persisted as redemption authority.
|
|
63
|
+
*/
|
|
64
|
+
export declare function fetchCodexRateLimitResetCreditsForAccount(db: Database, settings: Settings, workspaceId: string, credentialId: string, fetchImpl?: CodexFetch): Promise<CodexRateLimitResetCreditsAccountResult>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { environmentsEncryptionKeyBytes, type McpServerConnectionRef, type Settings } from "@opengeni/config";
|
|
2
|
+
import type { ConnectionCredentialsPort, McpConnectionResourceScope, McpCredentialAuthNeededReason, McpCredentialsRequest, TurnInitiator, TurnInitiatorContext } from "@opengeni/contracts";
|
|
3
|
+
import { type DnsLookup, type FetchLike } from "@opengeni/network";
|
|
4
|
+
export { isPrivateAddress } from "@opengeni/network";
|
|
5
|
+
import { encryptEnvironmentValue } from "./environment-crypto";
|
|
6
|
+
import { loadConnectionCredentialForBroker, recordConnectionTokenRefresh, recordConnectionUsed, setConnectionStatus, type ConnectionCredentialForBroker, type Database } from "./index";
|
|
7
|
+
export type ResolveConnectionCredentialResult = {
|
|
8
|
+
status: "ok";
|
|
9
|
+
headers: Record<string, string>;
|
|
10
|
+
connectionId: string;
|
|
11
|
+
expiresAt?: Date | null;
|
|
12
|
+
} | {
|
|
13
|
+
status: "auth_needed";
|
|
14
|
+
reason: McpCredentialAuthNeededReason;
|
|
15
|
+
providerDomain: string;
|
|
16
|
+
provider?: string;
|
|
17
|
+
connectionId?: string;
|
|
18
|
+
scopes?: string[];
|
|
19
|
+
resource?: string;
|
|
20
|
+
selectedResources?: McpConnectionResourceScope[];
|
|
21
|
+
authorizationUrl?: string;
|
|
22
|
+
};
|
|
23
|
+
export type ResolveConnectionCredentialInput = {
|
|
24
|
+
workspaceId: string;
|
|
25
|
+
subjectId?: string;
|
|
26
|
+
serverId: string;
|
|
27
|
+
toolName?: string;
|
|
28
|
+
/** @deprecated Use toolName. Retained for the API's pre-existing broker call shape. */
|
|
29
|
+
toolId?: string;
|
|
30
|
+
connectionRef: McpServerConnectionRef;
|
|
31
|
+
/** Exact MCP destination whose request would receive the resolved headers. */
|
|
32
|
+
destinationUrl: string;
|
|
33
|
+
forceRefresh?: boolean;
|
|
34
|
+
};
|
|
35
|
+
export type HostMcpCredentialResolverContext = {
|
|
36
|
+
accountId: string;
|
|
37
|
+
workspaceId: string;
|
|
38
|
+
sessionId: string;
|
|
39
|
+
rootSessionId: string;
|
|
40
|
+
turnId: string;
|
|
41
|
+
attemptId: string | null;
|
|
42
|
+
executionGeneration: number;
|
|
43
|
+
initiator: TurnInitiator;
|
|
44
|
+
initiatorContext: TurnInitiatorContext;
|
|
45
|
+
surface: McpCredentialsRequest["surface"];
|
|
46
|
+
};
|
|
47
|
+
export declare class HostMcpCredentialScopeError extends Error {
|
|
48
|
+
constructor(field: "accountId" | "workspaceId" | "sessionId");
|
|
49
|
+
}
|
|
50
|
+
export declare class HostMcpCredentialBindingError extends Error {
|
|
51
|
+
constructor(field: "provider" | "providerDomain" | "connectionId" | "scopes" | "resource" | "selectedResources" | "destinationUrl");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Adapts the public embedding credential port to the runtime's connection
|
|
55
|
+
* resolver contract. Scope echoes are checked before credential headers can
|
|
56
|
+
* reach a request; the returned object is a fresh copy so a host cannot mutate
|
|
57
|
+
* headers after resolution.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildHostConnectionTokenResolver(resolve: NonNullable<ConnectionCredentialsPort["mcpCredentials"]>, context: HostMcpCredentialResolverContext): (input: ResolveConnectionCredentialInput) => Promise<ResolveConnectionCredentialResult>;
|
|
60
|
+
export type ConnectionBrokerDeps = {
|
|
61
|
+
loadCredential: typeof loadConnectionCredentialForBroker;
|
|
62
|
+
recordRefresh: typeof recordConnectionTokenRefresh;
|
|
63
|
+
setStatus: typeof setConnectionStatus;
|
|
64
|
+
recordUsed: typeof recordConnectionUsed;
|
|
65
|
+
refresh: typeof refreshOAuthConnectionCredential;
|
|
66
|
+
encrypt: typeof encryptEnvironmentValue;
|
|
67
|
+
keyBytes: typeof environmentsEncryptionKeyBytes;
|
|
68
|
+
now: () => Date;
|
|
69
|
+
};
|
|
70
|
+
export type RefreshTransportOptions = {
|
|
71
|
+
fetchImpl?: FetchLike;
|
|
72
|
+
dnsLookup?: DnsLookup;
|
|
73
|
+
};
|
|
74
|
+
export declare function buildConnectionTokenResolver(db: Database, settings: Settings, deps?: ConnectionBrokerDeps): (input: ResolveConnectionCredentialInput) => Promise<ResolveConnectionCredentialResult>;
|
|
75
|
+
export declare class ConnectionRefreshHttpError extends Error {
|
|
76
|
+
readonly httpStatus: number;
|
|
77
|
+
constructor(httpStatus: number);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Normalizes an OAuth token_type into the Authorization scheme to send. RFC 6750
|
|
81
|
+
* says the scheme is case-insensitive, but some MCP servers (e.g. Linear) reject
|
|
82
|
+
* a lowercase `bearer` — so a `bearer`/`BEARER` (or absent) token_type is sent as
|
|
83
|
+
* the canonical `Bearer`. A non-bearer scheme is passed through unchanged.
|
|
84
|
+
*/
|
|
85
|
+
export declare function normalizeBearerScheme(tokenType: string | null | undefined): string;
|
|
86
|
+
export declare function refreshOAuthConnectionCredential(cred: ConnectionCredentialForBroker, ref: McpServerConnectionRef, settings: Settings, transportOptions?: RefreshTransportOptions): Promise<{
|
|
87
|
+
credential: Record<string, unknown>;
|
|
88
|
+
expiresAt: Date | null;
|
|
89
|
+
grantedScopes?: string[];
|
|
90
|
+
}>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypts one workspace environment variable value with AES-256-GCM under an
|
|
3
|
+
* operator key held outside Postgres. Output format: `v1:<b64 iv>:<b64 ciphertext||tag>`.
|
|
4
|
+
*/
|
|
5
|
+
export declare function encryptEnvironmentValue(key: Uint8Array, plaintext: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Decrypts a stored `v1:` value. Error messages never echo plaintext or
|
|
8
|
+
* ciphertext: unknown versions throw "unsupported environment value format",
|
|
9
|
+
* auth-tag mismatches throw "environment value decryption failed".
|
|
10
|
+
*/
|
|
11
|
+
export declare function decryptEnvironmentValue(key: Uint8Array, stored: string): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type SecretForRedaction } from "@opengeni/contracts";
|
|
2
|
+
/**
|
|
3
|
+
* Strip NUL and repair invalid/lone UTF-16 surrogates in a single string.
|
|
4
|
+
* Returns the input unchanged (same reference) when it is already clean, so the
|
|
5
|
+
* common case allocates nothing.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sanitizeEventString(value: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Deep-walk a session event payload and sanitize every string value. Mirrors the
|
|
10
|
+
* shape of the worker redaction deep-walk: objects, arrays, and nested
|
|
11
|
+
* combinations are traversed; non-string leaves pass through untouched. Object
|
|
12
|
+
* keys are sanitized too -- they are jsonb-constrained the same as values.
|
|
13
|
+
*/
|
|
14
|
+
export type SanitizeEventPayloadOptions = {
|
|
15
|
+
/**
|
|
16
|
+
* Separately trusted, server-created retained-output evidence. Never populate
|
|
17
|
+
* this from a producer-controlled payload field.
|
|
18
|
+
*/
|
|
19
|
+
fullEvidence?: unknown;
|
|
20
|
+
/** Exact runtime credential provenance to remove from keys and values. */
|
|
21
|
+
knownSecrets?: readonly SecretForRedaction[];
|
|
22
|
+
};
|
|
23
|
+
export declare function sanitizeEventPayload<T>(payload: T, options?: SanitizeEventPayloadOptions): T;
|
|
24
|
+
/**
|
|
25
|
+
* Make model-facing conversation data safe for Postgres and secret-redacted.
|
|
26
|
+
*
|
|
27
|
+
* Conversation history is the replay source for the model, so this boundary
|
|
28
|
+
* retains protocol structure and non-sensitive diagnostics while removing
|
|
29
|
+
* credential-bearing fields and text before durable storage. It also performs
|
|
30
|
+
* the database-safety repair (NUL removal and UTF-16 repair).
|
|
31
|
+
*/
|
|
32
|
+
export declare function sanitizeModelPayload<T>(payload: T, knownSecrets?: readonly SecretForRedaction[]): T;
|