@opengeni/api-router 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +1 -1
- package/dist/{chunk-E7TZFJ65.js → chunk-24C5LPNE.js} +808 -577
- package/dist/{chunk-E7TZFJ65.js.map → chunk-24C5LPNE.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/integrations/github-lens-connect.d.ts +1 -1
- package/dist/integrations/oauth-client.d.ts +1 -1
- package/dist/integrations/slack-bot.d.ts +8 -0
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/child-read-evidence.d.ts +9 -0
- package/dist/mcp/first-party-tool-permissions.d.ts +3 -0
- package/dist/mcp/session-wait.d.ts +2 -0
- package/dist/routes/codex.d.ts +2 -1
- package/package.json +15 -15
- package/src/integrations/oauth-client.ts +68 -5
- package/src/integrations/slack-bot.ts +32 -3
- package/src/integrations/slack-interactions.ts +79 -8
- package/src/mcp/child-read-evidence.ts +19 -0
- package/src/mcp/first-party-tool-permissions.ts +1 -0
- package/src/mcp/server.ts +175 -101
- package/src/mcp/session-wait.ts +17 -0
- package/src/routes/codex.ts +19 -12
- package/src/routes/skill-content.ts +21 -5
- package/src/sandbox/machines.ts +9 -8
- package/src/sandbox-file-artifacts.ts +15 -1
- package/src/site-uploads.ts +19 -4
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
createOpenGeniSlackBotInteractionClient,
|
|
8
8
|
startSlackInteractionPump,
|
|
9
9
|
startTemporalScheduleCleanupPump
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-24C5LPNE.js";
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
13
13
|
import { scheduledTaskKnowledgeSource } from "@opengeni/contracts";
|
|
@@ -46,7 +46,7 @@ export declare function requireGitHubLensConnect(deps: ApiRouteDeps): Promise<{
|
|
|
46
46
|
eager?: boolean | undefined;
|
|
47
47
|
optional?: boolean | undefined;
|
|
48
48
|
}[];
|
|
49
|
-
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_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_remove" | "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_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_search" | "slack_bot_thread_replies" | "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")[];
|
|
49
|
+
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_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" | "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_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_search" | "slack_bot_thread_replies" | "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
50
|
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" | "variable-sets:attach" | "variable-sets:list" | "variable-sets:manage" | "variable-sets:read" | "variable-sets:use" | "variable-sets:write" | "workspace:admin" | "workspace:create" | "workspace:read")[];
|
|
51
51
|
model: string | null;
|
|
52
52
|
reasoningEffort: "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh" | null;
|
|
@@ -39,7 +39,7 @@ export type OAuthCallbackResult = {
|
|
|
39
39
|
type AuthorizationServerMetadata = McpAuthorizationServerMetadata;
|
|
40
40
|
export declare const OAUTH_START_DEADLINE_MS = 15000;
|
|
41
41
|
export declare const OAUTH_CALLBACK_DEADLINE_MS = 30000;
|
|
42
|
-
export type OAuthStartStage = "connection_lookup" | "mcp_challenge" | "protected_resource_metadata" | "authorization_server_metadata" | "client_registration";
|
|
42
|
+
export type OAuthStartStage = "connection_lookup" | "mcp_challenge" | "protected_resource_metadata" | "authorization_server_metadata" | "client_registration" | "state_persist";
|
|
43
43
|
/** Policy is the outermost lock. Receipt replay still runs normal authorization,
|
|
44
44
|
* but the DB calls authorizeAcquisition only for uncommitted progress. Provider
|
|
45
45
|
* requests must happen after this transaction, never inside either wrapper. */
|
|
@@ -813,4 +813,12 @@ export declare function createSlackFilesListCursor(input: {
|
|
|
813
813
|
page: number;
|
|
814
814
|
}, context: SlackFilesCursorContext): string;
|
|
815
815
|
export declare function nextSlackFilesListPage(payload: SlackPayload, requested: SlackFilesListPage, returnedFileCount: number): number | null;
|
|
816
|
+
/**
|
|
817
|
+
* Slack action IDs are unique within a block. Keep the logical card untouched
|
|
818
|
+
* for the durable request digest, and partition repeated actions only on the
|
|
819
|
+
* provider wire. In particular, a legacy invalid_blocks receipt can retry with
|
|
820
|
+
* its original operation ID and digest; completed/uncertain posts keep their
|
|
821
|
+
* existing replay/reconciliation path. Values and action IDs remain exact.
|
|
822
|
+
*/
|
|
823
|
+
export declare function slackMessageWireBlocks(blocks: SlackMessageBlock[] | undefined): SlackMessageBlock[] | undefined;
|
|
816
824
|
export {};
|
|
@@ -19,7 +19,7 @@ export declare const SLACK_SESSION_INSTRUCTIONS: string;
|
|
|
19
19
|
* selection at session creation while keeping Slack mutations out of the model
|
|
20
20
|
* surface; interaction delivery remains owned by the durable delivery pump.
|
|
21
21
|
*/
|
|
22
|
-
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_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_remove" | "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_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_search" | "slack_bot_thread_replies" | "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")[];
|
|
22
|
+
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_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" | "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_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_search" | "slack_bot_thread_replies" | "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")[];
|
|
23
23
|
export type NormalizedSlackInteraction = {
|
|
24
24
|
providerEventId: string;
|
|
25
25
|
providerMessageId: string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Select only whole items that actually survived the model-facing envelope.
|
|
2
|
+
* A cursor is a position, never proof that preceding content was consumed. */
|
|
3
|
+
export declare function completeChildReadSequences(page: {
|
|
4
|
+
view: string;
|
|
5
|
+
sourceExact: boolean;
|
|
6
|
+
events: readonly ({
|
|
7
|
+
sequence: number;
|
|
8
|
+
} & Record<string, unknown>)[];
|
|
9
|
+
}): number[];
|
|
@@ -193,6 +193,9 @@ export declare const FIRST_PARTY_TOOL_AUTHORIZATION: {
|
|
|
193
193
|
connected_machine_remove: {
|
|
194
194
|
allOf: "enrollments:manage"[];
|
|
195
195
|
};
|
|
196
|
+
connected_machine_enroll_token: {
|
|
197
|
+
allOf: "enrollments:manage"[];
|
|
198
|
+
};
|
|
196
199
|
rig_list: {
|
|
197
200
|
allOf: "rigs:use"[];
|
|
198
201
|
};
|
|
@@ -69,6 +69,8 @@ export type SessionWaitTarget = {
|
|
|
69
69
|
afterSequence: number;
|
|
70
70
|
};
|
|
71
71
|
export type SessionWaitEventSummary = {
|
|
72
|
+
/** True only when every semantic result field survived all projections. */
|
|
73
|
+
contentComplete?: boolean;
|
|
72
74
|
id: string;
|
|
73
75
|
sequence: number;
|
|
74
76
|
type: SessionEventType;
|
package/dist/routes/codex.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Settings } from "@opengeni/config";
|
|
1
2
|
import { type CodexAccountStatus } from "@opengeni/db";
|
|
2
3
|
export declare function codexAccountJson(row: CodexAccountStatus, options?: {
|
|
3
4
|
appsCredentialId?: string | null;
|
|
@@ -44,7 +45,7 @@ export declare function codexWorkerReadiness(input: {
|
|
|
44
45
|
poolReady: boolean;
|
|
45
46
|
workerRoutable: boolean;
|
|
46
47
|
};
|
|
47
|
-
export declare function codexModelsForPicker(): Array<{
|
|
48
|
+
export declare function codexModelsForPicker(settings?: Settings): Array<{
|
|
48
49
|
id: string;
|
|
49
50
|
label: string;
|
|
50
51
|
provider: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/api-router",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
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": {
|
|
@@ -44,22 +44,22 @@
|
|
|
44
44
|
"@llamaindex/liteparse": "2.14.2",
|
|
45
45
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
46
46
|
"@opengeni/agent-proto": "^0.6.0",
|
|
47
|
-
"@opengeni/artifact-tool": "^0.3.
|
|
47
|
+
"@opengeni/artifact-tool": "^0.3.31",
|
|
48
48
|
"@opengeni/capabilities": "^0.3.4",
|
|
49
|
-
"@opengeni/codemode": "^0.6.
|
|
50
|
-
"@opengeni/codex": "^0.2.
|
|
51
|
-
"@opengeni/config": "^2.
|
|
52
|
-
"@opengeni/contracts": "^5.
|
|
53
|
-
"@opengeni/core": "^4.0.
|
|
54
|
-
"@opengeni/db": "^6.0.
|
|
55
|
-
"@opengeni/documents": "^0.8.
|
|
56
|
-
"@opengeni/events": "^0.4.
|
|
57
|
-
"@opengeni/github": "^0.7.
|
|
49
|
+
"@opengeni/codemode": "^0.6.1",
|
|
50
|
+
"@opengeni/codex": "^0.2.25",
|
|
51
|
+
"@opengeni/config": "^2.1.0",
|
|
52
|
+
"@opengeni/contracts": "^5.1.0",
|
|
53
|
+
"@opengeni/core": "^4.0.1",
|
|
54
|
+
"@opengeni/db": "^6.0.1",
|
|
55
|
+
"@opengeni/documents": "^0.8.32",
|
|
56
|
+
"@opengeni/events": "^0.4.30",
|
|
57
|
+
"@opengeni/github": "^0.7.15",
|
|
58
58
|
"@opengeni/network": "^0.3.1",
|
|
59
|
-
"@opengeni/observability": "^0.8.
|
|
60
|
-
"@opengeni/runtime": "^4.0.
|
|
61
|
-
"@opengeni/storage": "^0.2.
|
|
62
|
-
"@opengeni/tool-gateway": "^0.1.
|
|
59
|
+
"@opengeni/observability": "^0.8.31",
|
|
60
|
+
"@opengeni/runtime": "^4.0.1",
|
|
61
|
+
"@opengeni/storage": "^0.2.132",
|
|
62
|
+
"@opengeni/tool-gateway": "^0.1.12",
|
|
63
63
|
"@opengeni/xai-subscription": "^0.1.4",
|
|
64
64
|
"@temporalio/client": "^1.17.0",
|
|
65
65
|
"better-auth": "^1.6.14",
|
|
@@ -29,9 +29,11 @@ import {
|
|
|
29
29
|
getGlobalCatalogOAuthProfile,
|
|
30
30
|
listConnectionsMetadata,
|
|
31
31
|
loadIntegrationOAuthClient,
|
|
32
|
+
loadIntegrationOAuthPendingState,
|
|
32
33
|
normalizeBearerScheme,
|
|
33
34
|
replaceIntegrationOAuthClientIfCurrent,
|
|
34
35
|
storeIntegrationOAuthClient,
|
|
36
|
+
storeIntegrationOAuthPendingState,
|
|
35
37
|
updateConnection,
|
|
36
38
|
withDatabaseStatementTimeout,
|
|
37
39
|
type Database,
|
|
@@ -55,7 +57,7 @@ import {
|
|
|
55
57
|
type McpProtectedResourceMetadata,
|
|
56
58
|
} from "@opengeni/network";
|
|
57
59
|
import { Buffer } from "node:buffer";
|
|
58
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
60
|
+
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
59
61
|
import { HTTPException } from "hono/http-exception";
|
|
60
62
|
import {
|
|
61
63
|
assertConnectionOwnershipAllowedForPrincipal,
|
|
@@ -196,7 +198,8 @@ export type OAuthStartStage =
|
|
|
196
198
|
| "mcp_challenge"
|
|
197
199
|
| "protected_resource_metadata"
|
|
198
200
|
| "authorization_server_metadata"
|
|
199
|
-
| "client_registration"
|
|
201
|
+
| "client_registration"
|
|
202
|
+
| "state_persist";
|
|
200
203
|
|
|
201
204
|
class OAuthStartStageError extends Error {
|
|
202
205
|
constructor(
|
|
@@ -524,7 +527,7 @@ async function startMcpOAuthWithinDeadline(
|
|
|
524
527
|
),
|
|
525
528
|
);
|
|
526
529
|
const key = requireEnvironmentEncryption(settings);
|
|
527
|
-
const
|
|
530
|
+
const fullState = createSignedState(requireIntegrationsStateSecret(settings), {
|
|
528
531
|
...(context.integrationKey ? { integrationKey: context.integrationKey } : {}),
|
|
529
532
|
...(context.connectAttemptId ? { connectAttemptId: context.connectAttemptId } : {}),
|
|
530
533
|
accountId: context.accountId,
|
|
@@ -574,6 +577,29 @@ async function startMcpOAuthWithinDeadline(
|
|
|
574
577
|
returnPath,
|
|
575
578
|
...(existing ? { connectionId: existing.id, connectionVersion: existing.version } : {}),
|
|
576
579
|
});
|
|
580
|
+
const expiresAt = new Date(Date.now() + oauthStateTtlMs);
|
|
581
|
+
let state = fullState;
|
|
582
|
+
if (settings.integrationsOauthShortStateEnabled) {
|
|
583
|
+
const stateId = randomUUID();
|
|
584
|
+
state = createSignedState(requireIntegrationsStateSecret(settings), {
|
|
585
|
+
kind: "mcp_oauth_reference",
|
|
586
|
+
id: stateId,
|
|
587
|
+
accountId: context.accountId,
|
|
588
|
+
workspaceId: context.workspaceId,
|
|
589
|
+
});
|
|
590
|
+
if (state.length > 512) {
|
|
591
|
+
throw new Error("MCP OAuth reference state exceeded its provider-safe length");
|
|
592
|
+
}
|
|
593
|
+
await deadline.run("state_persist", async () =>
|
|
594
|
+
storeIntegrationOAuthPendingState(db, {
|
|
595
|
+
id: stateId,
|
|
596
|
+
accountId: context.accountId,
|
|
597
|
+
workspaceId: context.workspaceId,
|
|
598
|
+
stateEncrypted: encryptEnvironmentValue(key, fullState),
|
|
599
|
+
expiresAt,
|
|
600
|
+
}),
|
|
601
|
+
);
|
|
602
|
+
}
|
|
577
603
|
const authorizationUrl = buildAuthorizationUrl({
|
|
578
604
|
endpoint: discovery.as.authorizationEndpoint,
|
|
579
605
|
settings,
|
|
@@ -589,7 +615,7 @@ async function startMcpOAuthWithinDeadline(
|
|
|
589
615
|
return OAuthStartResponse.parse({
|
|
590
616
|
state,
|
|
591
617
|
authorizationUrl,
|
|
592
|
-
expiresAt:
|
|
618
|
+
expiresAt: expiresAt.toISOString(),
|
|
593
619
|
});
|
|
594
620
|
}
|
|
595
621
|
|
|
@@ -701,7 +727,7 @@ async function completeMcpOAuthCallbackWithinDeadline(
|
|
|
701
727
|
};
|
|
702
728
|
}
|
|
703
729
|
try {
|
|
704
|
-
state =
|
|
730
|
+
state = await resolveMcpOAuthState(db, settings, input.state, deadline);
|
|
705
731
|
integrationKey = state.integrationKey ?? "custom:mcp";
|
|
706
732
|
if (state.connectAttemptId) {
|
|
707
733
|
const stored = await getConnectAttempt(db, state, state.connectAttemptId);
|
|
@@ -1718,6 +1744,41 @@ export function buildAuthorizationUrl(input: {
|
|
|
1718
1744
|
return url.toString();
|
|
1719
1745
|
}
|
|
1720
1746
|
|
|
1747
|
+
async function resolveMcpOAuthState(
|
|
1748
|
+
db: Database,
|
|
1749
|
+
settings: Settings,
|
|
1750
|
+
state: string,
|
|
1751
|
+
deadline: OAuthCallbackDeadline,
|
|
1752
|
+
): Promise<OAuthStatePayload> {
|
|
1753
|
+
const reference = readSignedState(state, requireIntegrationsStateSecret(settings)) as Record<
|
|
1754
|
+
string,
|
|
1755
|
+
unknown
|
|
1756
|
+
> | null;
|
|
1757
|
+
if (reference?.kind !== "mcp_oauth_reference") {
|
|
1758
|
+
// In-flight grants from the previous release remain valid for their
|
|
1759
|
+
// normal ten-minute lifetime during a rolling deployment.
|
|
1760
|
+
return readOAuthState(state, settings);
|
|
1761
|
+
}
|
|
1762
|
+
const accountId = requiredString(reference.accountId, "state.accountId");
|
|
1763
|
+
const workspaceId = requiredString(reference.workspaceId, "state.workspaceId");
|
|
1764
|
+
const id = requiredString(reference.id, "state.id");
|
|
1765
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id)) {
|
|
1766
|
+
throw new HTTPException(400, { message: "invalid OAuth state reference" });
|
|
1767
|
+
}
|
|
1768
|
+
const encrypted = await runCallbackDatabaseStage(deadline, "state_verify", db, (scopedDb) =>
|
|
1769
|
+
loadIntegrationOAuthPendingState(scopedDb, { accountId, workspaceId, id }),
|
|
1770
|
+
);
|
|
1771
|
+
if (!encrypted) {
|
|
1772
|
+
throw new HTTPException(400, { message: "invalid or expired OAuth state" });
|
|
1773
|
+
}
|
|
1774
|
+
const fullState = decryptEnvironmentValue(requireEnvironmentEncryption(settings), encrypted);
|
|
1775
|
+
const resolved = readOAuthState(fullState, settings);
|
|
1776
|
+
if (resolved.accountId !== accountId || resolved.workspaceId !== workspaceId) {
|
|
1777
|
+
throw new HTTPException(400, { message: "OAuth state reference mismatch" });
|
|
1778
|
+
}
|
|
1779
|
+
return resolved;
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1721
1782
|
function readOAuthState(state: string, settings: Settings): OAuthStatePayload {
|
|
1722
1783
|
const payload = readSignedState(state, requireIntegrationsStateSecret(settings)) as Record<
|
|
1723
1784
|
string,
|
|
@@ -2170,6 +2231,8 @@ function oauthStartStageLabel(stage: OAuthStartStage): string {
|
|
|
2170
2231
|
return "authorization-server discovery";
|
|
2171
2232
|
case "client_registration":
|
|
2172
2233
|
return "OAuth client registration";
|
|
2234
|
+
case "state_persist":
|
|
2235
|
+
return "OAuth state persistence";
|
|
2173
2236
|
}
|
|
2174
2237
|
}
|
|
2175
2238
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
1
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
|
|
3
3
|
import {
|
|
4
4
|
OPENGENI_SLACK_BOT_CREDENTIAL_LABEL,
|
|
@@ -1051,6 +1051,7 @@ export class OpenGeniSlackBotClient {
|
|
|
1051
1051
|
const targetKind = input.userId ? "user" : "channel";
|
|
1052
1052
|
const targetId = input.userId ?? input.channelId!;
|
|
1053
1053
|
const blocks = validateSlackMessageBlocks(input.blocks);
|
|
1054
|
+
const wireBlocks = slackMessageWireBlocks(blocks);
|
|
1054
1055
|
const requestDigest = this.postRequestDigest({
|
|
1055
1056
|
operationId: input.operationId,
|
|
1056
1057
|
targetKind,
|
|
@@ -1132,7 +1133,7 @@ export class OpenGeniSlackBotClient {
|
|
|
1132
1133
|
const posted = await this.call(headers, "chat.postMessage", {
|
|
1133
1134
|
channel: channelId,
|
|
1134
1135
|
text: input.text,
|
|
1135
|
-
...(
|
|
1136
|
+
...(wireBlocks ? { blocks: JSON.stringify(wireBlocks) } : {}),
|
|
1136
1137
|
client_msg_id: input.operationId,
|
|
1137
1138
|
unfurl_links: "false",
|
|
1138
1139
|
unfurl_media: "false",
|
|
@@ -1197,6 +1198,7 @@ export class OpenGeniSlackBotClient {
|
|
|
1197
1198
|
let providerCallStarted = false;
|
|
1198
1199
|
const blocks = validateSlackMessageBlocks(input.blocks);
|
|
1199
1200
|
try {
|
|
1201
|
+
const wireBlocks = slackMessageWireBlocks(blocks);
|
|
1200
1202
|
const headers = await this.headersFor(operation);
|
|
1201
1203
|
await this.requireMemberChannel(headers, input.channelId);
|
|
1202
1204
|
const requestDigest = this.updateRequestDigest({ ...input, ...(blocks ? { blocks } : {}) });
|
|
@@ -1235,7 +1237,7 @@ export class OpenGeniSlackBotClient {
|
|
|
1235
1237
|
channel: input.channelId,
|
|
1236
1238
|
ts: input.timestamp,
|
|
1237
1239
|
text: input.text,
|
|
1238
|
-
...(
|
|
1240
|
+
...(wireBlocks ? { blocks: JSON.stringify(wireBlocks) } : {}),
|
|
1239
1241
|
});
|
|
1240
1242
|
const slackChannelId = requiredSlackString(updated.channel, "channel");
|
|
1241
1243
|
const slackMessageTimestamp = requiredSlackString(updated.ts, "ts");
|
|
@@ -3000,6 +3002,33 @@ export function nextSlackFilesListPage(
|
|
|
3000
3002
|
return nextPage;
|
|
3001
3003
|
}
|
|
3002
3004
|
|
|
3005
|
+
/**
|
|
3006
|
+
* Slack action IDs are unique within a block. Keep the logical card untouched
|
|
3007
|
+
* for the durable request digest, and partition repeated actions only on the
|
|
3008
|
+
* provider wire. In particular, a legacy invalid_blocks receipt can retry with
|
|
3009
|
+
* its original operation ID and digest; completed/uncertain posts keep their
|
|
3010
|
+
* existing replay/reconciliation path. Values and action IDs remain exact.
|
|
3011
|
+
*/
|
|
3012
|
+
export function slackMessageWireBlocks(
|
|
3013
|
+
blocks: SlackMessageBlock[] | undefined,
|
|
3014
|
+
): SlackMessageBlock[] | undefined {
|
|
3015
|
+
if (!blocks) return undefined;
|
|
3016
|
+
const wire = blocks.flatMap((block, index): SlackMessageBlock[] => {
|
|
3017
|
+
if (
|
|
3018
|
+
block.type !== "actions" ||
|
|
3019
|
+
new Set(block.elements.map((e) => e.action_id)).size === block.elements.length
|
|
3020
|
+
)
|
|
3021
|
+
return [block];
|
|
3022
|
+
const id = createHash("sha256").update(`${index}:${block.block_id}`).digest("hex").slice(0, 24);
|
|
3023
|
+
return block.elements.map((element, position) => ({
|
|
3024
|
+
type: "actions",
|
|
3025
|
+
block_id: `opengeni_wire_${id}_${position}`,
|
|
3026
|
+
elements: [element],
|
|
3027
|
+
}));
|
|
3028
|
+
});
|
|
3029
|
+
return validateSlackMessageBlocks(wire);
|
|
3030
|
+
}
|
|
3031
|
+
|
|
3003
3032
|
function validateSlackMessageBlocks(
|
|
3004
3033
|
blocks: SlackMessageBlock[] | undefined,
|
|
3005
3034
|
): SlackMessageBlock[] | undefined {
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
approvalIdentifier,
|
|
4
4
|
ApproveSlackUserLinkAccessRequest,
|
|
5
5
|
DEFAULT_FIRST_PARTY_MCP_TOOLS,
|
|
6
|
+
resolveWorkspaceSessionToolDefaults,
|
|
6
7
|
hasOpenGeniSlackReactionScope,
|
|
7
8
|
ListSlackUserLinkAccessRequestsResponse,
|
|
8
9
|
PrepareSlackUserLinkAccessRequest,
|
|
@@ -52,6 +53,7 @@ import {
|
|
|
52
53
|
getOrCreateSlackInteraction,
|
|
53
54
|
getLatestSessionModelForSubject,
|
|
54
55
|
getSession,
|
|
56
|
+
getSessionByCreateIdempotencyKey,
|
|
55
57
|
getSessionEvent,
|
|
56
58
|
getSessionHumanInputRequest,
|
|
57
59
|
childRequiresActionResolutionExists,
|
|
@@ -119,6 +121,7 @@ import {
|
|
|
119
121
|
acceptSessionUserMessage,
|
|
120
122
|
controlHumanSessionWorkstream,
|
|
121
123
|
createSessionForRequest,
|
|
124
|
+
getActorNewSessionDefaults,
|
|
122
125
|
hasPermission,
|
|
123
126
|
requireAccessContext,
|
|
124
127
|
requireAccessGrant,
|
|
@@ -2165,6 +2168,16 @@ async function processSlackInboxEntry(deps: ApiRouteDeps, entry: SlackInteractio
|
|
|
2165
2168
|
);
|
|
2166
2169
|
let session: Awaited<ReturnType<typeof createSessionForRequest>>;
|
|
2167
2170
|
try {
|
|
2171
|
+
const defaults = await slackNewSessionDefaults(
|
|
2172
|
+
deps,
|
|
2173
|
+
grant,
|
|
2174
|
+
interaction,
|
|
2175
|
+
`slack:${entry.connectionId}:${entry.providerEventId}`,
|
|
2176
|
+
);
|
|
2177
|
+
const workspace = await getWorkspace(deps.db, interaction.workspaceId);
|
|
2178
|
+
const defaultTools =
|
|
2179
|
+
defaults.firstPartyMcpTools ??
|
|
2180
|
+
resolveWorkspaceSessionToolDefaults(workspace?.settings)?.firstPartyMcpTools;
|
|
2168
2181
|
const prepared = slackInvocationPreparedMessage(
|
|
2169
2182
|
preparedEntry,
|
|
2170
2183
|
preparedAttachments,
|
|
@@ -2175,13 +2188,17 @@ async function processSlackInboxEntry(deps: ApiRouteDeps, entry: SlackInteractio
|
|
|
2175
2188
|
grant,
|
|
2176
2189
|
interaction.workspaceId,
|
|
2177
2190
|
{
|
|
2191
|
+
...defaults,
|
|
2178
2192
|
requestedSessionId: interaction.sessionReservationId,
|
|
2179
2193
|
initialMessage: prepared.entry.text,
|
|
2180
2194
|
...(prepared.modelContext ? { modelContext: prepared.modelContext } : {}),
|
|
2181
2195
|
instructions: SLACK_SESSION_INSTRUCTIONS,
|
|
2182
|
-
firstPartyMcpTools:
|
|
2183
|
-
|
|
2184
|
-
|
|
2196
|
+
firstPartyMcpTools:
|
|
2197
|
+
defaultTools !== undefined
|
|
2198
|
+
? allowedFirstPartyMcpToolsForSession(deps.settings, defaultTools)
|
|
2199
|
+
: slackTaskFirstPartyMcpTools(deps.settings),
|
|
2200
|
+
resources: [...defaults.resources, ...preparedAttachments.resources],
|
|
2201
|
+
...(!defaults.model && preferredModel ? { model: preferredModel } : {}),
|
|
2185
2202
|
idempotencyKey: `slack:${entry.connectionId}:${entry.providerEventId}`,
|
|
2186
2203
|
clientEventId: `slack:${entry.providerEventId}`,
|
|
2187
2204
|
},
|
|
@@ -2764,19 +2781,25 @@ async function processSlackReactionInboxEntry(
|
|
|
2764
2781
|
const preparedEntry = slackReactionPreparedEntry(entry, context, preparedTask);
|
|
2765
2782
|
let session: Awaited<ReturnType<typeof createSessionForRequest>>;
|
|
2766
2783
|
try {
|
|
2784
|
+
const defaults = await slackNewSessionDefaults(
|
|
2785
|
+
deps,
|
|
2786
|
+
grant,
|
|
2787
|
+
interaction,
|
|
2788
|
+
`slack-interaction:${interaction.id}`,
|
|
2789
|
+
);
|
|
2767
2790
|
session = await createSessionForRequest(
|
|
2768
2791
|
await withCatalogSettings(deps, grant),
|
|
2769
2792
|
grant,
|
|
2770
2793
|
interaction.workspaceId,
|
|
2771
2794
|
{
|
|
2795
|
+
...defaults,
|
|
2772
2796
|
requestedSessionId: interaction.sessionReservationId,
|
|
2773
2797
|
initialMessage: preparedEntry.text,
|
|
2774
2798
|
instructions: SLACK_SESSION_INSTRUCTIONS,
|
|
2775
|
-
//
|
|
2776
|
-
//
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
...(preferredModel ? { model: preferredModel } : {}),
|
|
2799
|
+
// Reaction context stays bounded; ordinary tools follow the same saved
|
|
2800
|
+
// selection/workspace defaults as the website, without adding Slack tools.
|
|
2801
|
+
resources: [...defaults.resources, ...preparedTask.resources],
|
|
2802
|
+
...(!defaults.model && preferredModel ? { model: preferredModel } : {}),
|
|
2780
2803
|
// Every reaction entry converging on this route must use the same create
|
|
2781
2804
|
// key. This closes the same-owner multi-event race while the owner check
|
|
2782
2805
|
// above prevents a different subject from winning creation authority.
|
|
@@ -2854,6 +2877,54 @@ function slackReactionPreparedEntry(
|
|
|
2854
2877
|
};
|
|
2855
2878
|
}
|
|
2856
2879
|
|
|
2880
|
+
// A create can commit its reserved shell before initial-event acceptance. Keep
|
|
2881
|
+
// that shell's selections on retry instead of reading a subsequently edited draft.
|
|
2882
|
+
async function slackNewSessionDefaults(
|
|
2883
|
+
deps: ApiRouteDeps,
|
|
2884
|
+
grant: AccessGrant,
|
|
2885
|
+
interaction: { workspaceId: string; sessionReservationId: string; owningSubjectId: string },
|
|
2886
|
+
createKey: string,
|
|
2887
|
+
): Promise<Awaited<ReturnType<typeof getActorNewSessionDefaults>>> {
|
|
2888
|
+
const pending = await getSessionByCreateIdempotencyKey(
|
|
2889
|
+
deps.db,
|
|
2890
|
+
interaction.workspaceId,
|
|
2891
|
+
createKey,
|
|
2892
|
+
);
|
|
2893
|
+
if (!pending) return getActorNewSessionDefaults(deps, grant, interaction.workspaceId);
|
|
2894
|
+
if (
|
|
2895
|
+
pending.id !== interaction.sessionReservationId ||
|
|
2896
|
+
interaction.owningSubjectId !== grant.subjectId ||
|
|
2897
|
+
pending.createdBy.kind !== "subject" ||
|
|
2898
|
+
pending.createdBy.subjectId !== grant.subjectId
|
|
2899
|
+
) {
|
|
2900
|
+
throw new SlackInteractionPermanentError("slack_session_create_identity_mismatch");
|
|
2901
|
+
}
|
|
2902
|
+
return {
|
|
2903
|
+
model: pending.model,
|
|
2904
|
+
reasoningEffort: pending.reasoningEffort,
|
|
2905
|
+
latencyMode: pending.latencyMode,
|
|
2906
|
+
resources: pending.resources.filter((resource) => resource.kind === "repository"),
|
|
2907
|
+
...(pending.toolPolicy.mode === "explicit"
|
|
2908
|
+
? { tools: pending.tools }
|
|
2909
|
+
: {
|
|
2910
|
+
excludedMcpServerIds: pending.toolPolicy.excludedMcpServerIds,
|
|
2911
|
+
}),
|
|
2912
|
+
firstPartyMcpTools: pending.firstPartyMcpTools,
|
|
2913
|
+
...(pending.firstPartyMcpPermissions
|
|
2914
|
+
? { firstPartyMcpPermissions: pending.firstPartyMcpPermissions }
|
|
2915
|
+
: {}),
|
|
2916
|
+
variableSetIds: pending.variableSetIds,
|
|
2917
|
+
...(pending.rigId ? { rigId: pending.rigId } : {}),
|
|
2918
|
+
sandboxBackend: pending.sandboxBackend,
|
|
2919
|
+
...(pending.sandboxBackend === "selfhosted" && pending.activeSandboxId
|
|
2920
|
+
? {
|
|
2921
|
+
targetSandboxId: pending.activeSandboxId,
|
|
2922
|
+
...(pending.workingDir ? { workingDir: pending.workingDir } : {}),
|
|
2923
|
+
}
|
|
2924
|
+
: {}),
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2857
2928
|
async function remountSlackReactionTaskForSession(
|
|
2858
2929
|
deps: ApiRouteDeps,
|
|
2859
2930
|
workspaceId: string,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Select only whole items that actually survived the model-facing envelope.
|
|
2
|
+
* A cursor is a position, never proof that preceding content was consumed. */
|
|
3
|
+
export function completeChildReadSequences(page: {
|
|
4
|
+
view: string;
|
|
5
|
+
sourceExact: boolean;
|
|
6
|
+
events: readonly ({ sequence: number } & Record<string, unknown>)[];
|
|
7
|
+
}): number[] {
|
|
8
|
+
if (!page.sourceExact || !["conversation", "results"].includes(page.view)) return [];
|
|
9
|
+
return page.events
|
|
10
|
+
.filter((event) => {
|
|
11
|
+
if (event.sourceOmitted) return false;
|
|
12
|
+
if (event.fragment === undefined) return true;
|
|
13
|
+
if (!event.fragment || typeof event.fragment !== "object") return false;
|
|
14
|
+
const fragment = event.fragment as Record<string, unknown>;
|
|
15
|
+
// A final fragment cannot prove earlier fragments were ever read.
|
|
16
|
+
return fragment.offset === 0 && fragment.complete === true;
|
|
17
|
+
})
|
|
18
|
+
.map((event) => event.sequence);
|
|
19
|
+
}
|
|
@@ -90,6 +90,7 @@ export const FIRST_PARTY_TOOL_AUTHORIZATION = {
|
|
|
90
90
|
run_on: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
91
91
|
sandbox_provision: { sessionRequired: true, allOf: ["sessions:control"] },
|
|
92
92
|
connected_machine_remove: { allOf: ["enrollments:manage"] },
|
|
93
|
+
connected_machine_enroll_token: { allOf: ["enrollments:manage"] },
|
|
93
94
|
rig_list: { allOf: ["rigs:use"] },
|
|
94
95
|
rig_get: { allOf: ["rigs:use"] },
|
|
95
96
|
rig_propose_change: { allOf: ["rigs:use"] },
|