@opengeni/api-router 5.4.1 → 5.5.0-canary.37090756480001

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.
@@ -10,9 +10,30 @@ export declare function refreshClaudeSubscriptionUsage(db: Database, settings: S
10
10
  resetsAt: string | null;
11
11
  status: "allowed" | "allowed_warning" | "rejected" | null;
12
12
  observedAt: string;
13
+ source?: "provider" | "response_headers" | undefined;
13
14
  }[];
14
15
  observedAt: string | null;
15
16
  source: "provider" | "response_headers" | null;
16
17
  refreshStatus: "available" | "not_checked" | "reconnect" | "scope_required" | "unavailable";
17
18
  refreshCheckedAt: string | null;
19
+ requestStatus?: {
20
+ status: "allowed" | "allowed_warning" | "rejected" | null;
21
+ resetsAt: string | null;
22
+ representativeClaim: "five_hour" | "overage" | "seven_day" | "seven_day_opus" | "seven_day_overage_included" | "seven_day_sonnet" | null;
23
+ overageStatus: "allowed" | "allowed_warning" | "rejected" | null;
24
+ overageResetsAt: string | null;
25
+ upstreamModelId: string | null;
26
+ observedAt: string;
27
+ source?: "provider" | "response_headers" | undefined;
28
+ } | null | undefined;
29
+ requestRestrictions?: {
30
+ status: "allowed" | "allowed_warning" | "rejected" | null;
31
+ resetsAt: string | null;
32
+ representativeClaim: "five_hour" | "overage" | "seven_day" | "seven_day_opus" | "seven_day_overage_included" | "seven_day_sonnet" | null;
33
+ overageStatus: "allowed" | "allowed_warning" | "rejected" | null;
34
+ overageResetsAt: string | null;
35
+ upstreamModelId: string | null;
36
+ observedAt: string;
37
+ source?: "provider" | "response_headers" | undefined;
38
+ }[] | undefined;
18
39
  }>;
@@ -31,4 +31,11 @@ export declare function workspaceControlBusyHttpError(error: unknown): ApiHttpEr
31
31
  * code in `details.code` so clients can branch without parsing messages.
32
32
  */
33
33
  export declare function agentConfigHttpError(error: unknown): ApiHttpError | null;
34
+ /**
35
+ * The requested or stored model is not in the live catalog. Rendered as the
36
+ * same 422 `validation_failed` with its historical message, plus
37
+ * `details.code: "model_unavailable"` and the model id so clients can ask for
38
+ * another model rather than offer a retry that cannot succeed.
39
+ */
40
+ export declare function modelUnavailableHttpError(error: unknown): ApiHttpError | null;
34
41
  export {};
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  startSlackInteractionPump,
13
13
  startTemporalScheduleCleanupPump,
14
14
  withAccessGrantSessionRlsContext
15
- } from "./chunk-2QCRFD6U.js";
15
+ } from "./chunk-ZQ7PIFGF.js";
16
16
  import "./chunk-OGQDXVXY.js";
17
17
 
18
18
  // src/index.ts
@@ -7,7 +7,7 @@ export declare function requireGitHubLensConnect(deps: ApiRouteDeps): Promise<{
7
7
  eventTypes: string[];
8
8
  configuration: {};
9
9
  sessionTemplate: {
10
- bundledSkillIds?: ("builtin:document-parsing" | "builtin:opengeni-client" | "builtin:opengeni-documents" | "builtin:opengeni-help" | "builtin:opengeni-presentations" | "builtin:opengeni-projects" | "builtin:opengeni-sites" | "builtin:opengeni-skills" | "builtin:opengeni-spreadsheets" | "builtin:opengeni-video-generation" | "builtin:opengeni-visualize")[] | undefined;
10
+ bundledSkillIds?: ("builtin:document-parsing" | "builtin:opengeni-client" | "builtin:opengeni-documents" | "builtin:opengeni-help" | "builtin:opengeni-presentations" | "builtin:opengeni-projects" | "builtin:opengeni-schedules" | "builtin:opengeni-sites" | "builtin:opengeni-skills" | "builtin:opengeni-spreadsheets" | "builtin:opengeni-video-generation" | "builtin:opengeni-visualize")[] | undefined;
11
11
  prompt: string;
12
12
  instructions: string | null;
13
13
  resources: ({
@@ -47,7 +47,7 @@ export declare function requireGitHubLensConnect(deps: ApiRouteDeps): Promise<{
47
47
  eager?: boolean | undefined;
48
48
  optional?: boolean | undefined;
49
49
  }[];
50
- firstPartyMcpTools: ("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_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")[];
50
+ firstPartyMcpTools: ("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")[];
51
51
  firstPartyMcpPermissions: ("account:admin" | "account:read" | "api_keys:manage" | "artifacts:publish" | "artifacts:read" | "billing:manage" | "billing:read" | "capabilities:manage" | "codemode:call" | "connections:read" | "connections:write" | "documents:manage" | "documents:search" | "enrollments:manage" | "enrollments:read" | "environments:manage" | "environments:use" | "files:read" | "files:upload" | "files:write" | "github:manage" | "github:use" | "goals:manage" | "mcp_servers:attach" | "members:manage" | "rigs:manage" | "rigs:use" | "scheduled_tasks:manage" | "scheduled_tasks:run" | "secrets:list" | "secrets:read" | "secrets:write" | "sessions:control" | "sessions:create" | "sessions:read" | "stream:acknowledge" | "stream:control" | "stream:view" | "terminal:attach" | "usage_allowances:manage" | "variable-sets:attach" | "variable-sets:list" | "variable-sets:manage" | "variable-sets:read" | "variable-sets:use" | "variable-sets:write" | "workspace:admin" | "workspace:create" | "workspace:read")[];
52
52
  model: string | null;
53
53
  reasoningEffort: "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh" | null;
@@ -21,7 +21,7 @@ export declare const SLACK_SESSION_INSTRUCTIONS: string;
21
21
  * tools are explicit-only, so freeze that narrow selection at session creation;
22
22
  * ordinary interaction messages remain owned by the durable delivery pump.
23
23
  */
24
- export declare const SLACK_TASK_FIRST_PARTY_MCP_TOOLS: ("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_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")[];
24
+ export declare const SLACK_TASK_FIRST_PARTY_MCP_TOOLS: ("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")[];
25
25
  /**
26
26
  * The workspace's default first-party selection (or the deployment default)
27
27
  * plus the bounded Slack task tools. These are added whatever the base is, then
@@ -294,6 +294,14 @@ export declare const FIRST_PARTY_TOOL_AUTHORIZATION: {
294
294
  sessionRequired: true;
295
295
  allOf: "sessions:read"[];
296
296
  };
297
+ browser_downloads: {
298
+ sessionRequired: true;
299
+ allOf: "sessions:read"[];
300
+ };
301
+ browser_download_save: {
302
+ sessionRequired: true;
303
+ allOf: ("files:upload" | "sessions:control")[];
304
+ };
297
305
  browser_auth: {
298
306
  sessionRequired: true;
299
307
  allOf: "sessions:control"[];
@@ -9,6 +9,7 @@ export declare function stripeCheckoutSessionCreateParams(input: {
9
9
  amountMicros: number;
10
10
  creditsProductId?: string | undefined;
11
11
  publicBaseUrl?: string | undefined;
12
+ webBaseUrl?: string | undefined;
12
13
  successUrl?: string | undefined;
13
14
  cancelUrl?: string | undefined;
14
15
  idempotencyKey: string;
@@ -16,6 +17,7 @@ export declare function stripeCheckoutSessionCreateParams(input: {
16
17
  export declare function stripeBillingPortalSessionCreateParams(input: {
17
18
  customerId: string;
18
19
  publicBaseUrl?: string | undefined;
20
+ webBaseUrl?: string | undefined;
19
21
  returnUrl?: string | undefined;
20
22
  }): Stripe.BillingPortal.SessionCreateParams;
21
23
  export type StripeCheckoutCreditDecision = {
@@ -31,7 +33,8 @@ export type StripeCheckoutCreditDecision = {
31
33
  * Sessions created outside OpenGeni on the same Stripe account carry no
32
34
  * `opengeni_` metadata and are acknowledged without effect. An OpenGeni
33
35
  * session with malformed credit metadata still throws so the failure stays
34
- * visible. Credits are granted only once Stripe reports the payment `paid`:
36
+ * visible. Credits are granted once Stripe reports the payment `paid` or a
37
+ * completed checkout fully covered by a coupon:
35
38
  * on `checkout.session.completed` for immediate methods, or on
36
39
  * `checkout.session.async_payment_succeeded` for delayed methods, whose
37
40
  * completion event arrives `unpaid`. Both events carry the same session
@@ -16,7 +16,106 @@ export declare const ClientErrorReport: z.ZodObject<{
16
16
  revision: z.ZodString;
17
17
  }, z.core.$strict>;
18
18
  export type ClientErrorReport = z.infer<typeof ClientErrorReport>;
19
+ export declare const ClientSignalReport: z.ZodDiscriminatedUnion<[z.ZodObject<{
20
+ signal: z.ZodLiteral<"request_failure">;
21
+ action: z.ZodEnum<{
22
+ checkout_start: "checkout_start";
23
+ composer_submit: "composer_submit";
24
+ connect_integration: "connect_integration";
25
+ connect_model: "connect_model";
26
+ create_session: "create_session";
27
+ retry_turn: "retry_turn";
28
+ send_message: "send_message";
29
+ steer_message: "steer_message";
30
+ }>;
31
+ reason: z.ZodEnum<{
32
+ network: "network";
33
+ offline: "offline";
34
+ timeout: "timeout";
35
+ }>;
36
+ route: z.ZodString;
37
+ revision: z.ZodString;
38
+ }, z.core.$strict>, z.ZodObject<{
39
+ signal: z.ZodLiteral<"stream">;
40
+ stream: z.ZodEnum<{
41
+ session: "session";
42
+ workspace: "workspace";
43
+ }>;
44
+ event: z.ZodEnum<{
45
+ long_disconnect: "long_disconnect";
46
+ reconnect: "reconnect";
47
+ reconnect_exhausted: "reconnect_exhausted";
48
+ }>;
49
+ route: z.ZodString;
50
+ revision: z.ZodString;
51
+ }, z.core.$strict>, z.ZodObject<{
52
+ signal: z.ZodLiteral<"web_vital">;
53
+ metric: z.ZodEnum<{
54
+ cls: "cls";
55
+ inp: "inp";
56
+ lcp: "lcp";
57
+ ttfb: "ttfb";
58
+ }>;
59
+ page: z.ZodEnum<{
60
+ "account-auth": "account-auth";
61
+ agents: "agents";
62
+ artifacts: "artifacts";
63
+ capabilities: "capabilities";
64
+ "checkout-return": "checkout-return";
65
+ device: "device";
66
+ documents: "documents";
67
+ environments: "environments";
68
+ files: "files";
69
+ home: "home";
70
+ "identity-link": "identity-link";
71
+ insights: "insights";
72
+ "integration-return": "integration-return";
73
+ machines: "machines";
74
+ memory: "memory";
75
+ organization: "organization";
76
+ other: "other";
77
+ "personal-security": "personal-security";
78
+ plugins: "plugins";
79
+ priority: "priority";
80
+ "reset-password": "reset-password";
81
+ rigs: "rigs";
82
+ schedules: "schedules";
83
+ "session-link": "session-link";
84
+ sessions: "sessions";
85
+ settings: "settings";
86
+ "setup-account": "setup-account";
87
+ state: "state";
88
+ "variable-sets": "variable-sets";
89
+ }>;
90
+ value: z.ZodNumber;
91
+ revision: z.ZodString;
92
+ }, z.core.$strict>], "signal">;
93
+ export type ClientSignalReport = z.infer<typeof ClientSignalReport>;
94
+ export type ClientBeaconReport = ClientErrorReport | ClientSignalReport;
95
+ /**
96
+ * Bucket bounds cover the published good/poor thresholds of every metric:
97
+ * CLS 0.1/0.25, INP 0.2/0.5 s, TTFB 0.8/1.8 s, LCP 2.5/4 s.
98
+ */
99
+ export declare const CLIENT_WEB_VITAL_BUCKETS: number[];
19
100
  export type ClientErrorAdmission = KeyedAdmission<ClientErrorKind>;
101
+ /** Keyed by `<signal>:<closed dimension>`, a finite set of buckets. */
102
+ export type ClientSignalAdmission = KeyedAdmission<string>;
103
+ /** Same bounds as the error kinds: one bucket per closed signal key. */
104
+ export declare function createClientSignalAdmission(options?: {
105
+ capacity?: number;
106
+ refillPerSecond?: number;
107
+ now?: () => number;
108
+ }): ClientSignalAdmission;
109
+ /**
110
+ * Web vitals arrive from every page load rather than only on failure, so they
111
+ * get a much larger dedicated bucket per metric (burst 1,200, then 20 a
112
+ * second per process). The browser samples vitals before sending.
113
+ */
114
+ export declare function createClientWebVitalAdmission(options?: {
115
+ capacity?: number;
116
+ refillPerSecond?: number;
117
+ now?: () => number;
118
+ }): ClientSignalAdmission;
20
119
  /**
21
120
  * One token bucket per closed kind bounds counter inflation and log volume
22
121
  * from any caller, including an anonymous one, without per-client state.
@@ -27,6 +126,9 @@ export declare function createClientErrorAdmission(options?: {
27
126
  now?: () => number;
28
127
  }): ClientErrorAdmission;
29
128
  export declare function parseClientErrorReport(body: string | null): ClientErrorReport | null;
129
+ /** Parse any accepted beacon body: an error report or a closed signal report. */
130
+ export declare function parseClientBeaconReport(body: string | null): ClientBeaconReport | null;
131
+ export declare function isClientSignalReport(report: ClientBeaconReport): report is ClientSignalReport;
30
132
  /**
31
133
  * The exact beacon request. The app-wide request-body limit skips it because
32
134
  * the route enforces its own 512-byte limit on the streamed body; the generic
@@ -38,4 +140,6 @@ export declare function registerClientErrorRoutes(app: Hono, deps: {
38
140
  observability: Observability;
39
141
  settings: Pick<Settings, "corsAllowOriginRegex" | "publicBaseUrl" | "webBaseUrl">;
40
142
  admission?: ClientErrorAdmission;
143
+ signalAdmission?: ClientSignalAdmission;
144
+ webVitalAdmission?: ClientSignalAdmission;
41
145
  }): void;
@@ -25,6 +25,8 @@ export declare function workspaceInsightsCoalesceKey(input: {
25
25
  range: string;
26
26
  provider: string | null;
27
27
  model: string | null;
28
+ rootSessionId?: string | null;
29
+ sessionId?: string | null;
28
30
  rlsActor: string | null;
29
31
  }): string;
30
32
  export declare function registerInsightsRoutes(app: Hono, deps: ApiRouteDeps): void;
@@ -20,6 +20,7 @@ export declare function sessionListQuery(query: Record<string, string>, allowCur
20
20
  cursor: ReturnType<typeof decodeSessionListCursor> | undefined;
21
21
  search: string | undefined;
22
22
  pinsOnly: boolean;
23
+ includePinned: boolean;
23
24
  archivedOnly: boolean;
24
25
  sortBy: "updatedAt" | "createdAt" | "name" | undefined;
25
26
  archiveStatus: "active" | "archived" | "all" | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/api-router",
3
- "version": "5.4.1",
3
+ "version": "5.5.0-canary.37090756480001",
4
4
  "description": "OpenGeni HTTP surface: the Hono adapter/router (createApp), routes, MCP HTTP transport, and HTTP access adapters over @opengeni/core. An engine-distribution surface — its runtime closure includes engine-internal packages.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -43,25 +43,25 @@
43
43
  "@hono/zod-validator": "^0.7.6",
44
44
  "@llamaindex/liteparse": "2.14.2",
45
45
  "@modelcontextprotocol/sdk": "^1.29.0",
46
- "@opengeni/agent-proto": "^0.6.1",
47
- "@opengeni/artifact-tool": "^0.3.37",
48
- "@opengeni/capabilities": "^0.3.5",
49
- "@opengeni/codemode": "^0.6.8",
50
- "@opengeni/codex": "^0.2.31",
51
- "@opengeni/config": "^3.2.1",
52
- "@opengeni/contracts": "^5.6.0",
53
- "@opengeni/core": "^5.1.1",
54
- "@opengeni/db": "^6.5.0",
55
- "@opengeni/documents": "^0.8.40",
56
- "@opengeni/events": "^0.4.38",
57
- "@opengeni/github": "^0.8.2",
58
- "@opengeni/interaction": "^0.4.45",
59
- "@opengeni/network": "^0.3.1",
60
- "@opengeni/observability": "^0.8.37",
61
- "@opengeni/runtime": "^4.6.0",
62
- "@opengeni/storage": "^0.2.138",
63
- "@opengeni/tool-gateway": "^0.1.18",
64
- "@opengeni/xai-subscription": "^0.1.5",
46
+ "@opengeni/agent-proto": "^0.6.1-canary.37090756480001",
47
+ "@opengeni/artifact-tool": "^0.3.38-canary.37090756480001",
48
+ "@opengeni/capabilities": "^0.3.6-canary.37090756480001",
49
+ "@opengeni/codemode": "^0.7.0-canary.37090756480001",
50
+ "@opengeni/codex": "^0.2.32-canary.37090756480001",
51
+ "@opengeni/config": "^3.3.0-canary.37090756480001",
52
+ "@opengeni/contracts": "^5.7.0-canary.37090756480001",
53
+ "@opengeni/core": "^5.2.0-canary.37090756480001",
54
+ "@opengeni/db": "^6.6.0-canary.37090756480001",
55
+ "@opengeni/documents": "^0.8.41-canary.37090756480001",
56
+ "@opengeni/events": "^0.4.39-canary.37090756480001",
57
+ "@opengeni/github": "^0.8.3-canary.37090756480001",
58
+ "@opengeni/interaction": "^0.4.46-canary.37090756480001",
59
+ "@opengeni/network": "^0.3.1-canary.37090756480001",
60
+ "@opengeni/observability": "^0.8.38-canary.37090756480001",
61
+ "@opengeni/runtime": "^4.7.0-canary.37090756480001",
62
+ "@opengeni/storage": "^0.2.139-canary.37090756480001",
63
+ "@opengeni/tool-gateway": "^0.1.19-canary.37090756480001",
64
+ "@opengeni/xai-subscription": "^0.1.5-canary.37090756480001",
65
65
  "@temporalio/client": "^1.17.0",
66
66
  "better-auth": "^1.6.14",
67
67
  "hono": "^4.12.18",
package/src/app.ts CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  configuredAllowedReasoningEfforts,
24
24
  resolveFirstPartyMcpToolPolicy,
25
25
  resolveVoiceInputProviderRegistry,
26
+ UnsupportedLatencyModeError,
26
27
  type Settings,
27
28
  } from "@opengeni/config";
28
29
  import {
@@ -87,6 +88,7 @@ import {
87
88
  ApiHttpError,
88
89
  agentConfigHttpError,
89
90
  allowanceExhaustedHttpError,
91
+ modelUnavailableHttpError,
90
92
  workspaceControlBusyHttpError,
91
93
  } from "./http/api-error";
92
94
  import {
@@ -1257,6 +1259,8 @@ export function createAppComposition(deps: AppDependencies): {
1257
1259
  auth: clientAuthConfig(deps.settings),
1258
1260
  documentationUrl: deps.settings.documentationUrl,
1259
1261
  analytics: clientAnalyticsConfig(deps.settings),
1262
+ legal: clientLegalConfig(deps.settings),
1263
+ ...(deps.settings.supportEmail ? { supportEmail: deps.settings.supportEmail } : {}),
1260
1264
  // Channel-A structured services (P4.4) ride exec/readFile/createEditor,
1261
1265
  // available on every real backend; `none` has no box so they are all off.
1262
1266
  // Per-session availability is still negotiated on /stream-capabilities.
@@ -1682,6 +1686,21 @@ export function createAppComposition(deps: AppDependencies): {
1682
1686
  : (allowanceExhaustedHttpError(rawError) ??
1683
1687
  workspaceControlBusyHttpError(rawError) ??
1684
1688
  agentConfigHttpError(rawError) ??
1689
+ modelUnavailableHttpError(rawError) ??
1690
+ (rawError instanceof UnsupportedLatencyModeError
1691
+ ? new ApiHttpError(422, {
1692
+ code: "validation_failed",
1693
+ message: rawError.message,
1694
+ retryable: false,
1695
+ outcomeUnknown: false,
1696
+ details: {
1697
+ code: rawError.code,
1698
+ modelId: rawError.modelId,
1699
+ latencyMode: rawError.latencyMode,
1700
+ allowedLatencyModes: [...rawError.allowedLatencyModes],
1701
+ },
1702
+ })
1703
+ : null) ??
1685
1704
  requestBodyValidationHttpError(rawError) ??
1686
1705
  invalidPathIdentifierHttpError(rawError, new URL(c.req.url).pathname) ??
1687
1706
  rawError);
@@ -2088,6 +2107,15 @@ function clientAnalyticsConfig(settings: AppDependencies["settings"]) {
2088
2107
  };
2089
2108
  }
2090
2109
 
2110
+ function clientLegalConfig(settings: AppDependencies["settings"]) {
2111
+ return {
2112
+ ...(settings.legalPrivacyPolicyUrl ? { privacyPolicyUrl: settings.legalPrivacyPolicyUrl } : {}),
2113
+ ...(settings.legalTermsOfServiceUrl
2114
+ ? { termsOfServiceUrl: settings.legalTermsOfServiceUrl }
2115
+ : {}),
2116
+ };
2117
+ }
2118
+
2091
2119
  function structuredServicesHint(backend: string): {
2092
2120
  fileSystem: boolean;
2093
2121
  git: boolean;
@@ -1,4 +1,9 @@
1
- import { AgentConfigError, AllowanceExhaustedRefusal, type ErrorCode } from "@opengeni/contracts";
1
+ import {
2
+ AgentConfigError,
3
+ AllowanceExhaustedRefusal,
4
+ ModelUnavailableError,
5
+ type ErrorCode,
6
+ } from "@opengeni/contracts";
2
7
  import { WorkspaceControlBusyError } from "@opengeni/db";
3
8
  import type { ContentfulStatusCode } from "hono/utils/http-status";
4
9
  import { HTTPException } from "hono/http-exception";
@@ -94,3 +99,26 @@ export function agentConfigHttpError(error: unknown): ApiHttpError | null {
94
99
  },
95
100
  });
96
101
  }
102
+
103
+ /**
104
+ * The requested or stored model is not in the live catalog. Rendered as the
105
+ * same 422 `validation_failed` with its historical message, plus
106
+ * `details.code: "model_unavailable"` and the model id so clients can ask for
107
+ * another model rather than offer a retry that cannot succeed.
108
+ */
109
+ export function modelUnavailableHttpError(error: unknown): ApiHttpError | null {
110
+ const cause =
111
+ error instanceof ModelUnavailableError
112
+ ? error
113
+ : error instanceof HTTPException && error.cause instanceof ModelUnavailableError
114
+ ? error.cause
115
+ : null;
116
+ if (!cause) return null;
117
+ return new ApiHttpError(422, {
118
+ code: "validation_failed",
119
+ message: cause.message,
120
+ retryable: false,
121
+ outcomeUnknown: false,
122
+ details: { code: cause.code, modelId: cause.modelId },
123
+ });
124
+ }
package/src/http/sse.ts CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  } from "@opengeni/contracts";
6
6
  import {
7
7
  getWorkspaceInteractionRevisionState,
8
- listSessionEvents,
8
+ listSessionEventPage,
9
9
  listWorkspaceControlEvents,
10
10
  type Database,
11
11
  } from "@opengeni/db";
@@ -21,6 +21,7 @@ import type { Observability } from "@opengeni/observability";
21
21
  import { MANAGED_AUTH_ACTOR_EPOCH_HEADER } from "@opengeni/core/managed-auth-session-sets";
22
22
 
23
23
  const SESSION_REPLAY_PAGE_SIZE = 100;
24
+ const SESSION_REPLAY_PAGE_MAX_BYTES = 8 * 1024 * 1024;
24
25
  const WORKSPACE_CONTROL_REPLAY_PAGE_SIZE = 100;
25
26
  export const SSE_QUEUED_FRAME_MAX_COUNT = 1;
26
27
  export const SSE_WRITE_STALL_TIMEOUT_MS = 30_000;
@@ -277,9 +278,10 @@ export async function sseSessionStream(
277
278
  options: SessionSseDeliveryOptions = {},
278
279
  ): Promise<Response> {
279
280
  if (isHttp1BrowserBatch(options)) {
280
- const events = await listSessionEvents(db, workspaceId, sessionId, {
281
+ const { events } = await listSessionEventPage(db, workspaceId, sessionId, {
281
282
  after,
282
283
  limit: SESSION_REPLAY_PAGE_SIZE,
284
+ maxBytes: options.finiteResponseMaxBytes ?? HTTP1_BROWSER_SSE_BATCH_MAX_BYTES,
283
285
  });
284
286
  await options.reauthorize?.();
285
287
  const compactProjection = coalesceSessionEventDeltasWithCoverage(events);
@@ -353,10 +355,12 @@ export async function sseSessionStream(
353
355
  targetSequence === undefined
354
356
  ? SESSION_REPLAY_PAGE_SIZE
355
357
  : Math.min(SESSION_REPLAY_PAGE_SIZE, targetSequence - lastSent);
356
- const page = await listSessionEvents(db, workspaceId, sessionId, {
358
+ const replayPage = await listSessionEventPage(db, workspaceId, sessionId, {
357
359
  after: lastSent,
358
360
  limit,
361
+ maxBytes: SESSION_REPLAY_PAGE_MAX_BYTES,
359
362
  });
363
+ const page = replayPage.events;
360
364
  const eligible =
361
365
  targetSequence === undefined
362
366
  ? page
@@ -382,8 +386,10 @@ export async function sseSessionStream(
382
386
  throw new Error(`Session event replay made no progress after sequence ${lastSent}`);
383
387
  }
384
388
  if (targetSequence !== undefined && lastSent >= targetSequence) return;
389
+ if (targetSequence === undefined && !replayPage.hasMore) return;
385
390
  // A byte-selected page may contain fewer rows than requested, especially
386
- // when one large message travels alone. Only an empty read proves EOF.
391
+ // when one large message travels alone. Use explicit continuation rather
392
+ // than page length; live publication covers appends after the snapshot.
387
393
  }
388
394
  };
389
395
  let durableDeliveryTail = Promise.resolve();
@@ -123,6 +123,11 @@ export const FIRST_PARTY_TOOL_AUTHORIZATION = {
123
123
  browser_act: { sessionRequired: true, allOf: ["sessions:control"] },
124
124
  browser_clipboard: { sessionRequired: true, allOf: ["sessions:read"] },
125
125
  browser_debug: { sessionRequired: true, allOf: ["sessions:read"] },
126
+ browser_downloads: { sessionRequired: true, allOf: ["sessions:read"] },
127
+ browser_download_save: {
128
+ sessionRequired: true,
129
+ allOf: ["sessions:control", "files:upload"],
130
+ },
126
131
  browser_auth: { sessionRequired: true, allOf: ["sessions:control"] },
127
132
  interaction_request_human: {
128
133
  sessionRequired: true,
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  AgentInstructionSaveRequest,
3
+ WORKSPACE_INSTRUCTION_POLICY_CONTENT_MAX_CHARS,
3
4
  WorkspaceInstructionPolicyTarget,
4
5
  KnowledgeEntryListRequest,
5
6
  KnowledgeSavePreparationRequest,
@@ -92,7 +93,7 @@ export function registerKnowledgeEntryTools(
92
93
  : state === "42501"
93
94
  ? "The instruction change is unavailable in this task's scope or Agent learning policy. Off disables authoring, not the task. Do not ask for approval to continue."
94
95
  : state === "22023" || state === "23514"
95
- ? "Invalid instruction change. Read the current instruction, use append for a new rule, or edit with one localized exact oldText match to update or remove text. Agents cannot replace the complete instruction. The resulting instruction must stay within 600 characters."
96
+ ? `Invalid instruction change. Read the current instruction, use append for a new rule, or edit with one localized exact oldText match to update or remove text. Agents cannot replace the complete instruction. Each supplied text and the resulting instruction must stay within ${WORKSPACE_INSTRUCTION_POLICY_CONTENT_MAX_CHARS} characters, the same limit as the human editor.`
96
97
  : "Workspace instructions are temporarily unavailable. Retry the same operation ID only if the prior change may have succeeded."
97
98
  : state === "40001"
98
99
  ? "This entry changed. Read its current revision and retry your correction with the current version."
@@ -304,8 +305,7 @@ export function registerKnowledgeEntryTools(
304
305
  server.registerTool(
305
306
  "instruction_policy_save",
306
307
  {
307
- description:
308
- "Change a concise standing workspace instruction through this task's Agent learning policy. Use for short always-on behavior such as 'Keep replies concise; expand when asked', within the intended workspace scope; active instructions enter applicable prompts without Knowledge retrieval. Read the current policy first and submit its exact baseline. Use editMode=append by default for a new rule; it preserves the current content and adds one blank-line separator. Use edit with a localized oldText that occurs exactly once and newText to update or remove only that passage. Agents cannot replace the complete instruction; direct the user to the manual workspace-instruction editor for a whole-policy rewrite. Each supplied text and the resulting instruction are limited to 600 characters. Facts and incidents belong in knowledge_save and reusable procedures or context-specific preferences in skill_save. Preserve personal scope rather than creating a workspace-wide rule. Review first saves an inactive revision and returns pending; continue the task without an approval question. Report the actual receipt; Knowledge is not a workaround for Off, pending review, unavailable scope or size limits.",
308
+ description: `Change a concise standing workspace instruction through this task's Agent learning policy. Use for short always-on behavior such as 'Keep replies concise; expand when asked', within the intended workspace scope; active instructions enter applicable prompts without Knowledge retrieval. Read the current policy first and submit its exact baseline. Use editMode=append by default for a new rule; it preserves the current content and adds one blank-line separator. Use edit with a localized oldText that occurs exactly once and newText to update or remove only that passage. Agents cannot replace the complete instruction; direct the user to the manual workspace-instruction editor for a whole-policy rewrite. Each supplied text and the resulting instruction are limited to ${WORKSPACE_INSTRUCTION_POLICY_CONTENT_MAX_CHARS} characters, the same limit as the human editor. Facts and incidents belong in knowledge_save and reusable procedures or context-specific preferences in skill_save. Preserve personal scope rather than creating a workspace-wide rule. Review first saves an inactive revision and returns pending; continue the task without an approval question. Report the actual receipt; Knowledge is not a workaround for Off, pending review, unavailable scope or size limits.`,
309
309
  inputSchema: AgentInstructionSaveRequest.shape,
310
310
  },
311
311
  (input) => run((context) => saveAgentInstruction(deps.db, context, input), "instruction"),
package/src/mcp/server.ts CHANGED
@@ -5314,7 +5314,9 @@ function registerWorkspaceOrchestrationTools(
5314
5314
  latencyMode: z4
5315
5315
  .enum(["standard", "priority", "fast"])
5316
5316
  .optional()
5317
- .describe("Omit to inherit the exact calling turn's latency mode."),
5317
+ .describe(
5318
+ "Omit for standard latency, even when model and reasoning are inherited. Fast or priority must be selected explicitly and supported by the model.",
5319
+ ),
5318
5320
  sandboxBackend: z4.string().optional(),
5319
5321
  // Model-only structural coupling: workingDir cannot exist without a
5320
5322
  // targetSandboxId because both live inside one optional object. The
@@ -25,12 +25,16 @@ export {
25
25
  export function projectClientModel(model: ConfiguredModel): ClientModel {
26
26
  const anonymousProvider =
27
27
  model.credentialSource.kind === "deployment" && model.credentialSource.mechanism === "none";
28
+ const directProvider =
29
+ model.providerId.startsWith("workspace-openai-") ||
30
+ model.providerId.startsWith("workspace-azure-openai-");
28
31
  const organizationProvider = model.credentialSource.kind === "organization_connection";
29
32
  // Keep the established closed `source` enum compatible for older same-major
30
33
  // clients. OpenRouter remains truthfully identified by its public provider
31
34
  // id/label and billing metadata; omitting this optional legacy grouping field
32
35
  // lets tolerant older contracts parse the additive provider.
33
36
  const source =
37
+ directProvider ||
34
38
  organizationProvider ||
35
39
  model.providerId === "workspace-anthropic" ||
36
40
  model.providerId === "workspace-claude-subscription" ||
@@ -47,6 +51,7 @@ export function projectClientModel(model: ConfiguredModel): ClientModel {
47
51
  ? undefined
48
52
  : "opengeni";
49
53
  const publicProvider =
54
+ directProvider ||
50
55
  anonymousProvider ||
51
56
  organizationProvider ||
52
57
  model.providerId === "workspace-anthropic" ||
@@ -27,6 +27,7 @@ import {
27
27
  } from "@opengeni/core";
28
28
  import { withOrganizationIntegrationPolicyFence } from "@opengeni/db/organization-integration-policy";
29
29
  import {
30
+ ApiIntegrationConnectionReferenceError,
30
31
  ApiIntegrationInstallationVersionConflictError,
31
32
  buildConnectionTokenResolver,
32
33
  getApiIntegrationUninstallPreview,
@@ -332,6 +333,11 @@ export function registerApiIntegrationRoutes(
332
333
  payload.expectedInstanceVersion === undefined ? 201 : 200,
333
334
  );
334
335
  } catch (error) {
336
+ if (error instanceof ApiIntegrationConnectionReferenceError) {
337
+ throw new HTTPException(error.reason === "not_found" ? 404 : 422, {
338
+ message: error.reason === "not_found" ? "connection not found" : error.message,
339
+ });
340
+ }
335
341
  if (
336
342
  error instanceof IntegrationFacetBindingVersionConflictError ||
337
343
  error instanceof IntegrationFacetBindingVersionRequiredError ||