@opengeni/core 1.3.0 → 1.4.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.
Files changed (36) hide show
  1. package/dist/application/modal-native-live-declaration-join.d.ts +37 -0
  2. package/dist/application/modal-native-original-configuration.d.ts +79 -0
  3. package/dist/domain/capabilities.d.ts +1 -0
  4. package/dist/domain/connector-tool-permissions.d.ts +1 -1
  5. package/dist/domain/realtime-voice-billing.d.ts +95 -0
  6. package/dist/domain/scheduled-task-access.d.ts +2 -2
  7. package/dist/domain/scheduled-tasks.d.ts +2 -2
  8. package/dist/domain/session-connection-accounts.d.ts +1 -1
  9. package/dist/domain/session-start-timing.d.ts +1 -1
  10. package/dist/domain/session-tool-policy.d.ts +11 -7
  11. package/dist/domain/voice-input-billing.d.ts +77 -0
  12. package/dist/domain/web-search-billing.d.ts +45 -0
  13. package/dist/goal-admission.d.ts +24 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.js +1065 -365
  16. package/dist/index.js.map +1 -1
  17. package/dist/transcription.d.ts +119 -2
  18. package/package.json +12 -12
  19. package/src/application/modal-native-live-declaration-join.ts +196 -0
  20. package/src/application/modal-native-original-configuration.ts +261 -0
  21. package/src/default-session-model.ts +58 -88
  22. package/src/domain/capabilities.ts +44 -2
  23. package/src/domain/connector-tool-permissions.ts +24 -8
  24. package/src/domain/realtime-voice-billing.ts +400 -0
  25. package/src/domain/scheduled-task-access.ts +2 -1
  26. package/src/domain/scheduled-tasks.ts +3 -2
  27. package/src/domain/session-connection-accounts.ts +7 -3
  28. package/src/domain/session-start-timing.ts +5 -0
  29. package/src/domain/session-tool-policy.ts +20 -4
  30. package/src/domain/sessions.ts +145 -98
  31. package/src/domain/voice-input-billing.ts +315 -0
  32. package/src/domain/web-search-billing.ts +154 -0
  33. package/src/index.ts +3 -0
  34. package/src/model-catalog.ts +32 -38
  35. package/src/sandbox/routing.ts +59 -25
  36. package/src/transcription.ts +122 -3
@@ -0,0 +1,37 @@
1
+ import type { Settings } from "@opengeni/config";
2
+ import { ModalNativePreparationDeclarationV2 } from "@opengeni/contracts/modal-native-proof-v2";
3
+ import { type Database, type ModalNativeLiveOriginProjection, type ModalNativeLiveOriginResult } from "@opengeni/db";
4
+ import { type NativeOriginalConfigurationSample } from "./modal-native-original-configuration.js";
5
+ type JoinSettings = Pick<Settings, "delegationSecret" | "modalTokenId" | "modalTokenSecret" | "modalEnvironment" | "environmentsEncryptionKey">;
6
+ /** Sensitive request-local data only. Do not publish/log/cache across commit.
7
+ * The sample contains immutable credential strings which disposal cannot erase.
8
+ * This result has no native-I/O, replay, custody or effect authority. */
9
+ export type NativeLiveDeclarationJoin = Readonly<{
10
+ requestId: string;
11
+ declaration: ModalNativePreparationDeclarationV2;
12
+ projection: ModalNativeLiveOriginProjection;
13
+ configuration: NativeOriginalConfigurationSample;
14
+ }>;
15
+ export type NativeLiveDeclarationJoinResult = {
16
+ kind: "joined_data";
17
+ data: NativeLiveDeclarationJoin;
18
+ } | {
19
+ kind: "off";
20
+ } | {
21
+ kind: "refused";
22
+ } | Exclude<ModalNativeLiveOriginResult, {
23
+ kind: "live";
24
+ }>;
25
+ /** Own bytes BEFORE either verifying or parsing. After the blocking SQL join,
26
+ * re-read the explicit CURRENT deployment root/profile and reverify current
27
+ * expiry over those SAME owned bytes. A historical MAC witness is insufficient.
28
+ * No tenant/subject GUC change, general access-token resolver, provider await,
29
+ * fallback, registration or error-to-success conversion occurs here.
30
+ *
31
+ * Input execution/human fields are signed CORRELATION, not caller authority:
32
+ * compare them to the exact DB-derived accepted original projection. This does
33
+ * NOT independently prove that the canonical worker produced the request.
34
+ * Enter an existing scoped transaction before any tenancy/control/row lock;
35
+ * consume the result in that transaction and dispose its sample afterward. */
36
+ export declare function lockNativeLiveDeclarationJoinTx(scopedTx: Database, settings: JoinSettings, envelope: string, inputBody: Uint8Array): Promise<NativeLiveDeclarationJoinResult>;
37
+ export {};
@@ -0,0 +1,79 @@
1
+ import type { Settings } from "@opengeni/config";
2
+ import { ModalNativePreparationDeclarationV2 } from "@opengeni/contracts/modal-native-proof-v2";
3
+ declare const SUBKEY_DOMAIN = "opengeni:modal-native:configuration-equality:subkey:v2";
4
+ declare const MESSAGE_DOMAIN = "opengeni:modal-native:configuration-equality:framed:v2\0";
5
+ type ConfigurationSettings = Pick<Settings, "modalTokenId" | "modalTokenSecret" | "modalEnvironment" | "environmentsEncryptionKey">;
6
+ export type NativeOriginalConfigurationSnapshot = Readonly<{
7
+ serverUrl: string;
8
+ tokenId: string;
9
+ tokenSecret: string;
10
+ environment: string;
11
+ }>;
12
+ declare function readProfile(environment: string): Readonly<{
13
+ protocol: "modal-direct-original-read-grpc-v1";
14
+ version: 1;
15
+ sourceGitBlob: "7599f5b80cf111a25f78f427cfb2616d7e24b36c";
16
+ serverUrl: "https://api.modal.com:443";
17
+ serverSelection: "host-pinned-modal-0.9-default";
18
+ credentialSelection: "explicit-deployment-settings-pair-only";
19
+ environmentSelection: "explicit-own-settings-no-default";
20
+ environment: string;
21
+ environmentTransmittedByCpReads: false;
22
+ namespaceSelection: "unbound-until-authenticated-once-capture";
23
+ modalProjectionVersion: "0.9.0";
24
+ grpcJsVersion: "1.14.4";
25
+ protobufJsVersion: "7.6.5";
26
+ rpcPrefix: "/modal.client.ModalClient/";
27
+ methods: readonly ["AuthTokenGet", "WorkspaceNameLookup", "TaskGetCommandRouterAccess"];
28
+ authHeaders: {
29
+ readonly clientType: "8";
30
+ readonly clientVersion: "1.0.0";
31
+ readonly libmodalVersion: "modal-js/0.9.0";
32
+ };
33
+ tlsPolicy: "explicit-node-tls-rootCertificates-v1";
34
+ tlsRootsSha256: string;
35
+ retries: 0;
36
+ nativeChainDeadlineMs: 30000;
37
+ maxWireBytes: 1048576;
38
+ }>;
39
+ type PrivateDescription = Readonly<{
40
+ protocol: "opengeni-modal-native-configuration-equality";
41
+ version: 2;
42
+ purpose: "original-native-custody-proof";
43
+ configurationCaptureRef: ModalNativePreparationDeclarationV2["configurationCaptureRef"];
44
+ proofGrantRef: ModalNativePreparationDeclarationV2["proofGrantRef"];
45
+ declarationJson: string;
46
+ declarationSha256: string;
47
+ profile: ReturnType<typeof readProfile>;
48
+ keySelection: "existing-environments-encryption-root-current-only";
49
+ algorithm: "hmac-sha256";
50
+ subkeyDomain: typeof SUBKEY_DOMAIN;
51
+ messageDomain: typeof MESSAGE_DOMAIN;
52
+ }>;
53
+ /** Sensitive PRIVATE storage draft, not proof that any row or grant committed. */
54
+ export type NativeOriginalConfigurationDraftV2 = Readonly<{
55
+ description: PrivateDescription;
56
+ equality: Uint8Array;
57
+ }>;
58
+ declare const sampleBrand: unique symbol;
59
+ export type NativeOriginalConfigurationSample = Readonly<{
60
+ [sampleBrand]: true;
61
+ }>;
62
+ /** Samples ONLY explicit own settings, without environment/profile/SDK lookup.
63
+ * A parsed declaration merely supplies correlation. Genuine known app/image
64
+ * provenance and same-transaction host/grant/admission checks remain external.
65
+ */
66
+ export declare function sampleNativeOriginalConfiguration(settings: ConfigurationSettings, declarationBytes: Uint8Array): NativeOriginalConfigurationSample | null;
67
+ /** Private draft extraction for protected storage only; never a status DTO. */
68
+ export declare function privateNativeOriginalConfigurationDraft(sample: NativeOriginalConfigurationSample): NativeOriginalConfigurationDraftV2 | null;
69
+ /** Same sampled strings for a future authenticated native caller; NOT a grant. */
70
+ export declare function nativeOriginalConfigurationSnapshot(sample: NativeOriginalConfigurationSample): NativeOriginalConfigurationSnapshot | null;
71
+ /** PRIVATE equality check only. No public oracle, grant renewal or key fallback.
72
+ * Restart reconstruction still requires an authenticated protected original row.
73
+ */
74
+ export declare function currentNativeOriginalConfigurationMatches(sample: NativeOriginalConfigurationSample, settings: ConfigurationSettings): boolean;
75
+ /** Wipes owned mutable copies, not JS immutable strings or native I/O/custody.
76
+ * A future owning native caller must join its real callbacks separately.
77
+ */
78
+ export declare function disposeNativeOriginalConfigurationSample(sample: NativeOriginalConfigurationSample): void;
79
+ export {};
@@ -8,6 +8,7 @@ export declare function buildCapabilityCatalog(input: {
8
8
  settings: Settings;
9
9
  subjectId?: string | null;
10
10
  }): Promise<CapabilityCatalogResponse>;
11
+ export declare function applyOperatorOAuthClientRequirement(item: CapabilityCatalogItem, configured: (issuer: string) => boolean): CapabilityCatalogItem;
11
12
  export declare function createCatalogItem(input: {
12
13
  db: Database;
13
14
  accountId: string;
@@ -28,7 +28,7 @@ export declare function connectorToolGroup(tool: ListedTool): "read" | "write" |
28
28
  export declare function unpinnedConnectorToolPermissionAccount<T extends ConnectionMetadata>(ref: McpServerConnectionRef, visible: T[], subjectId: string): T | null;
29
29
  export declare function connectorToolPermissionReference(ref: McpServerConnectionRef, connectionId: string): McpServerConnectionRef;
30
30
  /** Use the same adapter-owned identities as execution for reviewed local catalogs. */
31
- export declare function reviewedConnectorToolCatalog(server: Pick<Settings["mcpServers"][number], "url" | "connectionRef" | "allowedTools">, grantedScopes: readonly string[]): ListedTool[] | null;
31
+ export declare function reviewedConnectorToolCatalog(server: Pick<Settings["mcpServers"][number], "url" | "connectionRef" | "allowedTools">, grantedScopes: readonly string[], deployment: Pick<Settings, "gmailWatchTopicName">): ListedTool[] | null;
32
32
  /** Collect discovery pages without changing the caller's connection or authority. */
33
33
  export declare function collectConnectorToolPages(client: Pick<Client, "listTools">, deadline: AbortSignal): Promise<ListedTool[]>;
34
34
  export declare function getConnectorToolPermissions(input: Input): Promise<ConnectorToolPermissionsResponse>;
@@ -0,0 +1,95 @@
1
+ import { type RealtimeVoicePricing, type Settings } from "@opengeni/config";
2
+ import type { SessionRealtimeModel } from "@opengeni/contracts";
3
+ import { type CreditDebitAttribution, type Database } from "@opengeni/db";
4
+ import { TranscriptionBillingRefusedError } from "../transcription.js";
5
+ /** Credit debit type and usage source for deployment-funded live voice. */
6
+ export declare const REALTIME_VOICE_DEBIT_TYPE = "voice_realtime_debit";
7
+ export declare const REALTIME_VOICE_SOURCE_TYPE = "voice_realtime";
8
+ export type DeploymentRealtimeVoice = {
9
+ provider: "azure-live" | "ai-gateway";
10
+ /** Credentials exist and this deployment offers the model. */
11
+ configured: boolean;
12
+ pricing: RealtimeVoicePricing | null;
13
+ };
14
+ export declare function deploymentRealtimeVoice(settings: Settings, model: SessionRealtimeModel | string): DeploymentRealtimeVoice | null;
15
+ export type RealtimeVoiceUnavailableCode = "not_configured" | "pricing_unconfigured";
16
+ /** The deployment cannot offer this live-voice model at all (not a credit refusal). */
17
+ export declare class RealtimeVoiceUnavailableError extends Error {
18
+ readonly code: RealtimeVoiceUnavailableCode;
19
+ readonly name = "RealtimeVoiceUnavailableError";
20
+ constructor(code: RealtimeVoiceUnavailableCode, message: string);
21
+ }
22
+ /** Static offer check: configured, and priced whenever credits are enforced. */
23
+ export declare function realtimeVoiceOfferProblem(settings: Settings, voice: DeploymentRealtimeVoice): RealtimeVoiceUnavailableError | null;
24
+ export declare const REALTIME_VOICE_INSUFFICIENT_CREDITS_MESSAGE = "Live voice needs Opengeni credits. Add credits to continue.";
25
+ /**
26
+ * Credits voice can spend (general and signup credits), only model-scoped
27
+ * promotional credits, or neither.
28
+ */
29
+ export type VoiceCreditStanding = "spendable" | "promotional_only" | "none";
30
+ /** `balance` must be the voice-spendable balance (see {@link VOICE_CREDIT_USAGE}). */
31
+ export declare function voiceCreditStanding(balance: {
32
+ balanceMicros: number;
33
+ promotionalCredits?: readonly {
34
+ remainingMicros: number;
35
+ }[] | undefined;
36
+ }): VoiceCreditStanding;
37
+ /**
38
+ * Insufficient-credit copy for voice. Signup credits pay for voice like
39
+ * general credits; other promotional credits are scoped to chat models, so an
40
+ * account holding only those must not be told it has nothing.
41
+ */
42
+ export declare function voiceInsufficientCreditsMessage(feature: "Live voice" | "Voice input", standing: VoiceCreditStanding): string;
43
+ export type RealtimeVoiceSettlement = {
44
+ /** Credits debited by this settlement call (0 on replay). */
45
+ debitedMicros: number;
46
+ /** Why the live call must stop now, when it must. */
47
+ stop: TranscriptionBillingRefusedError | RealtimeVoiceUnavailableError | null;
48
+ };
49
+ /**
50
+ * Credit admission and time metering for deployment-funded live voice.
51
+ *
52
+ * Admission mirrors voice-input admission (same refusal codes and HTTP
53
+ * shape): voice-spendable credits (general plus signup) must be positive, the workspace/member
54
+ * allowance must not be exhausted, and the static monthly cost cap applies.
55
+ * Metering bills every started minute of each issued provider connection,
56
+ * measured only from server-observed facts (connection claim time, owner
57
+ * heartbeats, owner-proven end). Each (connection, minute) is one idempotent
58
+ * usage receipt plus one post-use debit.
59
+ */
60
+ export declare function createRealtimeVoiceBilling(deps: {
61
+ db: Database;
62
+ settings: Settings;
63
+ }): {
64
+ /**
65
+ * Throws RealtimeVoiceUnavailableError when the deployment cannot offer
66
+ * the model and TranscriptionBillingRefusedError for a credit refusal.
67
+ * Workspace-funded models are always admitted here.
68
+ */
69
+ admit(input: {
70
+ accountId: string;
71
+ workspaceId: string;
72
+ model: SessionRealtimeModel;
73
+ attribution: CreditDebitAttribution;
74
+ now?: Date;
75
+ }): Promise<void>;
76
+ /** Spendable credits are positive (always true when credits are not enforced). */
77
+ hasSpendableCredits(accountId: string): Promise<boolean>;
78
+ /** Credit standing for live voice ("spendable" when credits are not enforced). */
79
+ creditStanding(accountId: string): Promise<VoiceCreditStanding>;
80
+ /**
81
+ * Settle every started minute observed so far for one live-voice mode and
82
+ * report whether the call must stop. Safe to call repeatedly and
83
+ * concurrently: receipts and debits are idempotent per (connection, minute).
84
+ */
85
+ settle(input: {
86
+ workspaceId: string;
87
+ sessionId: string;
88
+ realtimeId: string;
89
+ /** Authenticated caller; its attribution applies only when it owns the call. */
90
+ callerSubjectId: string;
91
+ attribution: CreditDebitAttribution;
92
+ now?: Date;
93
+ }): Promise<RealtimeVoiceSettlement>;
94
+ };
95
+ export type RealtimeVoiceBilling = ReturnType<typeof createRealtimeVoiceBilling>;
@@ -10,7 +10,7 @@
10
10
  * ordinary owner update path, so a refresh never widens beyond what the calling
11
11
  * person could grant with an edit. See docs/scheduled-task-access.md.
12
12
  */
13
- import { type Settings } from "@opengeni/config";
13
+ import { type Settings, type FirstPartyMcpToolPolicySettings } from "@opengeni/config";
14
14
  import { type AccessGrant, type ConnectionMetadata, type FirstPartyMcpToolName, type McpConnectionAccountSelection, type Permission, type RefreshScheduledTaskAccessRequest, type ScheduledTask, type ScheduledTaskAccessAttention, type ScheduledTaskAccessConnector, type ScheduledTaskPolicyDrift, type ScheduledTaskRun, type ScheduledTaskRunAccessFailure, type SessionAuthorizationPort, type SessionAuthorizationSurface, type ToolRef } from "@opengeni/contracts";
15
15
  import { type Database, type ScheduledTaskCreatorPolicy } from "@opengeni/db";
16
16
  import { type AccessGrantAuthorization } from "../access/index.js";
@@ -88,7 +88,7 @@ export type FirstPartyToolPermissionRequirements = (tools: readonly FirstPartyMc
88
88
  */
89
89
  export declare function planScheduledTaskOpenGeniTools(input: {
90
90
  creatorPolicy: Pick<ScheduledTaskCreatorPolicy, "firstPartyMcpTools" | "firstPartyMcpPermissions"> | null;
91
- settings: Pick<Settings, "defaultFirstPartyMcpTools" | "allowedFirstPartyMcpTools">;
91
+ settings: FirstPartyMcpToolPolicySettings;
92
92
  grantPermissions: readonly Permission[];
93
93
  grantPermissionMode?: AccessGrant["permissionMode"];
94
94
  permissionsRequiredByTools: FirstPartyToolPermissionRequirements;
@@ -1,4 +1,4 @@
1
- import { type Settings } from "@opengeni/config";
1
+ import { type FirstPartyMcpToolPolicySettings, type Settings } from "@opengeni/config";
2
2
  import type { AccessGrant, ScheduledTask, ScheduledTaskAgentConfig, Session, ToolRef, SessionAuthorizationPort, SessionAuthorizationSurface, CreateScheduledTaskRequest as CreateScheduledTaskPayload, UpdateScheduledTaskRequest as UpdateScheduledTaskPayload } from "@opengeni/contracts";
3
3
  import { type KnowledgeContext, type Database, type ScheduledTaskCreatorPolicy, type SessionCommandActor, type TemporalScheduleCleanupClaim, type UpdateScheduledTaskInput } from "@opengeni/db";
4
4
  import { type AccessGrantAuthorization } from "../access/index.js";
@@ -195,4 +195,4 @@ export declare function manualScheduledTaskTriggerUsageKey(workspaceId: string,
195
195
  * provider, or treats ambient worker credentials as responder capability.
196
196
  * Mutable rig/variable-set metadata is revalidated again at dispatch.
197
197
  */
198
- export declare function validateIncidentTelemetryPreflightSelection(settings: Pick<Settings, "allowedFirstPartyMcpTools" | "defaultFirstPartyMcpTools">, agentConfig: ScheduledTaskAgentConfig): void;
198
+ export declare function validateIncidentTelemetryPreflightSelection(settings: FirstPartyMcpToolPolicySettings, agentConfig: ScheduledTaskAgentConfig): void;
@@ -1,5 +1,5 @@
1
1
  import type { AccessGrant } from "@opengeni/contracts";
2
- import type { Settings } from "@opengeni/config";
2
+ import { type Settings } from "@opengeni/config";
3
3
  import { type Database } from "@opengeni/db";
4
4
  /** Freeze the authenticated request's eligible accounts before starting voice.
5
5
  * The resulting lease snapshot, not a later participant or ambient connection,
@@ -1,5 +1,5 @@
1
1
  import { type Observability } from "@opengeni/observability";
2
- type SessionStartPhase = "workspace_read" | "model_catalog_initial" | "default_model" | "model_catalog_effective" | "capability_settings" | "initiator_freeze" | "allowance" | "shell_insert" | "initialize" | "event_fanout" | "workflow_wake" | "session_reload";
2
+ type SessionStartPhase = "workspace_read" | "idempotent_replay" | "model_catalog_initial" | "default_model" | "model_catalog_effective" | "capability_settings" | "resource_validation" | "rig_binding" | "model_admission" | "connection_freeze" | "initiator_freeze" | "allowance" | "shell_insert" | "initialize" | "event_fanout" | "workflow_wake" | "session_reload";
3
3
  export type SessionStartObservability = Pick<Observability, "startSpan">;
4
4
  /** Content-free child spans around existing work; export is never joined and
5
5
  * diagnostic failures cannot alter the lifecycle or its exact error. */
@@ -1,6 +1,6 @@
1
1
  import { type Settings } from "@opengeni/config";
2
2
  import { type AgentToolEnvironment, type AgentMediaAttachment, type Session, type SessionTurn, type SessionEffectiveToolPolicy, type SessionToolPolicy, type ToolRef } from "@opengeni/contracts";
3
- import { type Database } from "@opengeni/db";
3
+ import { requireWorkspace, type Database } from "@opengeni/db";
4
4
  export type ResolvedSessionToolPolicy = {
5
5
  toolRefs: ToolRef[];
6
6
  effectivePolicy: SessionEffectiveToolPolicy;
@@ -35,7 +35,9 @@ export declare function resolveTurnToolPolicy(input: Omit<SessionToolPolicyInput
35
35
  turn: Pick<SessionTurn, "tools" | "metadata">;
36
36
  }): ResolvedSessionToolPolicy;
37
37
  /** Resolve availability and defaults from the same subject-scoped registry read. */
38
- export declare function workspaceSessionToolPolicyContext(db: Database, workspaceId: string, settings: Settings, subjectId?: string): Promise<{
38
+ export declare function workspaceSessionToolPolicyContext(db: Database, workspaceId: string, settings: Settings, subjectId?: string,
39
+ /** An in-flight read of this exact workspace a caller already started. */
40
+ workspaceRead?: ReturnType<typeof requireWorkspace>): Promise<{
39
41
  workspaceServerIds: string[];
40
42
  workspaceDefaultServerIds: string[];
41
43
  }>;
@@ -73,7 +75,9 @@ export declare function workspaceSessionEffectiveToolsContext(deps: {
73
75
  db: Database;
74
76
  settings: Settings;
75
77
  objectStorage?: unknown;
76
- }, workspaceId: string, subjectId: string, sessions: readonly Session[]): Promise<SessionEffectiveToolsContext>;
78
+ }, workspaceId: string, subjectId: string, sessions: readonly Session[],
79
+ /** An in-flight read of this exact workspace a caller already started. */
80
+ workspaceRead?: ReturnType<typeof requireWorkspace>): Promise<SessionEffectiveToolsContext>;
77
81
  /** The server can prove attachment policy, not successful future provider calls. */
78
82
  export declare function sessionEffectiveToolProjectionInput(session: Session & {
79
83
  agent: NonNullable<Session["agent"]>;
@@ -106,10 +110,10 @@ export declare function sessionEffectiveToolProjectionInput(session: Session & {
106
110
  mcpServerIds: string[];
107
111
  productServerIds: ReadonlySet<string>;
108
112
  environment: AgentToolEnvironment;
109
- runtimeToolNames: ("apply_patch" | "code_search" | "exec_command" | "generate_image" | "generate_video" | "get_video_generation_capabilities" | "image_generation" | "knowledge_source_fetch" | "knowledge_source_read" | "list_models" | "operation_read" | "refresh_credentials" | "repository_skill_read" | "request_human_input" | "skill_checkout" | "skill_install" | "skill_publish" | "skill_read" | "skill_remove" | "skill_save" | "skill_search" | "tool_invoke" | "tool_list" | "tool_search" | "view_image" | "web_search" | "write_stdin" | "x_search")[];
110
- hostedToolNames: ("apply_patch" | "code_search" | "exec_command" | "generate_image" | "generate_video" | "get_video_generation_capabilities" | "image_generation" | "knowledge_source_fetch" | "knowledge_source_read" | "list_models" | "operation_read" | "refresh_credentials" | "repository_skill_read" | "request_human_input" | "skill_checkout" | "skill_install" | "skill_publish" | "skill_read" | "skill_remove" | "skill_save" | "skill_search" | "tool_invoke" | "tool_list" | "tool_search" | "view_image" | "web_search" | "write_stdin" | "x_search")[];
111
- sandboxToolNames: ("apply_patch" | "code_search" | "exec_command" | "generate_image" | "generate_video" | "get_video_generation_capabilities" | "image_generation" | "knowledge_source_fetch" | "knowledge_source_read" | "list_models" | "operation_read" | "refresh_credentials" | "repository_skill_read" | "request_human_input" | "skill_checkout" | "skill_install" | "skill_publish" | "skill_read" | "skill_remove" | "skill_save" | "skill_search" | "tool_invoke" | "tool_list" | "tool_search" | "view_image" | "web_search" | "write_stdin" | "x_search")[];
112
- routerToolNames: ("apply_patch" | "code_search" | "exec_command" | "generate_image" | "generate_video" | "get_video_generation_capabilities" | "image_generation" | "knowledge_source_fetch" | "knowledge_source_read" | "list_models" | "operation_read" | "refresh_credentials" | "repository_skill_read" | "request_human_input" | "skill_checkout" | "skill_install" | "skill_publish" | "skill_read" | "skill_remove" | "skill_save" | "skill_search" | "tool_invoke" | "tool_list" | "tool_search" | "view_image" | "web_search" | "write_stdin" | "x_search")[];
113
+ runtimeToolNames: ("apply_patch" | "code_search" | "exec_command" | "generate_image" | "generate_video" | "get_video_generation_capabilities" | "image_generation" | "knowledge_source_fetch" | "knowledge_source_read" | "list_models" | "operation_read" | "refresh_credentials" | "repository_skill_read" | "request_human_input" | "skill_checkout" | "skill_install" | "skill_publish" | "skill_read" | "skill_remove" | "skill_save" | "skill_search" | "tool_invoke" | "tool_list" | "tool_search" | "view_image" | "web_fetch" | "web_search" | "write_stdin" | "x_search")[];
114
+ hostedToolNames: ("apply_patch" | "code_search" | "exec_command" | "generate_image" | "generate_video" | "get_video_generation_capabilities" | "image_generation" | "knowledge_source_fetch" | "knowledge_source_read" | "list_models" | "operation_read" | "refresh_credentials" | "repository_skill_read" | "request_human_input" | "skill_checkout" | "skill_install" | "skill_publish" | "skill_read" | "skill_remove" | "skill_save" | "skill_search" | "tool_invoke" | "tool_list" | "tool_search" | "view_image" | "web_fetch" | "web_search" | "write_stdin" | "x_search")[];
115
+ sandboxToolNames: ("apply_patch" | "code_search" | "exec_command" | "generate_image" | "generate_video" | "get_video_generation_capabilities" | "image_generation" | "knowledge_source_fetch" | "knowledge_source_read" | "list_models" | "operation_read" | "refresh_credentials" | "repository_skill_read" | "request_human_input" | "skill_checkout" | "skill_install" | "skill_publish" | "skill_read" | "skill_remove" | "skill_save" | "skill_search" | "tool_invoke" | "tool_list" | "tool_search" | "view_image" | "web_fetch" | "web_search" | "write_stdin" | "x_search")[];
116
+ routerToolNames: ("apply_patch" | "code_search" | "exec_command" | "generate_image" | "generate_video" | "get_video_generation_capabilities" | "image_generation" | "knowledge_source_fetch" | "knowledge_source_read" | "list_models" | "operation_read" | "refresh_credentials" | "repository_skill_read" | "request_human_input" | "skill_checkout" | "skill_install" | "skill_publish" | "skill_read" | "skill_remove" | "skill_save" | "skill_search" | "tool_invoke" | "tool_list" | "tool_search" | "view_image" | "web_fetch" | "web_search" | "write_stdin" | "x_search")[];
113
117
  upfrontToolNames: Set<string>;
114
118
  mediaAttachment: AgentMediaAttachment | {
115
119
  readonly image: null;
@@ -0,0 +1,77 @@
1
+ import { type Settings, type VoiceInputPricing, type VoiceInputUsage } from "@opengeni/config";
2
+ import { type Database } from "@opengeni/db";
3
+ import { type TranscriptionBilling, type TranscriptionBillingContext } from "../transcription.js";
4
+ /** Credit debit type and usage source for deployment-funded voice input. */
5
+ export declare const VOICE_INPUT_DEBIT_TYPE = "voice_transcription_debit";
6
+ export declare const VOICE_INPUT_SOURCE_TYPE = "voice_transcription";
7
+ /** How the billed quantity was measured; recorded on the ledger entry. */
8
+ export type VoiceInputBillingBasis = "provider_tokens" | "provider_duration" | "server_duration" | "reconciled_receipt";
9
+ /**
10
+ * Pick the billed measurement. Provider usage wins when this deployment can
11
+ * price it (token usage with token rates, or a reported duration). Otherwise
12
+ * the duration the server measured from WAV bytes it produced bills. Every
13
+ * deployment-funded call carries that server duration, so an unpriced usage
14
+ * shape never leaves a call unbillable. A client-reported duration is never a
15
+ * billing input.
16
+ */
17
+ export declare function voiceInputBillableUsage(input: {
18
+ pricing: VoiceInputPricing;
19
+ usage: VoiceInputUsage | null;
20
+ trustedDurationSeconds?: number | undefined;
21
+ }): {
22
+ usage: VoiceInputUsage;
23
+ basis: VoiceInputBillingBasis;
24
+ };
25
+ /**
26
+ * Same admission shape as knowledge-search and session admission:
27
+ * voice-spendable credits (general plus signup, not model-scoped promotional
28
+ * credits) must be positive, the workspace/member
29
+ * allowance must not be exhausted, and a static monthly cost cap applies.
30
+ * Admission is a read, not a reservation; settlement is post-use and may take
31
+ * an account below zero by at most the concurrent in-flight calls.
32
+ */
33
+ export declare function createVoiceInputBilling(deps: {
34
+ db: Database;
35
+ settings: Settings;
36
+ /** Reconciliation is best-effort at admission; failures are reported here. */
37
+ onReconcileError?: (error: unknown, scope: {
38
+ accountId: string;
39
+ workspaceId: string;
40
+ }) => void;
41
+ /** Minimum spacing of admission-time reconciliation per workspace (default 5 min). */
42
+ reconcileIntervalMilliseconds?: number;
43
+ }): TranscriptionBilling;
44
+ /**
45
+ * Two commits, in order: the `model.cost` usage receipt (Insights spend,
46
+ * monthly cost cap, member-allowance attribution), then the post-use credit
47
+ * debit. The receipt is the first-writer authority for the amount and the
48
+ * durable record of the charge: if the debit commit fails, the receipt stays
49
+ * and {@link reconcileUnsettledVoiceInputCharges} applies the same debit (same
50
+ * idempotency key) on the workspace's next admission. A retried unit settles
51
+ * exactly once at the originally recorded price.
52
+ */
53
+ export declare function settleVoiceInputUsage(db: Database, input: {
54
+ accountId: string;
55
+ workspaceId: string;
56
+ providerId: string;
57
+ model: string;
58
+ billing: TranscriptionBillingContext;
59
+ usage: VoiceInputUsage;
60
+ basis: VoiceInputBillingBasis;
61
+ providerCostMicros: number;
62
+ creditCostMicros: number;
63
+ }): Promise<{
64
+ creditCostMicros: number;
65
+ }>;
66
+ /**
67
+ * Apply the debit for every voice usage receipt in this workspace whose debit
68
+ * never committed. Idempotent with the inline settlement (same key), bounded
69
+ * per call, and safe to run concurrently.
70
+ */
71
+ export declare function reconcileUnsettledVoiceInputCharges(db: Database, input: {
72
+ accountId: string;
73
+ workspaceId: string;
74
+ minAgeMilliseconds?: number;
75
+ }): Promise<{
76
+ reconciled: number;
77
+ }>;
@@ -0,0 +1,45 @@
1
+ import type { Settings } from "@opengeni/config";
2
+ import { type Database } from "@opengeni/db";
3
+ /** Credit ledger type and usage source for deployment-funded web search calls. */
4
+ export declare const WEB_SEARCH_DEBIT_TYPE = "web_search_debit";
5
+ export declare const WEB_SEARCH_SOURCE_TYPE = "web_search";
6
+ /** Same rule as other deployment-funded resources: Stripe or managed limits. */
7
+ export declare function webSearchCreditBillingActive(settings: Pick<Settings, "billingMode" | "usageLimitsMode">): boolean;
8
+ /** Exact attempt that called the tool. Every charge is attributed to its turn. */
9
+ export type WebSearchCallScope = {
10
+ accountId: string;
11
+ workspaceId: string;
12
+ sessionId: string;
13
+ turnId: string;
14
+ attemptId: string;
15
+ };
16
+ export type WebSearchCallCost = {
17
+ /** Model tool-call or Codemode operation identity, unique within the attempt. */
18
+ operationId: string;
19
+ operation: "search" | "fetch";
20
+ provider: string;
21
+ providerMicros: number;
22
+ creditMicros: number;
23
+ marginBps: number;
24
+ basis: "provider_reported" | "configured_price" | "list_price";
25
+ };
26
+ export declare class WebSearchBillingRefusedError extends Error {
27
+ readonly code: "insufficient_credits" | "allowance_exhausted";
28
+ constructor(code: "insufficient_credits" | "allowance_exhausted", message: string);
29
+ }
30
+ /**
31
+ * Admission and post-use settlement for paid web search, matching paid
32
+ * Knowledge queries and voice input: admission reads general credits and the
33
+ * workspace/member allowance (a read, not a reservation); settlement records a
34
+ * durable usage receipt and the idempotent debit in one transaction. Calls
35
+ * that cost nothing (free providers, or billing inactive) are never refused.
36
+ */
37
+ export declare function createWebSearchBilling(deps: {
38
+ db: Database;
39
+ settings: Settings;
40
+ }): {
41
+ active: boolean;
42
+ admit(scope: WebSearchCallScope, expectedProviderMicros: number): Promise<void>;
43
+ settle(scope: WebSearchCallScope, cost: WebSearchCallCost): Promise<void>;
44
+ };
45
+ export type WebSearchBilling = ReturnType<typeof createWebSearchBilling>;
@@ -102,6 +102,7 @@ export declare function resolveGoalModelAdmission(db: Database, settings: Settin
102
102
  delegationSecret?: string | undefined;
103
103
  defaultFirstPartyMcpTools?: ("artifacts_archive" | "artifacts_create" | "artifacts_get_source" | "artifacts_list" | "artifacts_prepare_upload" | "artifacts_publish" | "artifacts_restore" | "artifacts_rollback" | "atlassian_get" | "atlassian_search" | "atlassian_sources_list" | "browser_act" | "browser_auth" | "browser_clipboard" | "browser_debug" | "browser_download_save" | "browser_downloads" | "browser_identity" | "browser_lifecycle" | "browser_observe" | "browser_open" | "browser_publish" | "browser_read" | "browser_screenshot" | "browser_tabs" | "capability_authorization_request" | "capability_catalog_search" | "command_read" | "command_wait" | "company_profile_confirm" | "company_profile_propose" | "computer_act" | "computer_clipboard" | "computer_lifecycle" | "computer_observe" | "computer_open" | "computer_targets" | "connected_machine_enroll_token" | "connected_machine_remove" | "custom_mcp_setup_request" | "editable_artifact_apply" | "editable_artifact_create" | "editable_artifact_export" | "editable_artifact_export_status" | "editable_artifact_get" | "editable_artifact_import" | "editable_artifact_inspect" | "editable_artifact_list" | "environment_list" | "environment_set_variable" | "fiken_bank_accounts_list" | "fiken_companies_list" | "fiken_contact_create" | "fiken_contacts_list" | "fiken_invoice_draft_create" | "fiken_invoice_get" | "fiken_invoices_list" | "fiken_products_list" | "fiken_purchases_list" | "fiken_sales_list" | "github_connect_link" | "github_repositories_list" | "goal_complete" | "goal_pause" | "goal_progress" | "goal_resume" | "goal_set" | "goal_update" | "instruction_policy_get" | "instruction_policy_propose" | "instruction_policy_save" | "interaction_discover" | "interaction_request_human" | "knowledge_archive" | "knowledge_browse" | "knowledge_correct" | "knowledge_get" | "knowledge_prepare_save" | "knowledge_propose" | "knowledge_retain_file" | "knowledge_retain_message" | "knowledge_save" | "knowledge_search" | "memory_correct" | "memory_save" | "memory_search" | "preference_propose" | "preference_registry_get" | "preference_registry_summary" | "project_create" | "project_delete" | "project_get" | "project_list" | "project_reorder" | "project_update" | "reddit_accounts_list" | "reddit_mentions_live" | "reddit_post_reply" | "reddit_posts_sync" | "reddit_search_live" | "reddit_thread_fetch" | "remember" | "remember_confirm" | "rig_get" | "rig_list" | "rig_promote" | "rig_propose_change" | "rig_verify" | "run_on" | "sandbox_attach" | "sandbox_file_publish" | "sandbox_provision" | "sandbox_swap" | "sandboxes_list" | "scheduled_task_runs_list" | "scheduled_tasks_create" | "scheduled_tasks_delete" | "scheduled_tasks_get" | "scheduled_tasks_list" | "scheduled_tasks_pause" | "scheduled_tasks_resume" | "scheduled_tasks_trigger" | "scheduled_tasks_update" | "session_create" | "session_events" | "session_get" | "session_human_input_respond" | "session_pause" | "session_resume" | "session_send_message" | "session_set_model" | "session_set_project" | "session_steer" | "session_wait" | "sessions_list" | "set_other_session_title" | "set_session_title" | "slack_bot_channel_history" | "slack_bot_delete_message" | "slack_bot_file_content" | "slack_bot_file_info" | "slack_bot_list_channels" | "slack_bot_list_files" | "slack_bot_list_users" | "slack_bot_post_message" | "slack_bot_prepare_message" | "slack_bot_search" | "slack_bot_send_prepared_message" | "slack_bot_thread_replies" | "slack_bot_upload_file" | "social_connections_list" | "social_daily_analysis_context" | "social_mentions_live" | "social_post_reply" | "social_posts_recent" | "social_posts_sync" | "social_search_live" | "social_thread_fetch" | "task_note_archive" | "task_note_promote_instruction_policy" | "task_note_promote_knowledge" | "task_note_promote_preference" | "task_note_replace" | "task_note_save" | "task_notes_list" | "variable_set_get_variable" | "variable_set_list" | "variable_set_set_variable" | "wait_for_input" | "work_claim_release" | "work_claim_upsert" | "x_accounts_list" | "x_mentions_live" | "x_post_reply" | "x_posts_sync" | "x_search_live" | "x_thread_fetch")[] | undefined;
104
104
  allowedFirstPartyMcpTools?: ("artifacts_archive" | "artifacts_create" | "artifacts_get_source" | "artifacts_list" | "artifacts_prepare_upload" | "artifacts_publish" | "artifacts_restore" | "artifacts_rollback" | "atlassian_get" | "atlassian_search" | "atlassian_sources_list" | "browser_act" | "browser_auth" | "browser_clipboard" | "browser_debug" | "browser_download_save" | "browser_downloads" | "browser_identity" | "browser_lifecycle" | "browser_observe" | "browser_open" | "browser_publish" | "browser_read" | "browser_screenshot" | "browser_tabs" | "capability_authorization_request" | "capability_catalog_search" | "command_read" | "command_wait" | "company_profile_confirm" | "company_profile_propose" | "computer_act" | "computer_clipboard" | "computer_lifecycle" | "computer_observe" | "computer_open" | "computer_targets" | "connected_machine_enroll_token" | "connected_machine_remove" | "custom_mcp_setup_request" | "editable_artifact_apply" | "editable_artifact_create" | "editable_artifact_export" | "editable_artifact_export_status" | "editable_artifact_get" | "editable_artifact_import" | "editable_artifact_inspect" | "editable_artifact_list" | "environment_list" | "environment_set_variable" | "fiken_bank_accounts_list" | "fiken_companies_list" | "fiken_contact_create" | "fiken_contacts_list" | "fiken_invoice_draft_create" | "fiken_invoice_get" | "fiken_invoices_list" | "fiken_products_list" | "fiken_purchases_list" | "fiken_sales_list" | "github_connect_link" | "github_repositories_list" | "goal_complete" | "goal_pause" | "goal_progress" | "goal_resume" | "goal_set" | "goal_update" | "instruction_policy_get" | "instruction_policy_propose" | "instruction_policy_save" | "interaction_discover" | "interaction_request_human" | "knowledge_archive" | "knowledge_browse" | "knowledge_correct" | "knowledge_get" | "knowledge_prepare_save" | "knowledge_propose" | "knowledge_retain_file" | "knowledge_retain_message" | "knowledge_save" | "knowledge_search" | "memory_correct" | "memory_save" | "memory_search" | "preference_propose" | "preference_registry_get" | "preference_registry_summary" | "project_create" | "project_delete" | "project_get" | "project_list" | "project_reorder" | "project_update" | "reddit_accounts_list" | "reddit_mentions_live" | "reddit_post_reply" | "reddit_posts_sync" | "reddit_search_live" | "reddit_thread_fetch" | "remember" | "remember_confirm" | "rig_get" | "rig_list" | "rig_promote" | "rig_propose_change" | "rig_verify" | "run_on" | "sandbox_attach" | "sandbox_file_publish" | "sandbox_provision" | "sandbox_swap" | "sandboxes_list" | "scheduled_task_runs_list" | "scheduled_tasks_create" | "scheduled_tasks_delete" | "scheduled_tasks_get" | "scheduled_tasks_list" | "scheduled_tasks_pause" | "scheduled_tasks_resume" | "scheduled_tasks_trigger" | "scheduled_tasks_update" | "session_create" | "session_events" | "session_get" | "session_human_input_respond" | "session_pause" | "session_resume" | "session_send_message" | "session_set_model" | "session_set_project" | "session_steer" | "session_wait" | "sessions_list" | "set_other_session_title" | "set_session_title" | "slack_bot_channel_history" | "slack_bot_delete_message" | "slack_bot_file_content" | "slack_bot_file_info" | "slack_bot_list_channels" | "slack_bot_list_files" | "slack_bot_list_users" | "slack_bot_post_message" | "slack_bot_prepare_message" | "slack_bot_search" | "slack_bot_send_prepared_message" | "slack_bot_thread_replies" | "slack_bot_upload_file" | "social_connections_list" | "social_daily_analysis_context" | "social_mentions_live" | "social_post_reply" | "social_posts_recent" | "social_posts_sync" | "social_search_live" | "social_thread_fetch" | "task_note_archive" | "task_note_promote_instruction_policy" | "task_note_promote_knowledge" | "task_note_promote_preference" | "task_note_replace" | "task_note_save" | "task_notes_list" | "variable_set_get_variable" | "variable_set_list" | "variable_set_set_variable" | "wait_for_input" | "work_claim_release" | "work_claim_upsert" | "x_accounts_list" | "x_mentions_live" | "x_post_reply" | "x_posts_sync" | "x_search_live" | "x_thread_fetch")[] | undefined;
105
+ artifactMaterializerDeployed: boolean;
105
106
  streamTokenSecret?: string | undefined;
106
107
  streamControlEnabled: boolean;
107
108
  workDiscoveryEnabled: boolean;
@@ -201,6 +202,20 @@ export declare function resolveGoalModelAdmission(db: Database, settings: Settin
201
202
  voiceInputAzureApiVersion?: string | undefined;
202
203
  voiceInputAzureApiKey?: string | undefined;
203
204
  voiceInputAzureAdToken?: string | undefined;
205
+ azureLiveEndpoint?: string | undefined;
206
+ azureLiveApiKey?: string | undefined;
207
+ azureLiveDeployment: string;
208
+ azureLiveVoice: string;
209
+ azureLivePricingJson?: string | undefined;
210
+ aiGatewayRealtimePricingJson?: string | undefined;
211
+ voiceInputMaiEndpoint?: string | undefined;
212
+ voiceInputMaiApiKey?: string | undefined;
213
+ voiceInputMaiApiVersion: string;
214
+ voiceInputMaiPricingJson?: string | undefined;
215
+ voiceInputMaiModel: string;
216
+ voiceInputAzureModel?: string | undefined;
217
+ voiceInputOpenaiPricingJson?: string | undefined;
218
+ voiceInputAzurePricingJson?: string | undefined;
204
219
  voiceInputCodexExperimentalEnabled: boolean;
205
220
  modelPricingJson: string;
206
221
  modelCatalogSource: "code" | "database";
@@ -231,6 +246,15 @@ export declare function resolveGoalModelAdmission(db: Database, settings: Settin
231
246
  openaiReasoningEncryptedContent: boolean;
232
247
  openaiMaxRetries: number;
233
248
  webSearchEnabled: boolean;
249
+ webSearchProvider?: string | undefined;
250
+ webSearchApiKey?: string | undefined;
251
+ webSearchBaseUrl?: string | undefined;
252
+ webFetchProvider?: string | undefined;
253
+ webFetchApiKey?: string | undefined;
254
+ webFetchBaseUrl?: string | undefined;
255
+ webSearchProviderMode?: string | undefined;
256
+ webSearchPricingJson?: string | undefined;
257
+ webSearchRequestTimeoutMs: number;
234
258
  jevApiKey?: string | undefined;
235
259
  jevBaseUrl: string;
236
260
  jevModel: string;
package/dist/index.d.ts CHANGED
@@ -7,6 +7,9 @@ export * from "./domain/session-connection-accounts.js";
7
7
  export * from "./domain/organization-integration-catalog.js";
8
8
  export * from "./domain/knowledge.js";
9
9
  export * from "./domain/knowledge-search.js";
10
+ export * from "./domain/voice-input-billing.js";
11
+ export * from "./domain/web-search-billing.js";
12
+ export * from "./domain/realtime-voice-billing.js";
10
13
  export * from "./dependencies.js";
11
14
  export * from "./workflow-wake-contract.js";
12
15
  export * from "./user-presence.js";