@opengeni/db 0.27.8 → 0.27.9
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-IHPCI4GV.js → chunk-2JFRTKTG.js} +1 -1
- package/dist/chunk-2JFRTKTG.js.map +1 -0
- package/dist/{chunk-M6EOXYHK.js → chunk-NX2JNJJP.js} +8 -4
- package/dist/{chunk-M6EOXYHK.js.map → chunk-NX2JNJJP.js.map} +1 -1
- package/dist/codex-token-resolver.d.ts +50 -8
- package/dist/connection-token-resolver.d.ts +45 -7
- package/dist/database.d.ts +137 -0
- package/dist/index.d.ts +21 -154
- package/dist/index.js +2694 -2615
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +1 -1
- package/dist/memory-governance.d.ts +1 -1
- package/dist/new-session-drafts.d.ts +1 -1
- package/dist/preference-registry.d.ts +1 -1
- package/dist/provision-roles.d.ts +1 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +1 -1
- package/dist/schema.d.ts +27 -0
- package/dist/schema.js +1 -1
- package/dist/scoped-knowledge.d.ts +1 -1
- package/dist/session-control.d.ts +1 -1
- package/dist/session-queue-commands.d.ts +1 -1
- package/dist/session-realtime-context.d.ts +1 -1
- package/dist/session-realtime-ledger.d.ts +1 -1
- package/dist/session-realtime-mirror.d.ts +1 -1
- package/dist/session-realtime-state.d.ts +1 -1
- package/dist/session-realtime-terminal.d.ts +1 -1
- package/dist/session-realtime.d.ts +1 -1
- package/dist/session-tool-call-settlement.d.ts +1 -1
- package/dist/turn-initiator.d.ts +1 -1
- package/dist/workspace-artifacts.d.ts +1 -1
- package/dist/workspace-instruction-policies.d.ts +1 -1
- package/drizzle/0171_social_connection_subject_ownership.sql +53 -0
- package/package.json +3 -3
- package/src/codex-token-resolver.ts +102 -49
- package/src/connection-token-resolver.ts +67 -26
- package/src/database.ts +393 -0
- package/src/index.ts +279 -479
- package/src/insights.ts +2 -2
- package/src/memory-governance.ts +2 -2
- package/src/new-session-drafts.ts +1 -1
- package/src/preference-registry.ts +2 -2
- package/src/provision-roles.ts +1 -1
- package/src/runtime-posture.ts +1 -1
- package/src/schema.ts +11 -3
- package/src/scoped-knowledge.ts +2 -2
- package/src/session-control.ts +1 -1
- package/src/session-queue-commands.ts +1 -1
- package/src/session-realtime-context.ts +1 -1
- package/src/session-realtime-ledger.ts +1 -1
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-realtime-state.ts +1 -1
- package/src/session-realtime-terminal.ts +1 -1
- package/src/session-realtime.ts +1 -1
- package/src/session-tool-call-settlement.ts +1 -1
- package/src/turn-initiator.ts +1 -1
- package/src/workspace-artifacts.ts +2 -2
- package/src/workspace-instruction-policies.ts +2 -2
- package/dist/chunk-IHPCI4GV.js.map +0 -1
package/dist/insights.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Database } from "./
|
|
1
|
+
import type { Database } from "./database";
|
|
2
2
|
import { type MemoryOperationPlanInput, type MemoryRevertPlanInput } from "./memory-domain";
|
|
3
3
|
export declare class MemoryGovernanceAuthorityError extends Error {
|
|
4
4
|
readonly name = "MemoryGovernanceAuthorityError";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NewSessionDraftOptions, LatencyMode, ReasoningEffort, ResourceRef, ToolRef } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
import * as schema from "./schema";
|
|
4
4
|
export type NewSessionDraftRow = typeof schema.newSessionDrafts.$inferSelect;
|
|
5
5
|
export declare class NewSessionDraftConflictError extends Error {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PreferenceRegistryDescriptor, type CreatePreferenceRegistryProposalRequest, type PreferenceRegistryConflictStrategy, type PreferenceRegistryScope, type PreferenceRegistryStatus } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
export declare class PreferenceRegistryNotFoundError extends Error {
|
|
4
4
|
readonly name = "PreferenceRegistryNotFoundError";
|
|
5
5
|
}
|
package/dist/provision-roles.js
CHANGED
package/dist/schema.d.ts
CHANGED
|
@@ -6930,6 +6930,7 @@ export declare const sessions: import("drizzle-orm/pg-core").PgTableWithColumns<
|
|
|
6930
6930
|
ownerSubjectId: string;
|
|
6931
6931
|
providerDomain: string;
|
|
6932
6932
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
6933
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
6933
6934
|
}[];
|
|
6934
6935
|
driverParam: unknown;
|
|
6935
6936
|
notNull: true;
|
|
@@ -6948,6 +6949,7 @@ export declare const sessions: import("drizzle-orm/pg-core").PgTableWithColumns<
|
|
|
6948
6949
|
ownerSubjectId: string;
|
|
6949
6950
|
providerDomain: string;
|
|
6950
6951
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
6952
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
6951
6953
|
}[];
|
|
6952
6954
|
}>;
|
|
6953
6955
|
toolPolicy: import("drizzle-orm/pg-core").PgColumn<{
|
|
@@ -11958,6 +11960,7 @@ export declare const sessionTurns: import("drizzle-orm/pg-core").PgTableWithColu
|
|
|
11958
11960
|
ownerSubjectId: string;
|
|
11959
11961
|
providerDomain: string;
|
|
11960
11962
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
11963
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
11961
11964
|
}[];
|
|
11962
11965
|
driverParam: unknown;
|
|
11963
11966
|
notNull: true;
|
|
@@ -11976,6 +11979,7 @@ export declare const sessionTurns: import("drizzle-orm/pg-core").PgTableWithColu
|
|
|
11976
11979
|
ownerSubjectId: string;
|
|
11977
11980
|
providerDomain: string;
|
|
11978
11981
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
11982
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
11979
11983
|
}[];
|
|
11980
11984
|
}>;
|
|
11981
11985
|
cancelledBy: import("drizzle-orm/pg-core").PgColumn<{
|
|
@@ -14751,6 +14755,7 @@ export declare const sessionSystemUpdates: import("drizzle-orm/pg-core").PgTable
|
|
|
14751
14755
|
ownerSubjectId: string;
|
|
14752
14756
|
providerDomain: string;
|
|
14753
14757
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
14758
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
14754
14759
|
}[];
|
|
14755
14760
|
driverParam: unknown;
|
|
14756
14761
|
notNull: true;
|
|
@@ -14769,6 +14774,7 @@ export declare const sessionSystemUpdates: import("drizzle-orm/pg-core").PgTable
|
|
|
14769
14774
|
ownerSubjectId: string;
|
|
14770
14775
|
providerDomain: string;
|
|
14771
14776
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
14777
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
14772
14778
|
}[];
|
|
14773
14779
|
}>;
|
|
14774
14780
|
state: import("drizzle-orm/pg-core").PgColumn<{
|
|
@@ -15088,6 +15094,7 @@ export declare const sessionSystemUpdateOutbox: import("drizzle-orm/pg-core").Pg
|
|
|
15088
15094
|
ownerSubjectId: string;
|
|
15089
15095
|
providerDomain: string;
|
|
15090
15096
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
15097
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
15091
15098
|
}[];
|
|
15092
15099
|
driverParam: unknown;
|
|
15093
15100
|
notNull: true;
|
|
@@ -15106,6 +15113,7 @@ export declare const sessionSystemUpdateOutbox: import("drizzle-orm/pg-core").Pg
|
|
|
15106
15113
|
ownerSubjectId: string;
|
|
15107
15114
|
providerDomain: string;
|
|
15108
15115
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
15116
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
15109
15117
|
}[];
|
|
15110
15118
|
}>;
|
|
15111
15119
|
status: import("drizzle-orm/pg-core").PgColumn<{
|
|
@@ -22720,6 +22728,7 @@ export declare const scheduledTasks: import("drizzle-orm/pg-core").PgTableWithCo
|
|
|
22720
22728
|
ownerSubjectId: string;
|
|
22721
22729
|
providerDomain: string;
|
|
22722
22730
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
22731
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
22723
22732
|
}[];
|
|
22724
22733
|
driverParam: unknown;
|
|
22725
22734
|
notNull: true;
|
|
@@ -22738,6 +22747,7 @@ export declare const scheduledTasks: import("drizzle-orm/pg-core").PgTableWithCo
|
|
|
22738
22747
|
ownerSubjectId: string;
|
|
22739
22748
|
providerDomain: string;
|
|
22740
22749
|
kind?: "api_key" | "app_install" | "delegated" | "oauth2" | undefined;
|
|
22750
|
+
connectionType?: "mcp" | "social" | undefined;
|
|
22741
22751
|
}[];
|
|
22742
22752
|
}>;
|
|
22743
22753
|
reusableSessionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
@@ -27176,6 +27186,23 @@ export declare const socialConnections: import("drizzle-orm/pg-core").PgTableWit
|
|
|
27176
27186
|
identity: undefined;
|
|
27177
27187
|
generated: undefined;
|
|
27178
27188
|
}, {}, {}>;
|
|
27189
|
+
subjectId: import("drizzle-orm/pg-core").PgColumn<{
|
|
27190
|
+
name: "subject_id";
|
|
27191
|
+
tableName: "social_connections";
|
|
27192
|
+
dataType: "string";
|
|
27193
|
+
columnType: "PgText";
|
|
27194
|
+
data: string;
|
|
27195
|
+
driverParam: string;
|
|
27196
|
+
notNull: false;
|
|
27197
|
+
hasDefault: false;
|
|
27198
|
+
isPrimaryKey: false;
|
|
27199
|
+
isAutoincrement: false;
|
|
27200
|
+
hasRuntimeDefault: false;
|
|
27201
|
+
enumValues: [string, ...string[]];
|
|
27202
|
+
baseColumn: never;
|
|
27203
|
+
identity: undefined;
|
|
27204
|
+
generated: undefined;
|
|
27205
|
+
}, {}, {}>;
|
|
27179
27206
|
provider: import("drizzle-orm/pg-core").PgColumn<{
|
|
27180
27207
|
name: "provider";
|
|
27181
27208
|
tableName: "social_connections";
|
package/dist/schema.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EligibleKnowledgeClaim, KnowledgeChangeProposalRecord, KnowledgeClaimEvidencePolarity, KnowledgeClaimOrigin, KnowledgeClaimRecord, KnowledgeClaimRelationType, KnowledgeClaimReviewState, KnowledgeDocumentVersionRecord, KnowledgeFactObjectKind, KnowledgeFactRecord, KnowledgeLifecycleEventType, KnowledgeLifecycleState, KnowledgeProviderRecord, KnowledgeSourceAclVersionRecord, KnowledgeSourceObjectRecord, KnowledgeSourceRecord, KnowledgeSyncRunRecord, ScopedKnowledgeActor, ScopedKnowledgeScope } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
type KnowledgeWriteContext = {
|
|
4
4
|
accountId: string;
|
|
5
5
|
workspaceId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SessionMcpApprovalPolicy, type TurnInitiatorContext } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
import * as schema from "./schema";
|
|
4
4
|
export declare const SESSION_ANCESTRY_LIMIT = 10000;
|
|
5
5
|
export type WorkspaceControlLockMode = "none" | "share" | "update";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ResourceRef, type McpPersonalConnectionDelegation, type LatencyMode, type ReasoningEffort, type TurnExecutionPolicyV1 } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
import { type SessionCommandActor, type SessionCommandReceiptRow } from "./session-control";
|
|
4
4
|
import * as schema from "./schema";
|
|
5
5
|
export type QueueCommandConflictCode = "QUEUE_VERSION_CHANGED" | "QUEUE_PROMPT_STARTED" | "QUEUE_ANCHOR_CHANGED" | "PROMPT_CHANGED" | "DRAFT_CHANGED" | "DRAFT_NOT_EMPTY" | "EDIT_SOURCE_CHANGED";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Database } from "./
|
|
1
|
+
import type { Database } from "./database";
|
|
2
2
|
export declare const SESSION_REALTIME_CONTEXT_MAX_BYTES = 65536;
|
|
3
3
|
export declare const SESSION_REALTIME_TAIL_SOURCE = "transcript_tail_flush";
|
|
4
4
|
export declare const SESSION_REALTIME_TAIL_INSTRUCTION = "The user just ended the realtime voice session but remains reachable by text. Ending voice changes only the communication mode; it does not stop, pause, or complete existing work. Treat the remaining transcript tail as additional context. If work was already underway, continue it from the current state. Change or stop that work only if the user explicitly requested it. If nothing was underway and the transcript contains no unhandled request, acknowledge briefly; otherwise handle any unhandled request.";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SessionRealtimeMode } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
import { type AssertSessionRealtimeOwnerInput } from "./session-realtime";
|
|
4
4
|
export declare const SESSION_REALTIME_LEDGER_MAX_BATCH = 64;
|
|
5
5
|
export declare const SESSION_REALTIME_LEDGER_MAX_TEXT_BYTES = 131072;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HumanInputQuestion, HumanInputResponse } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
import * as schema from "./schema";
|
|
4
4
|
export type SessionRealtimeMirrorChannel = "speakable" | "commentary" | null;
|
|
5
5
|
export type MirrorSessionRealtimeContextInput = {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { Database } from "./
|
|
1
|
+
import type { Database } from "./database";
|
|
2
2
|
export declare function sessionRealtimeIsActiveInTransaction(db: Database, workspaceId: string, sessionId: string, now?: Date): Promise<boolean>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SessionRealtimeEndReason, SessionRealtimeMode, SessionRealtimeModel } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
import * as schema from "./schema";
|
|
4
4
|
export { sessionRealtimeIsActiveInTransaction } from "./session-realtime-state";
|
|
5
5
|
export declare const SESSION_REALTIME_LEASE_MS = 30000;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SessionEvent } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
export declare const TOOL_RESULT_TYPE_BY_CALL_TYPE: Readonly<Record<string, string>>;
|
|
4
4
|
export declare function historyCallId(item: Record<string, unknown>): string | null;
|
|
5
5
|
export declare function historyItemType(item: Record<string, unknown>): string | null;
|
package/dist/turn-initiator.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TurnInitiator, type TurnInitiatorContext } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
import type { SessionCommandActor } from "./session-control";
|
|
4
4
|
export declare const UNATTRIBUTED_LEGACY_INITIATOR: TurnInitiator;
|
|
5
5
|
export type FrozenTurnInitiator = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WorkspaceArtifact, WorkspaceArtifactDetailResponse, WorkspaceArtifactMutationResponse, WorkspaceArtifactVersion } from "@opengeni/contracts";
|
|
2
|
-
import type { Database } from "./
|
|
2
|
+
import type { Database } from "./database";
|
|
3
3
|
type ArtifactMutationToolName = "artifacts_create" | "artifacts_publish" | "artifacts_rollback";
|
|
4
4
|
export declare class WorkspaceArtifactNotFoundError extends Error {
|
|
5
5
|
readonly name = "WorkspaceArtifactNotFoundError";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WorkspaceInstructionPolicySnapshot } from "@opengeni/contracts";
|
|
2
2
|
import type { WorkspaceInstructionPolicyActivationResponse, WorkspaceInstructionPolicyDiffResponse, WorkspaceInstructionPolicyDraftProvenanceSource, WorkspaceInstructionPolicyHead, WorkspaceInstructionPolicyListQuery, WorkspaceInstructionPolicyListResponse, WorkspaceInstructionPolicyOnboardingProposal, WorkspaceInstructionPolicyOnboardingProposalListQuery, WorkspaceInstructionPolicyOnboardingProposalListResponse, WorkspaceInstructionPolicyProvenanceSource, WorkspaceInstructionPolicyRevision, WorkspaceInstructionPolicyTarget } from "@opengeni/contracts";
|
|
3
|
-
import type { Database } from "./
|
|
3
|
+
import type { Database } from "./database";
|
|
4
4
|
type DraftRequestInput = WorkspaceInstructionPolicyTarget & {
|
|
5
5
|
operationId: string;
|
|
6
6
|
accountId: string;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Extend first-party social credentials with the same workspace/personal ownership boundary as MCP connections.
|
|
3
|
+
|
|
4
|
+
ALTER TABLE social_connections
|
|
5
|
+
ADD COLUMN subject_id text;
|
|
6
|
+
|
|
7
|
+
DROP INDEX social_connections_workspace_provider_handle_idx;
|
|
8
|
+
CREATE UNIQUE INDEX social_connections_workspace_provider_handle_idx
|
|
9
|
+
ON social_connections (workspace_id, provider, account_handle)
|
|
10
|
+
WHERE subject_id IS NULL;
|
|
11
|
+
CREATE UNIQUE INDEX social_connections_subject_provider_handle_idx
|
|
12
|
+
ON social_connections (workspace_id, subject_id, provider)
|
|
13
|
+
WHERE subject_id IS NOT NULL;
|
|
14
|
+
CREATE INDEX social_connections_subject_provider_status_idx
|
|
15
|
+
ON social_connections (workspace_id, subject_id, provider, status);
|
|
16
|
+
|
|
17
|
+
DROP POLICY workspace_isolation ON social_connections;
|
|
18
|
+
CREATE POLICY workspace_isolation ON social_connections
|
|
19
|
+
USING (
|
|
20
|
+
opengeni_private.workspace_rls_visible(account_id, workspace_id)
|
|
21
|
+
AND (
|
|
22
|
+
subject_id IS NULL
|
|
23
|
+
OR subject_id = nullif(current_setting('opengeni.subject_id', true), '')
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
WITH CHECK (
|
|
27
|
+
opengeni_private.workspace_rls_visible(account_id, workspace_id)
|
|
28
|
+
AND (
|
|
29
|
+
subject_id IS NULL
|
|
30
|
+
OR subject_id = nullif(current_setting('opengeni.subject_id', true), '')
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
DROP POLICY workspace_isolation ON social_posts;
|
|
35
|
+
CREATE POLICY workspace_isolation ON social_posts
|
|
36
|
+
USING (
|
|
37
|
+
opengeni_private.workspace_rls_visible(account_id, workspace_id)
|
|
38
|
+
AND EXISTS (
|
|
39
|
+
SELECT 1
|
|
40
|
+
FROM social_connections connection
|
|
41
|
+
WHERE connection.id = social_posts.connection_id
|
|
42
|
+
AND connection.workspace_id = social_posts.workspace_id
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
WITH CHECK (
|
|
46
|
+
opengeni_private.workspace_rls_visible(account_id, workspace_id)
|
|
47
|
+
AND EXISTS (
|
|
48
|
+
SELECT 1
|
|
49
|
+
FROM social_connections connection
|
|
50
|
+
WHERE connection.id = social_posts.connection_id
|
|
51
|
+
AND connection.workspace_id = social_posts.workspace_id
|
|
52
|
+
)
|
|
53
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/db",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.9",
|
|
4
4
|
"description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@opengeni/codex": "^0.2.10",
|
|
54
|
-
"@opengeni/config": "^0.10.
|
|
55
|
-
"@opengeni/contracts": "^0.38.
|
|
54
|
+
"@opengeni/config": "^0.10.12",
|
|
55
|
+
"@opengeni/contracts": "^0.38.2",
|
|
56
56
|
"@opengeni/network": "^0.2.0",
|
|
57
57
|
"drizzle-orm": "^0.45.2",
|
|
58
58
|
"postgres": "^3.4.7"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Hoisted here from apps/worker/src/activities/codex-auth.ts so BOTH the worker
|
|
4
4
|
// (turn-time bearer for the streamed run) AND the api (the /wham/usage quota-bar
|
|
5
5
|
// reads) drive ONE resolver — no duplicated refresh/CAS/single-flight logic. The
|
|
6
|
-
// worker re-exports buildCodexTokenResolver from
|
|
6
|
+
// worker re-exports buildCodexTokenResolver from @opengeni/db for back-compat, so
|
|
7
7
|
// the agent-turn.ts call site is unchanged.
|
|
8
8
|
//
|
|
9
9
|
// Why @opengeni/db is the right home: the resolver only orchestrates accessors
|
|
@@ -35,15 +35,52 @@ import {
|
|
|
35
35
|
refreshCodexToken,
|
|
36
36
|
} from "@opengeni/codex";
|
|
37
37
|
import { encryptEnvironmentValue } from "./environment-crypto";
|
|
38
|
-
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
import type { Database } from "./database";
|
|
39
|
+
|
|
40
|
+
export type CodexCredentialTokens = {
|
|
41
|
+
accessToken: string;
|
|
42
|
+
refreshToken: string;
|
|
43
|
+
idToken: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type CodexCredentialForRun = {
|
|
47
|
+
id: string;
|
|
48
|
+
version: number;
|
|
49
|
+
workspaceId: string;
|
|
50
|
+
tokens: CodexCredentialTokens;
|
|
51
|
+
chatgptAccountId: string | null;
|
|
52
|
+
scopes: string | null;
|
|
53
|
+
planType: string | null;
|
|
54
|
+
isFedramp: boolean;
|
|
55
|
+
expiresAt: Date | null;
|
|
56
|
+
lastRefreshAt: Date | null;
|
|
57
|
+
status: string;
|
|
58
|
+
lastError: string | null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type CodexAccountUsageSnapshot = {
|
|
62
|
+
primaryUsedPercent?: number | null;
|
|
63
|
+
primaryResetAt?: Date | null;
|
|
64
|
+
secondaryUsedPercent?: number | null;
|
|
65
|
+
secondaryResetAt?: Date | null;
|
|
66
|
+
checkedAt?: Date;
|
|
67
|
+
resetCreditAvailableCount?: number | null;
|
|
68
|
+
resetCreditsCheckedAt?: Date | null;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type CodexCredentialRefreshInput = {
|
|
72
|
+
id: string;
|
|
73
|
+
version: number;
|
|
74
|
+
workspaceId: string;
|
|
75
|
+
credentialEncrypted: string;
|
|
76
|
+
expiresAt: Date | null;
|
|
77
|
+
lastRefreshAt: Date;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
type CodexCredentialStatusTarget = {
|
|
81
|
+
id: string;
|
|
82
|
+
version: number;
|
|
83
|
+
};
|
|
47
84
|
|
|
48
85
|
// Single-flight per CREDENTIAL INSTANCE (row id + version), process-module scope.
|
|
49
86
|
// Keying by the loaded credential's id+version — NOT by workspaceId alone (P1-b) —
|
|
@@ -142,25 +179,37 @@ export async function withCodexTokenDeadline<T>(
|
|
|
142
179
|
|
|
143
180
|
// Dependencies are injectable so the lifecycle logic (single-flight, staleness,
|
|
144
181
|
// needs_relogin transition) is unit-testable without a database. Production uses
|
|
145
|
-
// the real db + codex functions
|
|
182
|
+
// the root composition wrapper supplies the real db + codex functions.
|
|
146
183
|
export type CodexAuthDeps = {
|
|
147
|
-
loadCredential:
|
|
148
|
-
|
|
149
|
-
|
|
184
|
+
loadCredential: (
|
|
185
|
+
db: Database,
|
|
186
|
+
settings: Settings,
|
|
187
|
+
workspaceId: string,
|
|
188
|
+
credentialId: string,
|
|
189
|
+
) => Promise<CodexCredentialForRun | null>;
|
|
190
|
+
recordRefresh: (db: Database, input: CodexCredentialRefreshInput) => Promise<boolean>;
|
|
191
|
+
setStatus: (
|
|
192
|
+
db: Database,
|
|
193
|
+
workspaceId: string,
|
|
194
|
+
status: "active" | "needs_relogin" | "error",
|
|
195
|
+
lastError: string | null,
|
|
196
|
+
target: CodexCredentialStatusTarget,
|
|
197
|
+
) => Promise<boolean>;
|
|
150
198
|
refresh: typeof refreshCodexToken;
|
|
151
199
|
encrypt: typeof encryptEnvironmentValue;
|
|
152
200
|
keyBytes: typeof environmentsEncryptionKeyBytes;
|
|
153
|
-
withRefreshLock:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
201
|
+
withRefreshLock: <T>(
|
|
202
|
+
db: Database,
|
|
203
|
+
workspaceId: string,
|
|
204
|
+
credentialId: string,
|
|
205
|
+
fn: (lockedDb: Database) => Promise<T>,
|
|
206
|
+
) => Promise<T>;
|
|
207
|
+
recordUsage?: (
|
|
208
|
+
db: Database,
|
|
209
|
+
workspaceId: string,
|
|
210
|
+
credentialId: string,
|
|
211
|
+
snapshot: CodexAccountUsageSnapshot,
|
|
212
|
+
) => Promise<boolean>;
|
|
164
213
|
};
|
|
165
214
|
|
|
166
215
|
export function buildCodexTokenResolver(
|
|
@@ -173,7 +222,7 @@ export function buildCodexTokenResolver(
|
|
|
173
222
|
// 0 rows against the now-inactive row — so a refresh racing a switch can never
|
|
174
223
|
// clobber the newly-active account. The single-flight map needs zero change.
|
|
175
224
|
credentialId: string,
|
|
176
|
-
deps: CodexAuthDeps
|
|
225
|
+
deps: CodexAuthDeps,
|
|
177
226
|
): { getToken: () => Promise<CodexTokenSnapshot>; refresh: () => Promise<CodexTokenSnapshot> } {
|
|
178
227
|
const snapshot = (cred: CodexCredentialForRun): CodexTokenSnapshot => ({
|
|
179
228
|
accessToken: cred.tokens.accessToken,
|
|
@@ -336,9 +385,10 @@ export async function fetchCodexUsageForAccount(
|
|
|
336
385
|
settings: Settings,
|
|
337
386
|
workspaceId: string,
|
|
338
387
|
credentialId: string,
|
|
388
|
+
deps: CodexAuthDeps,
|
|
339
389
|
fetchImpl: CodexFetch = fetch,
|
|
340
390
|
): Promise<CodexUsagePayload> {
|
|
341
|
-
const resolver = buildCodexTokenResolver(db, settings, workspaceId, credentialId);
|
|
391
|
+
const resolver = buildCodexTokenResolver(db, settings, workspaceId, credentialId, deps);
|
|
342
392
|
let token: CodexTokenSnapshot;
|
|
343
393
|
try {
|
|
344
394
|
token = await resolver.getToken();
|
|
@@ -373,27 +423,29 @@ export async function fetchCodexUsageForAccount(
|
|
|
373
423
|
// cached without erasing or falsely refreshing the last valid quota truth.
|
|
374
424
|
// Cache-write is best-effort: a disconnect under us (false) or a transient
|
|
375
425
|
// write error must NOT sink the freshly-read result we are about to return.
|
|
376
|
-
await
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
426
|
+
await deps
|
|
427
|
+
.recordUsage?.(db, workspaceId, credentialId, {
|
|
428
|
+
...(parsedQuota
|
|
429
|
+
? {
|
|
430
|
+
primaryUsedPercent: normalized.fiveHour?.percent ?? null,
|
|
431
|
+
primaryResetAt: normalized.fiveHour?.resetAt
|
|
432
|
+
? new Date(normalized.fiveHour.resetAt)
|
|
433
|
+
: null,
|
|
434
|
+
secondaryUsedPercent: normalized.weekly?.percent ?? null,
|
|
435
|
+
secondaryResetAt: normalized.weekly?.resetAt
|
|
436
|
+
? new Date(normalized.weekly.resetAt)
|
|
437
|
+
: null,
|
|
438
|
+
checkedAt,
|
|
439
|
+
}
|
|
440
|
+
: {}),
|
|
441
|
+
...(normalized.rateLimitResetCredits
|
|
442
|
+
? {
|
|
443
|
+
resetCreditAvailableCount: normalized.rateLimitResetCredits.availableCount,
|
|
444
|
+
resetCreditsCheckedAt: checkedAt,
|
|
445
|
+
}
|
|
446
|
+
: {}),
|
|
447
|
+
})
|
|
448
|
+
.catch(() => undefined);
|
|
397
449
|
}
|
|
398
450
|
|
|
399
451
|
return normalized;
|
|
@@ -418,9 +470,10 @@ export async function fetchCodexRateLimitResetCreditsForAccount(
|
|
|
418
470
|
settings: Settings,
|
|
419
471
|
workspaceId: string,
|
|
420
472
|
credentialId: string,
|
|
473
|
+
deps: CodexAuthDeps,
|
|
421
474
|
fetchImpl: CodexFetch = fetch,
|
|
422
475
|
): Promise<CodexRateLimitResetCreditsAccountResult> {
|
|
423
|
-
const resolver = buildCodexTokenResolver(db, settings, workspaceId, credentialId);
|
|
476
|
+
const resolver = buildCodexTokenResolver(db, settings, workspaceId, credentialId, deps);
|
|
424
477
|
let token: CodexTokenSnapshot;
|
|
425
478
|
try {
|
|
426
479
|
token = await resolver.getToken();
|