From fdf94feaceb36b937bcb9d28069c1be937c1d3d1 Mon Sep 17 00:00:00 2001 From: murashit Date: Wed, 13 May 2026 00:05:56 +0900 Subject: [PATCH] initial commit --- .gitignore | 7 + .prettierrc.json | 4 + LICENSE | 21 + README.md | 112 + esbuild.config.mjs | 25 + eslint.config.mjs | 38 + manifest.json | 9 + package-lock.json | 3594 +++++++++++++++++ package.json | 40 + src/app-server/client.ts | 421 ++ src/app-server/compatibility.ts | 31 + src/app-server/connection-manager.ts | 114 + src/app-server/service-tier.ts | 14 + src/app-server/session-client.ts | 22 + src/app-server/transport.ts | 98 + src/app-server/types.ts | 30 + src/approvals/model.ts | 152 + src/composer/suggestions.ts | 146 + src/composer/wikilink-context.ts | 62 + src/constants.ts | 3 + src/display/agent.ts | 109 + src/display/labels.ts | 45 + src/display/model.ts | 851 ++++ src/display/review.ts | 173 + src/display/signature.ts | 56 + src/display/state.ts | 38 + src/display/tool-view.ts | 139 + src/display/types.ts | 161 + src/generated/app-server/AbsolutePathBuf.ts | 14 + src/generated/app-server/AgentPath.ts | 5 + .../app-server/ApplyPatchApprovalParams.ts | 21 + .../app-server/ApplyPatchApprovalResponse.ts | 6 + src/generated/app-server/AuthMode.ts | 8 + src/generated/app-server/ClientInfo.ts | 5 + .../app-server/ClientNotification.ts | 5 + src/generated/app-server/ClientRequest.ts | 105 + src/generated/app-server/CollaborationMode.ts | 10 + src/generated/app-server/ContentItem.ts | 6 + .../app-server/ConversationGitInfo.ts | 5 + .../app-server/ConversationSummary.ts | 8 + .../app-server/ExecCommandApprovalParams.ts | 16 + .../app-server/ExecCommandApprovalResponse.ts | 6 + .../app-server/ExecPolicyAmendment.ts | 12 + src/generated/app-server/FileChange.ts | 5 + src/generated/app-server/ForcedLoginMethod.ts | 5 + .../app-server/FunctionCallOutputBody.ts | 6 + .../FunctionCallOutputContentItem.ts | 10 + .../app-server/FuzzyFileSearchMatchType.ts | 5 + .../app-server/FuzzyFileSearchParams.ts | 5 + .../app-server/FuzzyFileSearchResponse.ts | 6 + .../app-server/FuzzyFileSearchResult.ts | 9 + ...yFileSearchSessionCompletedNotification.ts | 5 + .../FuzzyFileSearchSessionStartParams.ts | 5 + .../FuzzyFileSearchSessionStartResponse.ts | 5 + .../FuzzyFileSearchSessionStopParams.ts | 5 + .../FuzzyFileSearchSessionStopResponse.ts | 5 + .../FuzzyFileSearchSessionUpdateParams.ts | 5 + .../FuzzyFileSearchSessionUpdateResponse.ts | 5 + ...zzyFileSearchSessionUpdatedNotification.ts | 6 + .../app-server/GetAuthStatusParams.ts | 5 + .../app-server/GetAuthStatusResponse.ts | 6 + .../GetConversationSummaryParams.ts | 6 + .../GetConversationSummaryResponse.ts | 6 + .../app-server/GitDiffToRemoteParams.ts | 5 + .../app-server/GitDiffToRemoteResponse.ts | 6 + src/generated/app-server/GitSha.ts | 5 + src/generated/app-server/ImageDetail.ts | 5 + .../app-server/InitializeCapabilities.ts | 17 + src/generated/app-server/InitializeParams.ts | 7 + .../app-server/InitializeResponse.ts | 20 + src/generated/app-server/InputModality.ts | 8 + .../app-server/InternalSessionSource.ts | 5 + src/generated/app-server/LocalShellAction.ts | 6 + .../app-server/LocalShellExecAction.ts | 5 + src/generated/app-server/LocalShellStatus.ts | 5 + src/generated/app-server/MessagePhase.ts | 11 + src/generated/app-server/ModeKind.ts | 8 + .../app-server/NetworkPolicyAmendment.ts | 6 + .../app-server/NetworkPolicyRuleAction.ts | 5 + src/generated/app-server/ParsedCommand.ts | 12 + src/generated/app-server/Personality.ts | 5 + src/generated/app-server/PlanType.ts | 5 + .../app-server/RealtimeConversationVersion.ts | 5 + .../app-server/RealtimeOutputModality.ts | 5 + src/generated/app-server/RealtimeVoice.ts | 5 + .../app-server/RealtimeVoicesList.ts | 6 + src/generated/app-server/ReasoningEffort.ts | 8 + .../app-server/ReasoningItemContent.ts | 5 + .../ReasoningItemReasoningSummary.ts | 5 + src/generated/app-server/ReasoningSummary.ts | 10 + src/generated/app-server/RequestId.ts | 5 + src/generated/app-server/Resource.ts | 9 + src/generated/app-server/ResourceContent.ts | 17 + src/generated/app-server/ResourceTemplate.ts | 9 + src/generated/app-server/ResponseItem.ts | 17 + src/generated/app-server/ReviewDecision.ts | 10 + .../app-server/ServerNotification.ts | 72 + src/generated/app-server/ServerRequest.ts | 18 + src/generated/app-server/SessionSource.ts | 7 + src/generated/app-server/Settings.ts | 9 + src/generated/app-server/SubAgentSource.ts | 7 + src/generated/app-server/ThreadId.ts | 5 + src/generated/app-server/ThreadMemoryMode.ts | 5 + src/generated/app-server/Tool.ts | 9 + src/generated/app-server/Verbosity.ts | 9 + src/generated/app-server/WebSearchAction.ts | 5 + .../app-server/WebSearchContextSize.ts | 5 + src/generated/app-server/WebSearchLocation.ts | 5 + src/generated/app-server/WebSearchMode.ts | 5 + .../app-server/WebSearchToolConfig.ts | 7 + src/generated/app-server/index.ts | 85 + .../app-server/serde_json/JsonValue.ts | 5 + src/generated/app-server/v2/Account.ts | 6 + .../v2/AccountLoginCompletedNotification.ts | 5 + .../AccountRateLimitsUpdatedNotification.ts | 6 + .../v2/AccountUpdatedNotification.ts | 7 + .../app-server/v2/ActivePermissionProfile.ts | 21 + .../v2/ActivePermissionProfileModification.ts | 6 + .../v2/AddCreditsNudgeCreditType.ts | 5 + .../v2/AddCreditsNudgeEmailStatus.ts | 5 + .../v2/AdditionalFileSystemPermissions.ts | 15 + .../v2/AdditionalNetworkPermissions.ts | 5 + .../v2/AdditionalPermissionProfile.ts | 11 + .../v2/AgentMessageDeltaNotification.ts | 5 + .../app-server/v2/AnalyticsConfig.ts | 6 + src/generated/app-server/v2/AppBranding.ts | 8 + src/generated/app-server/v2/AppInfo.ts | 19 + .../v2/AppListUpdatedNotification.ts | 9 + src/generated/app-server/v2/AppMetadata.ts | 7 + src/generated/app-server/v2/AppReview.ts | 5 + src/generated/app-server/v2/AppScreenshot.ts | 5 + src/generated/app-server/v2/AppSummary.ts | 8 + .../app-server/v2/AppToolApproval.ts | 5 + src/generated/app-server/v2/AppToolsConfig.ts | 6 + .../app-server/v2/ApprovalsReviewer.ts | 12 + src/generated/app-server/v2/AppsConfig.ts | 8 + .../app-server/v2/AppsDefaultConfig.ts | 5 + src/generated/app-server/v2/AppsListParams.ts | 24 + .../app-server/v2/AppsListResponse.ts | 14 + src/generated/app-server/v2/AskForApproval.ts | 5 + .../app-server/v2/AutoReviewDecisionSource.ts | 8 + src/generated/app-server/v2/ByteRange.ts | 5 + .../app-server/v2/CancelLoginAccountParams.ts | 5 + .../v2/CancelLoginAccountResponse.ts | 6 + .../app-server/v2/CancelLoginAccountStatus.ts | 5 + .../v2/ChatgptAuthTokensRefreshParams.ts | 16 + .../v2/ChatgptAuthTokensRefreshReason.ts | 5 + .../v2/ChatgptAuthTokensRefreshResponse.ts | 5 + src/generated/app-server/v2/CodexErrorInfo.ts | 12 + .../app-server/v2/CollabAgentState.ts | 6 + .../app-server/v2/CollabAgentStatus.ts | 5 + .../app-server/v2/CollabAgentTool.ts | 5 + .../v2/CollabAgentToolCallStatus.ts | 5 + .../v2/CollaborationModeListParams.ts | 8 + .../v2/CollaborationModeListResponse.ts | 9 + .../app-server/v2/CollaborationModeMask.ts | 10 + src/generated/app-server/v2/CommandAction.ts | 6 + .../v2/CommandExecOutputDeltaNotification.ts | 30 + .../app-server/v2/CommandExecOutputStream.ts | 8 + .../app-server/v2/CommandExecParams.ts | 106 + .../app-server/v2/CommandExecResizeParams.ts | 18 + .../v2/CommandExecResizeResponse.ts | 8 + .../app-server/v2/CommandExecResponse.ts | 24 + .../app-server/v2/CommandExecTerminalSize.ts | 16 + .../v2/CommandExecTerminateParams.ts | 13 + .../v2/CommandExecTerminateResponse.ts | 8 + .../app-server/v2/CommandExecWriteParams.ts | 22 + .../app-server/v2/CommandExecWriteResponse.ts | 8 + .../v2/CommandExecutionApprovalDecision.ts | 7 + ...CommandExecutionOutputDeltaNotification.ts | 5 + .../CommandExecutionRequestApprovalParams.ts | 62 + ...CommandExecutionRequestApprovalResponse.ts | 6 + .../app-server/v2/CommandExecutionSource.ts | 5 + .../app-server/v2/CommandExecutionStatus.ts | 5 + .../app-server/v2/CommandMigration.ts | 5 + src/generated/app-server/v2/Config.ts | 24 + .../app-server/v2/ConfigBatchWriteParams.ts | 14 + src/generated/app-server/v2/ConfigEdit.ts | 7 + src/generated/app-server/v2/ConfigLayer.ts | 7 + .../app-server/v2/ConfigLayerMetadata.ts | 6 + .../app-server/v2/ConfigLayerSource.ts | 16 + .../app-server/v2/ConfigReadParams.ts | 11 + .../app-server/v2/ConfigReadResponse.ts | 8 + .../app-server/v2/ConfigRequirements.ts | 12 + .../v2/ConfigRequirementsReadResponse.ts | 10 + .../app-server/v2/ConfigValueWriteParams.ts | 11 + .../v2/ConfigWarningNotification.ts | 22 + .../app-server/v2/ConfigWriteResponse.ts | 12 + .../app-server/v2/ConfiguredHookHandler.ts | 5 + .../v2/ConfiguredHookMatcherGroup.ts | 6 + .../v2/ContextCompactedNotification.ts | 8 + .../app-server/v2/CreditsSnapshot.ts | 5 + .../v2/DeprecationNoticeNotification.ts | 13 + .../app-server/v2/DeviceKeyAlgorithm.ts | 8 + .../app-server/v2/DeviceKeyCreateParams.ts | 13 + .../app-server/v2/DeviceKeyCreateResponse.ts | 14 + .../app-server/v2/DeviceKeyProtectionClass.ts | 8 + .../v2/DeviceKeyProtectionPolicy.ts | 8 + .../app-server/v2/DeviceKeyPublicParams.ts | 8 + .../app-server/v2/DeviceKeyPublicResponse.ts | 14 + .../app-server/v2/DeviceKeySignParams.ts | 9 + .../app-server/v2/DeviceKeySignPayload.ts | 54 + .../app-server/v2/DeviceKeySignResponse.ts | 18 + .../v2/DynamicToolCallOutputContentItem.ts | 5 + .../app-server/v2/DynamicToolCallParams.ts | 6 + .../app-server/v2/DynamicToolCallResponse.ts | 6 + .../app-server/v2/DynamicToolCallStatus.ts | 5 + .../app-server/v2/DynamicToolSpec.ts | 6 + .../app-server/v2/ErrorNotification.ts | 6 + .../app-server/v2/ExecPolicyAmendment.ts | 5 + .../app-server/v2/ExperimentalFeature.ts | 37 + .../ExperimentalFeatureEnablementSetParams.ts | 12 + ...xperimentalFeatureEnablementSetResponse.ts | 9 + .../v2/ExperimentalFeatureListParams.ts | 13 + .../v2/ExperimentalFeatureListResponse.ts | 11 + .../app-server/v2/ExperimentalFeatureStage.ts | 5 + .../v2/ExternalAgentConfigDetectParams.ts | 13 + .../v2/ExternalAgentConfigDetectResponse.ts | 6 + ...lAgentConfigImportCompletedNotification.ts | 5 + .../v2/ExternalAgentConfigImportParams.ts | 6 + .../v2/ExternalAgentConfigImportResponse.ts | 5 + .../v2/ExternalAgentConfigMigrationItem.ts | 11 + .../ExternalAgentConfigMigrationItemType.ts | 5 + .../app-server/v2/FeedbackUploadParams.ts | 5 + .../app-server/v2/FeedbackUploadResponse.ts | 5 + .../v2/FileChangeApprovalDecision.ts | 5 + .../v2/FileChangeOutputDeltaNotification.ts | 10 + .../v2/FileChangePatchUpdatedNotification.ts | 6 + .../v2/FileChangeRequestApprovalParams.ts | 18 + .../v2/FileChangeRequestApprovalResponse.ts | 6 + .../app-server/v2/FileSystemAccessMode.ts | 5 + src/generated/app-server/v2/FileSystemPath.ts | 7 + .../app-server/v2/FileSystemSandboxEntry.ts | 7 + .../app-server/v2/FileSystemSpecialPath.ts | 5 + .../app-server/v2/FileUpdateChange.ts | 6 + .../app-server/v2/FsChangedNotification.ts | 17 + src/generated/app-server/v2/FsCopyParams.ts | 21 + src/generated/app-server/v2/FsCopyResponse.ts | 8 + .../app-server/v2/FsCreateDirectoryParams.ts | 17 + .../v2/FsCreateDirectoryResponse.ts | 8 + .../app-server/v2/FsGetMetadataParams.ts | 13 + .../app-server/v2/FsGetMetadataResponse.ts | 28 + .../app-server/v2/FsReadDirectoryEntry.ts | 20 + .../app-server/v2/FsReadDirectoryParams.ts | 13 + .../app-server/v2/FsReadDirectoryResponse.ts | 13 + .../app-server/v2/FsReadFileParams.ts | 13 + .../app-server/v2/FsReadFileResponse.ts | 12 + src/generated/app-server/v2/FsRemoveParams.ts | 21 + .../app-server/v2/FsRemoveResponse.ts | 8 + .../app-server/v2/FsUnwatchParams.ts | 12 + .../app-server/v2/FsUnwatchResponse.ts | 8 + src/generated/app-server/v2/FsWatchParams.ts | 17 + .../app-server/v2/FsWatchResponse.ts | 13 + .../app-server/v2/FsWriteFileParams.ts | 17 + .../app-server/v2/FsWriteFileResponse.ts | 8 + .../app-server/v2/GetAccountParams.ts | 13 + .../v2/GetAccountRateLimitsResponse.ts | 14 + .../app-server/v2/GetAccountResponse.ts | 6 + src/generated/app-server/v2/GitInfo.ts | 5 + .../app-server/v2/GrantedPermissionProfile.ts | 7 + .../app-server/v2/GuardianApprovalReview.ts | 13 + .../v2/GuardianApprovalReviewAction.ts | 9 + .../v2/GuardianApprovalReviewStatus.ts | 8 + .../app-server/v2/GuardianCommandSource.ts | 5 + .../app-server/v2/GuardianRiskLevel.ts | 8 + .../v2/GuardianUserAuthorization.ts | 8 + .../v2/GuardianWarningNotification.ts | 13 + .../v2/HookCompletedNotification.ts | 6 + src/generated/app-server/v2/HookErrorInfo.ts | 5 + src/generated/app-server/v2/HookEventName.ts | 5 + .../app-server/v2/HookExecutionMode.ts | 5 + .../app-server/v2/HookHandlerType.ts | 5 + src/generated/app-server/v2/HookMetadata.ts | 10 + src/generated/app-server/v2/HookMigration.ts | 5 + .../app-server/v2/HookOutputEntry.ts | 6 + .../app-server/v2/HookOutputEntryKind.ts | 5 + .../app-server/v2/HookPromptFragment.ts | 5 + src/generated/app-server/v2/HookRunStatus.ts | 5 + src/generated/app-server/v2/HookRunSummary.ts | 13 + src/generated/app-server/v2/HookScope.ts | 5 + src/generated/app-server/v2/HookSource.ts | 5 + .../app-server/v2/HookStartedNotification.ts | 6 + .../app-server/v2/HookTrustStatus.ts | 5 + src/generated/app-server/v2/HooksListEntry.ts | 7 + .../app-server/v2/HooksListParams.ts | 9 + .../app-server/v2/HooksListResponse.ts | 6 + .../v2/ItemCompletedNotification.ts | 10 + ...dianApprovalReviewCompletedNotification.ts | 38 + ...ardianApprovalReviewStartedNotification.ts | 33 + .../app-server/v2/ItemStartedNotification.ts | 10 + .../v2/ListMcpServerStatusParams.ts | 19 + .../v2/ListMcpServerStatusResponse.ts | 11 + .../app-server/v2/LoginAccountParams.ts | 21 + .../app-server/v2/LoginAccountResponse.ts | 17 + .../app-server/v2/LogoutAccountResponse.ts | 5 + .../app-server/v2/ManagedHooksRequirements.ts | 6 + .../app-server/v2/MarketplaceAddParams.ts | 5 + .../app-server/v2/MarketplaceAddResponse.ts | 6 + .../app-server/v2/MarketplaceInterface.ts | 5 + .../app-server/v2/MarketplaceLoadErrorInfo.ts | 6 + .../app-server/v2/MarketplaceRemoveParams.ts | 5 + .../v2/MarketplaceRemoveResponse.ts | 6 + .../v2/MarketplaceUpgradeErrorInfo.ts | 5 + .../app-server/v2/MarketplaceUpgradeParams.ts | 5 + .../v2/MarketplaceUpgradeResponse.ts | 7 + src/generated/app-server/v2/McpAuthStatus.ts | 5 + .../app-server/v2/McpElicitationArrayType.ts | 5 + .../v2/McpElicitationBooleanSchema.ts | 6 + .../v2/McpElicitationBooleanType.ts | 5 + .../v2/McpElicitationConstOption.ts | 5 + .../app-server/v2/McpElicitationEnumSchema.ts | 8 + .../McpElicitationLegacyTitledEnumSchema.ts | 6 + .../v2/McpElicitationMultiSelectEnumSchema.ts | 7 + .../v2/McpElicitationNumberSchema.ts | 6 + .../app-server/v2/McpElicitationNumberType.ts | 5 + .../app-server/v2/McpElicitationObjectType.ts | 5 + .../v2/McpElicitationPrimitiveSchema.ts | 9 + .../app-server/v2/McpElicitationSchema.ts | 13 + .../McpElicitationSingleSelectEnumSchema.ts | 7 + .../v2/McpElicitationStringFormat.ts | 5 + .../v2/McpElicitationStringSchema.ts | 7 + .../app-server/v2/McpElicitationStringType.ts | 5 + .../v2/McpElicitationTitledEnumItems.ts | 6 + ...pElicitationTitledMultiSelectEnumSchema.ts | 7 + ...ElicitationTitledSingleSelectEnumSchema.ts | 7 + .../v2/McpElicitationUntitledEnumItems.ts | 6 + ...licitationUntitledMultiSelectEnumSchema.ts | 7 + ...icitationUntitledSingleSelectEnumSchema.ts | 6 + .../app-server/v2/McpResourceReadParams.ts | 5 + .../app-server/v2/McpResourceReadResponse.ts | 6 + .../v2/McpServerElicitationAction.ts | 5 + .../v2/McpServerElicitationRequestParams.ts | 16 + .../v2/McpServerElicitationRequestResponse.ts | 17 + .../app-server/v2/McpServerMigration.ts | 5 + ...cpServerOauthLoginCompletedNotification.ts | 5 + .../v2/McpServerOauthLoginParams.ts | 5 + .../v2/McpServerOauthLoginResponse.ts | 5 + .../app-server/v2/McpServerRefreshResponse.ts | 5 + .../app-server/v2/McpServerStartupState.ts | 5 + .../app-server/v2/McpServerStatus.ts | 9 + .../app-server/v2/McpServerStatusDetail.ts | 5 + .../v2/McpServerStatusUpdatedNotification.ts | 6 + .../app-server/v2/McpServerToolCallParams.ts | 6 + .../v2/McpServerToolCallResponse.ts | 6 + .../app-server/v2/McpToolCallError.ts | 5 + .../v2/McpToolCallProgressNotification.ts | 5 + .../app-server/v2/McpToolCallResult.ts | 6 + .../app-server/v2/McpToolCallStatus.ts | 5 + src/generated/app-server/v2/MemoryCitation.ts | 6 + .../app-server/v2/MemoryCitationEntry.ts | 5 + .../app-server/v2/MemoryResetResponse.ts | 5 + src/generated/app-server/v2/MergeStrategy.ts | 5 + .../app-server/v2/MigrationDetails.ts | 11 + .../v2/MockExperimentalMethodParams.ts | 9 + .../v2/MockExperimentalMethodResponse.ts | 9 + src/generated/app-server/v2/Model.ts | 15 + .../app-server/v2/ModelAvailabilityNux.ts | 5 + .../app-server/v2/ModelListParams.ts | 17 + .../app-server/v2/ModelListResponse.ts | 11 + .../v2/ModelProviderCapabilitiesReadParams.ts | 5 + .../ModelProviderCapabilitiesReadResponse.ts | 5 + .../app-server/v2/ModelRerouteReason.ts | 5 + .../v2/ModelReroutedNotification.ts | 6 + .../app-server/v2/ModelServiceTier.ts | 5 + .../app-server/v2/ModelUpgradeInfo.ts | 5 + .../app-server/v2/ModelVerification.ts | 5 + .../v2/ModelVerificationNotification.ts | 6 + src/generated/app-server/v2/NetworkAccess.ts | 5 + .../app-server/v2/NetworkApprovalContext.ts | 6 + .../app-server/v2/NetworkApprovalProtocol.ts | 5 + .../app-server/v2/NetworkDomainPermission.ts | 5 + .../app-server/v2/NetworkPolicyAmendment.ts | 6 + .../app-server/v2/NetworkPolicyRuleAction.ts | 5 + .../app-server/v2/NetworkRequirements.ts | 32 + .../v2/NetworkUnixSocketPermission.ts | 5 + .../app-server/v2/NonSteerableTurnKind.ts | 5 + .../app-server/v2/OverriddenMetadata.ts | 7 + .../app-server/v2/PatchApplyStatus.ts | 5 + .../app-server/v2/PatchChangeKind.ts | 5 + .../app-server/v2/PermissionGrantScope.ts | 5 + .../app-server/v2/PermissionProfile.ts | 7 + .../PermissionProfileFileSystemPermissions.ts | 6 + .../v2/PermissionProfileModificationParams.ts | 6 + .../v2/PermissionProfileNetworkPermissions.ts | 5 + .../v2/PermissionProfileSelectionParams.ts | 6 + .../v2/PermissionsRequestApprovalParams.ts | 11 + .../v2/PermissionsRequestApprovalResponse.ts | 11 + .../app-server/v2/PlanDeltaNotification.ts | 9 + .../app-server/v2/PluginAuthPolicy.ts | 5 + .../app-server/v2/PluginAvailability.ts | 5 + src/generated/app-server/v2/PluginDetail.ts | 10 + .../app-server/v2/PluginHookSummary.ts | 6 + .../app-server/v2/PluginInstallParams.ts | 6 + .../app-server/v2/PluginInstallPolicy.ts | 5 + .../app-server/v2/PluginInstallResponse.ts | 7 + .../app-server/v2/PluginInterface.ts | 35 + .../v2/PluginListMarketplaceKind.ts | 5 + .../app-server/v2/PluginListParams.ts | 17 + .../app-server/v2/PluginListResponse.ts | 7 + .../app-server/v2/PluginMarketplaceEntry.ts | 13 + .../app-server/v2/PluginReadParams.ts | 6 + .../app-server/v2/PluginReadResponse.ts | 6 + .../app-server/v2/PluginShareContext.ts | 6 + .../app-server/v2/PluginShareDeleteParams.ts | 5 + .../v2/PluginShareDeleteResponse.ts | 5 + .../v2/PluginShareDiscoverability.ts | 5 + .../app-server/v2/PluginShareListItem.ts | 7 + .../app-server/v2/PluginShareListParams.ts | 5 + .../app-server/v2/PluginShareListResponse.ts | 6 + .../app-server/v2/PluginSharePrincipal.ts | 6 + .../app-server/v2/PluginSharePrincipalType.ts | 5 + .../app-server/v2/PluginShareSaveParams.ts | 8 + .../app-server/v2/PluginShareSaveResponse.ts | 5 + .../app-server/v2/PluginShareTarget.ts | 6 + .../v2/PluginShareUpdateDiscoverability.ts | 5 + .../v2/PluginShareUpdateTargetsParams.ts | 7 + .../v2/PluginShareUpdateTargetsResponse.ts | 7 + .../app-server/v2/PluginSkillReadParams.ts | 5 + .../app-server/v2/PluginSkillReadResponse.ts | 5 + src/generated/app-server/v2/PluginSource.ts | 6 + src/generated/app-server/v2/PluginSummary.ts | 19 + .../app-server/v2/PluginUninstallParams.ts | 5 + .../app-server/v2/PluginUninstallResponse.ts | 5 + .../app-server/v2/PluginsMigration.ts | 5 + .../v2/ProcessExitedNotification.ts | 42 + .../app-server/v2/ProcessKillParams.ts | 12 + .../app-server/v2/ProcessKillResponse.ts | 8 + .../v2/ProcessOutputDeltaNotification.ts | 26 + .../app-server/v2/ProcessOutputStream.ts | 8 + .../app-server/v2/ProcessResizePtyParams.ts | 17 + .../app-server/v2/ProcessResizePtyResponse.ts | 8 + .../app-server/v2/ProcessSpawnParams.ts | 73 + .../app-server/v2/ProcessSpawnResponse.ts | 8 + .../app-server/v2/ProcessTerminalSize.ts | 16 + .../app-server/v2/ProcessWriteStdinParams.ts | 21 + .../v2/ProcessWriteStdinResponse.ts | 8 + src/generated/app-server/v2/ProfileV2.ts | 19 + .../app-server/v2/RateLimitReachedType.ts | 5 + .../app-server/v2/RateLimitSnapshot.ts | 9 + .../app-server/v2/RateLimitWindow.ts | 5 + .../RawResponseItemCompletedNotification.ts | 6 + .../app-server/v2/ReasoningEffortOption.ts | 6 + .../ReasoningSummaryPartAddedNotification.ts | 5 + .../ReasoningSummaryTextDeltaNotification.ts | 5 + .../v2/ReasoningTextDeltaNotification.ts | 5 + .../RemoteControlClientConnectionAudience.ts | 8 + .../RemoteControlClientEnrollmentAudience.ts | 8 + .../v2/RemoteControlConnectionStatus.ts | 5 + .../RemoteControlStatusChangedNotification.ts | 9 + .../app-server/v2/RequestPermissionProfile.ts | 7 + .../app-server/v2/ResidencyRequirement.ts | 5 + src/generated/app-server/v2/ReviewDelivery.ts | 5 + .../app-server/v2/ReviewStartParams.ts | 12 + .../app-server/v2/ReviewStartResponse.ts | 13 + src/generated/app-server/v2/ReviewTarget.ts | 9 + src/generated/app-server/v2/SandboxMode.ts | 5 + src/generated/app-server/v2/SandboxPolicy.ts | 7 + .../app-server/v2/SandboxWorkspaceWrite.ts | 5 + .../v2/SendAddCreditsNudgeEmailParams.ts | 6 + .../v2/SendAddCreditsNudgeEmailResponse.ts | 6 + .../v2/ServerRequestResolvedNotification.ts | 6 + .../app-server/v2/SessionMigration.ts | 5 + src/generated/app-server/v2/SessionSource.ts | 6 + .../app-server/v2/SkillDependencies.ts | 6 + src/generated/app-server/v2/SkillErrorInfo.ts | 5 + src/generated/app-server/v2/SkillInterface.ts | 6 + src/generated/app-server/v2/SkillMetadata.ts | 13 + src/generated/app-server/v2/SkillScope.ts | 5 + src/generated/app-server/v2/SkillSummary.ts | 7 + .../app-server/v2/SkillToolDependency.ts | 5 + .../v2/SkillsChangedNotification.ts | 11 + .../app-server/v2/SkillsConfigWriteParams.ts | 14 + .../v2/SkillsConfigWriteResponse.ts | 5 + .../app-server/v2/SkillsListEntry.ts | 7 + .../v2/SkillsListExtraRootsForCwd.ts | 5 + .../app-server/v2/SkillsListParams.ts | 13 + .../app-server/v2/SkillsListResponse.ts | 6 + src/generated/app-server/v2/SortDirection.ts | 5 + .../app-server/v2/SubagentMigration.ts | 5 + .../v2/TerminalInteractionNotification.ts | 5 + src/generated/app-server/v2/TextElement.ts | 14 + src/generated/app-server/v2/TextPosition.ts | 13 + src/generated/app-server/v2/TextRange.ts | 6 + src/generated/app-server/v2/Thread.ts | 86 + .../app-server/v2/ThreadActiveFlag.ts | 5 + ...ThreadApproveGuardianDeniedActionParams.ts | 10 + ...readApproveGuardianDeniedActionResponse.ts | 5 + .../app-server/v2/ThreadArchiveParams.ts | 5 + .../app-server/v2/ThreadArchiveResponse.ts | 5 + .../v2/ThreadArchivedNotification.ts | 5 + .../ThreadBackgroundTerminalsCleanParams.ts | 5 + .../ThreadBackgroundTerminalsCleanResponse.ts | 5 + .../app-server/v2/ThreadClosedNotification.ts | 5 + .../app-server/v2/ThreadCompactStartParams.ts | 5 + .../v2/ThreadCompactStartResponse.ts | 5 + .../v2/ThreadDecrementElicitationParams.ts | 12 + .../v2/ThreadDecrementElicitationResponse.ts | 16 + .../app-server/v2/ThreadForkParams.ts | 56 + .../app-server/v2/ThreadForkResponse.ts | 37 + src/generated/app-server/v2/ThreadGoal.ts | 6 + .../app-server/v2/ThreadGoalClearParams.ts | 5 + .../app-server/v2/ThreadGoalClearResponse.ts | 5 + .../v2/ThreadGoalClearedNotification.ts | 5 + .../app-server/v2/ThreadGoalGetParams.ts | 5 + .../app-server/v2/ThreadGoalGetResponse.ts | 6 + .../app-server/v2/ThreadGoalSetParams.ts | 6 + .../app-server/v2/ThreadGoalSetResponse.ts | 6 + .../app-server/v2/ThreadGoalStatus.ts | 5 + .../v2/ThreadGoalUpdatedNotification.ts | 6 + .../v2/ThreadIncrementElicitationParams.ts | 12 + .../v2/ThreadIncrementElicitationResponse.ts | 16 + .../app-server/v2/ThreadInjectItemsParams.ts | 10 + .../v2/ThreadInjectItemsResponse.ts | 5 + src/generated/app-server/v2/ThreadItem.ts | 101 + .../app-server/v2/ThreadListParams.ts | 54 + .../app-server/v2/ThreadListResponse.ts | 18 + .../app-server/v2/ThreadLoadedListParams.ts | 13 + .../app-server/v2/ThreadLoadedListResponse.ts | 14 + .../v2/ThreadMemoryModeSetParams.ts | 6 + .../v2/ThreadMemoryModeSetResponse.ts | 5 + .../v2/ThreadMetadataGitInfoUpdateParams.ts | 20 + .../v2/ThreadMetadataUpdateParams.ts | 12 + .../v2/ThreadMetadataUpdateResponse.ts | 6 + .../v2/ThreadNameUpdatedNotification.ts | 5 + .../app-server/v2/ThreadReadParams.ts | 9 + .../app-server/v2/ThreadReadResponse.ts | 6 + .../v2/ThreadRealtimeAppendAudioParams.ts | 9 + .../v2/ThreadRealtimeAppendAudioResponse.ts | 8 + .../v2/ThreadRealtimeAppendTextParams.ts | 8 + .../v2/ThreadRealtimeAppendTextResponse.ts | 8 + .../app-server/v2/ThreadRealtimeAudioChunk.ts | 8 + .../v2/ThreadRealtimeClosedNotification.ts | 8 + .../v2/ThreadRealtimeErrorNotification.ts | 8 + .../v2/ThreadRealtimeItemAddedNotification.ts | 9 + .../v2/ThreadRealtimeListVoicesParams.ts | 8 + .../v2/ThreadRealtimeListVoicesResponse.ts | 9 + ...eadRealtimeOutputAudioDeltaNotification.ts | 9 + .../v2/ThreadRealtimeSdpNotification.ts | 8 + .../v2/ThreadRealtimeStartParams.ts | 16 + .../v2/ThreadRealtimeStartResponse.ts | 8 + .../v2/ThreadRealtimeStartTransport.ts | 13 + .../v2/ThreadRealtimeStartedNotification.ts | 9 + .../app-server/v2/ThreadRealtimeStopParams.ts | 8 + .../v2/ThreadRealtimeStopResponse.ts | 8 + ...readRealtimeTranscriptDeltaNotification.ts | 13 + ...hreadRealtimeTranscriptDoneNotification.ts | 13 + .../app-server/v2/ThreadResumeParams.ts | 61 + .../app-server/v2/ThreadResumeResponse.ts | 37 + .../app-server/v2/ThreadRollbackParams.ts | 12 + .../app-server/v2/ThreadRollbackResponse.ts | 14 + .../app-server/v2/ThreadSetNameParams.ts | 5 + .../app-server/v2/ThreadSetNameResponse.ts | 5 + .../app-server/v2/ThreadShellCommandParams.ts | 12 + .../v2/ThreadShellCommandResponse.ts | 5 + src/generated/app-server/v2/ThreadSortKey.ts | 5 + src/generated/app-server/v2/ThreadSource.ts | 5 + .../app-server/v2/ThreadSourceKind.ts | 5 + .../app-server/v2/ThreadStartParams.ts | 55 + .../app-server/v2/ThreadStartResponse.ts | 37 + .../app-server/v2/ThreadStartSource.ts | 5 + .../v2/ThreadStartedNotification.ts | 6 + src/generated/app-server/v2/ThreadStatus.ts | 6 + .../v2/ThreadStatusChangedNotification.ts | 6 + .../app-server/v2/ThreadTokenUsage.ts | 6 + .../v2/ThreadTokenUsageUpdatedNotification.ts | 6 + .../v2/ThreadTurnsItemsListParams.ts | 18 + .../v2/ThreadTurnsItemsListResponse.ts | 16 + .../app-server/v2/ThreadTurnsListParams.ts | 23 + .../app-server/v2/ThreadTurnsListResponse.ts | 18 + .../app-server/v2/ThreadUnarchiveParams.ts | 5 + .../app-server/v2/ThreadUnarchiveResponse.ts | 6 + .../v2/ThreadUnarchivedNotification.ts | 5 + .../app-server/v2/ThreadUnsubscribeParams.ts | 5 + .../v2/ThreadUnsubscribeResponse.ts | 6 + .../app-server/v2/ThreadUnsubscribeStatus.ts | 5 + .../app-server/v2/TokenUsageBreakdown.ts | 5 + .../v2/ToolRequestUserInputAnswer.ts | 8 + .../v2/ToolRequestUserInputOption.ts | 8 + .../v2/ToolRequestUserInputParams.ts | 9 + .../v2/ToolRequestUserInputQuestion.ts | 9 + .../v2/ToolRequestUserInputResponse.ts | 9 + src/generated/app-server/v2/ToolsV2.ts | 6 + src/generated/app-server/v2/Turn.ts | 33 + .../v2/TurnCompletedNotification.ts | 6 + .../v2/TurnDiffUpdatedNotification.ts | 9 + .../app-server/v2/TurnEnvironmentParams.ts | 6 + src/generated/app-server/v2/TurnError.ts | 6 + .../app-server/v2/TurnInterruptParams.ts | 5 + .../app-server/v2/TurnInterruptResponse.ts | 5 + src/generated/app-server/v2/TurnItemsView.ts | 5 + src/generated/app-server/v2/TurnPlanStep.ts | 6 + .../app-server/v2/TurnPlanStepStatus.ts | 5 + .../v2/TurnPlanUpdatedNotification.ts | 6 + .../app-server/v2/TurnStartParams.ts | 85 + .../app-server/v2/TurnStartResponse.ts | 6 + .../app-server/v2/TurnStartedNotification.ts | 6 + src/generated/app-server/v2/TurnStatus.ts | 5 + .../app-server/v2/TurnSteerParams.ts | 15 + .../app-server/v2/TurnSteerResponse.ts | 5 + src/generated/app-server/v2/UserInput.ts | 10 + .../app-server/v2/WarningNotification.ts | 13 + .../app-server/v2/WebSearchAction.ts | 5 + .../app-server/v2/WindowsSandboxReadiness.ts | 5 + .../v2/WindowsSandboxReadinessResponse.ts | 6 + ...indowsSandboxSetupCompletedNotification.ts | 6 + .../app-server/v2/WindowsSandboxSetupMode.ts | 5 + .../v2/WindowsSandboxSetupStartParams.ts | 7 + .../v2/WindowsSandboxSetupStartResponse.ts | 5 + ...WindowsWorldWritableWarningNotification.ts | 5 + src/generated/app-server/v2/WriteStatus.ts | 5 + src/generated/app-server/v2/index.ts | 500 +++ src/main.ts | 1325 ++++++ src/panel/app-server-logs.ts | 34 + src/panel/collaboration-mode.ts | 37 + src/panel/controller.ts | 390 ++ src/panel/diagnostics.ts | 43 + src/panel/hook-display.ts | 36 + src/panel/model-runtime.ts | 28 + src/panel/request-state.ts | 44 + src/panel/runtime-settings.ts | 44 + src/panel/runtime-state.ts | 146 + src/panel/runtime-view.ts | 258 ++ src/panel/session-controller.ts | 92 + src/panel/slash-commands.ts | 117 + src/panel/thread-history.ts | 76 + src/panel/thread-naming.ts | 332 ++ src/panel/thread-rename.ts | 199 + src/settings-data.ts | 94 + src/settings-tab.ts | 406 ++ src/settings.ts | 63 + src/state/panel-state.ts | 129 + src/threads.ts | 27 + src/user-input/model.ts | 39 + src/utils.ts | 36 + src/view/components.ts | 56 + src/view/composer.ts | 202 + src/view/config.ts | 14 + src/view/dom.ts | 45 + src/view/execution-state.ts | 7 + src/view/message-stream.ts | 228 ++ src/view/pending-request-message.ts | 185 + src/view/scroll.ts | 51 + src/view/tool-result.ts | 109 + src/view/toolbar.ts | 418 ++ src/view/work-items.ts | 158 + src/view/work-message.ts | 19 + styles.css | 1483 +++++++ tests/app-server-client.test.ts | 417 ++ tests/app-server-compatibility.test.ts | 27 + tests/app-server-logs.test.ts | 41 + tests/approvals.test.ts | 102 + tests/collaboration-mode.test.ts | 45 + tests/composer-suggestions.test.ts | 79 + tests/connection-manager.test.ts | 92 + tests/display-model.test.ts | 951 +++++ tests/mocks/obsidian.ts | 278 ++ tests/panel-controller.test.ts | 722 ++++ tests/runtime-settings.test.ts | 241 ++ tests/service-tier.test.ts | 27 + tests/settings-tab.test.ts | 308 ++ tests/settings.test.ts | 61 + tests/slash-commands.test.ts | 56 + tests/thread-naming.test.ts | 211 + tests/user-input.test.ts | 35 + tests/view-dom.test.ts | 55 + tests/view-renderers.test.ts | 1169 ++++++ tests/view-scroll.test.ts | 74 + tests/wikilink-context.test.ts | 37 + tsconfig.json | 17 + versions.json | 3 + vitest.config.ts | 14 + 671 files changed, 24986 insertions(+) create mode 100644 .gitignore create mode 100644 .prettierrc.json create mode 100644 LICENSE create mode 100644 README.md create mode 100644 esbuild.config.mjs create mode 100644 eslint.config.mjs create mode 100644 manifest.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/app-server/client.ts create mode 100644 src/app-server/compatibility.ts create mode 100644 src/app-server/connection-manager.ts create mode 100644 src/app-server/service-tier.ts create mode 100644 src/app-server/session-client.ts create mode 100644 src/app-server/transport.ts create mode 100644 src/app-server/types.ts create mode 100644 src/approvals/model.ts create mode 100644 src/composer/suggestions.ts create mode 100644 src/composer/wikilink-context.ts create mode 100644 src/constants.ts create mode 100644 src/display/agent.ts create mode 100644 src/display/labels.ts create mode 100644 src/display/model.ts create mode 100644 src/display/review.ts create mode 100644 src/display/signature.ts create mode 100644 src/display/state.ts create mode 100644 src/display/tool-view.ts create mode 100644 src/display/types.ts create mode 100644 src/generated/app-server/AbsolutePathBuf.ts create mode 100644 src/generated/app-server/AgentPath.ts create mode 100644 src/generated/app-server/ApplyPatchApprovalParams.ts create mode 100644 src/generated/app-server/ApplyPatchApprovalResponse.ts create mode 100644 src/generated/app-server/AuthMode.ts create mode 100644 src/generated/app-server/ClientInfo.ts create mode 100644 src/generated/app-server/ClientNotification.ts create mode 100644 src/generated/app-server/ClientRequest.ts create mode 100644 src/generated/app-server/CollaborationMode.ts create mode 100644 src/generated/app-server/ContentItem.ts create mode 100644 src/generated/app-server/ConversationGitInfo.ts create mode 100644 src/generated/app-server/ConversationSummary.ts create mode 100644 src/generated/app-server/ExecCommandApprovalParams.ts create mode 100644 src/generated/app-server/ExecCommandApprovalResponse.ts create mode 100644 src/generated/app-server/ExecPolicyAmendment.ts create mode 100644 src/generated/app-server/FileChange.ts create mode 100644 src/generated/app-server/ForcedLoginMethod.ts create mode 100644 src/generated/app-server/FunctionCallOutputBody.ts create mode 100644 src/generated/app-server/FunctionCallOutputContentItem.ts create mode 100644 src/generated/app-server/FuzzyFileSearchMatchType.ts create mode 100644 src/generated/app-server/FuzzyFileSearchParams.ts create mode 100644 src/generated/app-server/FuzzyFileSearchResponse.ts create mode 100644 src/generated/app-server/FuzzyFileSearchResult.ts create mode 100644 src/generated/app-server/FuzzyFileSearchSessionCompletedNotification.ts create mode 100644 src/generated/app-server/FuzzyFileSearchSessionStartParams.ts create mode 100644 src/generated/app-server/FuzzyFileSearchSessionStartResponse.ts create mode 100644 src/generated/app-server/FuzzyFileSearchSessionStopParams.ts create mode 100644 src/generated/app-server/FuzzyFileSearchSessionStopResponse.ts create mode 100644 src/generated/app-server/FuzzyFileSearchSessionUpdateParams.ts create mode 100644 src/generated/app-server/FuzzyFileSearchSessionUpdateResponse.ts create mode 100644 src/generated/app-server/FuzzyFileSearchSessionUpdatedNotification.ts create mode 100644 src/generated/app-server/GetAuthStatusParams.ts create mode 100644 src/generated/app-server/GetAuthStatusResponse.ts create mode 100644 src/generated/app-server/GetConversationSummaryParams.ts create mode 100644 src/generated/app-server/GetConversationSummaryResponse.ts create mode 100644 src/generated/app-server/GitDiffToRemoteParams.ts create mode 100644 src/generated/app-server/GitDiffToRemoteResponse.ts create mode 100644 src/generated/app-server/GitSha.ts create mode 100644 src/generated/app-server/ImageDetail.ts create mode 100644 src/generated/app-server/InitializeCapabilities.ts create mode 100644 src/generated/app-server/InitializeParams.ts create mode 100644 src/generated/app-server/InitializeResponse.ts create mode 100644 src/generated/app-server/InputModality.ts create mode 100644 src/generated/app-server/InternalSessionSource.ts create mode 100644 src/generated/app-server/LocalShellAction.ts create mode 100644 src/generated/app-server/LocalShellExecAction.ts create mode 100644 src/generated/app-server/LocalShellStatus.ts create mode 100644 src/generated/app-server/MessagePhase.ts create mode 100644 src/generated/app-server/ModeKind.ts create mode 100644 src/generated/app-server/NetworkPolicyAmendment.ts create mode 100644 src/generated/app-server/NetworkPolicyRuleAction.ts create mode 100644 src/generated/app-server/ParsedCommand.ts create mode 100644 src/generated/app-server/Personality.ts create mode 100644 src/generated/app-server/PlanType.ts create mode 100644 src/generated/app-server/RealtimeConversationVersion.ts create mode 100644 src/generated/app-server/RealtimeOutputModality.ts create mode 100644 src/generated/app-server/RealtimeVoice.ts create mode 100644 src/generated/app-server/RealtimeVoicesList.ts create mode 100644 src/generated/app-server/ReasoningEffort.ts create mode 100644 src/generated/app-server/ReasoningItemContent.ts create mode 100644 src/generated/app-server/ReasoningItemReasoningSummary.ts create mode 100644 src/generated/app-server/ReasoningSummary.ts create mode 100644 src/generated/app-server/RequestId.ts create mode 100644 src/generated/app-server/Resource.ts create mode 100644 src/generated/app-server/ResourceContent.ts create mode 100644 src/generated/app-server/ResourceTemplate.ts create mode 100644 src/generated/app-server/ResponseItem.ts create mode 100644 src/generated/app-server/ReviewDecision.ts create mode 100644 src/generated/app-server/ServerNotification.ts create mode 100644 src/generated/app-server/ServerRequest.ts create mode 100644 src/generated/app-server/SessionSource.ts create mode 100644 src/generated/app-server/Settings.ts create mode 100644 src/generated/app-server/SubAgentSource.ts create mode 100644 src/generated/app-server/ThreadId.ts create mode 100644 src/generated/app-server/ThreadMemoryMode.ts create mode 100644 src/generated/app-server/Tool.ts create mode 100644 src/generated/app-server/Verbosity.ts create mode 100644 src/generated/app-server/WebSearchAction.ts create mode 100644 src/generated/app-server/WebSearchContextSize.ts create mode 100644 src/generated/app-server/WebSearchLocation.ts create mode 100644 src/generated/app-server/WebSearchMode.ts create mode 100644 src/generated/app-server/WebSearchToolConfig.ts create mode 100644 src/generated/app-server/index.ts create mode 100644 src/generated/app-server/serde_json/JsonValue.ts create mode 100644 src/generated/app-server/v2/Account.ts create mode 100644 src/generated/app-server/v2/AccountLoginCompletedNotification.ts create mode 100644 src/generated/app-server/v2/AccountRateLimitsUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/AccountUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/ActivePermissionProfile.ts create mode 100644 src/generated/app-server/v2/ActivePermissionProfileModification.ts create mode 100644 src/generated/app-server/v2/AddCreditsNudgeCreditType.ts create mode 100644 src/generated/app-server/v2/AddCreditsNudgeEmailStatus.ts create mode 100644 src/generated/app-server/v2/AdditionalFileSystemPermissions.ts create mode 100644 src/generated/app-server/v2/AdditionalNetworkPermissions.ts create mode 100644 src/generated/app-server/v2/AdditionalPermissionProfile.ts create mode 100644 src/generated/app-server/v2/AgentMessageDeltaNotification.ts create mode 100644 src/generated/app-server/v2/AnalyticsConfig.ts create mode 100644 src/generated/app-server/v2/AppBranding.ts create mode 100644 src/generated/app-server/v2/AppInfo.ts create mode 100644 src/generated/app-server/v2/AppListUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/AppMetadata.ts create mode 100644 src/generated/app-server/v2/AppReview.ts create mode 100644 src/generated/app-server/v2/AppScreenshot.ts create mode 100644 src/generated/app-server/v2/AppSummary.ts create mode 100644 src/generated/app-server/v2/AppToolApproval.ts create mode 100644 src/generated/app-server/v2/AppToolsConfig.ts create mode 100644 src/generated/app-server/v2/ApprovalsReviewer.ts create mode 100644 src/generated/app-server/v2/AppsConfig.ts create mode 100644 src/generated/app-server/v2/AppsDefaultConfig.ts create mode 100644 src/generated/app-server/v2/AppsListParams.ts create mode 100644 src/generated/app-server/v2/AppsListResponse.ts create mode 100644 src/generated/app-server/v2/AskForApproval.ts create mode 100644 src/generated/app-server/v2/AutoReviewDecisionSource.ts create mode 100644 src/generated/app-server/v2/ByteRange.ts create mode 100644 src/generated/app-server/v2/CancelLoginAccountParams.ts create mode 100644 src/generated/app-server/v2/CancelLoginAccountResponse.ts create mode 100644 src/generated/app-server/v2/CancelLoginAccountStatus.ts create mode 100644 src/generated/app-server/v2/ChatgptAuthTokensRefreshParams.ts create mode 100644 src/generated/app-server/v2/ChatgptAuthTokensRefreshReason.ts create mode 100644 src/generated/app-server/v2/ChatgptAuthTokensRefreshResponse.ts create mode 100644 src/generated/app-server/v2/CodexErrorInfo.ts create mode 100644 src/generated/app-server/v2/CollabAgentState.ts create mode 100644 src/generated/app-server/v2/CollabAgentStatus.ts create mode 100644 src/generated/app-server/v2/CollabAgentTool.ts create mode 100644 src/generated/app-server/v2/CollabAgentToolCallStatus.ts create mode 100644 src/generated/app-server/v2/CollaborationModeListParams.ts create mode 100644 src/generated/app-server/v2/CollaborationModeListResponse.ts create mode 100644 src/generated/app-server/v2/CollaborationModeMask.ts create mode 100644 src/generated/app-server/v2/CommandAction.ts create mode 100644 src/generated/app-server/v2/CommandExecOutputDeltaNotification.ts create mode 100644 src/generated/app-server/v2/CommandExecOutputStream.ts create mode 100644 src/generated/app-server/v2/CommandExecParams.ts create mode 100644 src/generated/app-server/v2/CommandExecResizeParams.ts create mode 100644 src/generated/app-server/v2/CommandExecResizeResponse.ts create mode 100644 src/generated/app-server/v2/CommandExecResponse.ts create mode 100644 src/generated/app-server/v2/CommandExecTerminalSize.ts create mode 100644 src/generated/app-server/v2/CommandExecTerminateParams.ts create mode 100644 src/generated/app-server/v2/CommandExecTerminateResponse.ts create mode 100644 src/generated/app-server/v2/CommandExecWriteParams.ts create mode 100644 src/generated/app-server/v2/CommandExecWriteResponse.ts create mode 100644 src/generated/app-server/v2/CommandExecutionApprovalDecision.ts create mode 100644 src/generated/app-server/v2/CommandExecutionOutputDeltaNotification.ts create mode 100644 src/generated/app-server/v2/CommandExecutionRequestApprovalParams.ts create mode 100644 src/generated/app-server/v2/CommandExecutionRequestApprovalResponse.ts create mode 100644 src/generated/app-server/v2/CommandExecutionSource.ts create mode 100644 src/generated/app-server/v2/CommandExecutionStatus.ts create mode 100644 src/generated/app-server/v2/CommandMigration.ts create mode 100644 src/generated/app-server/v2/Config.ts create mode 100644 src/generated/app-server/v2/ConfigBatchWriteParams.ts create mode 100644 src/generated/app-server/v2/ConfigEdit.ts create mode 100644 src/generated/app-server/v2/ConfigLayer.ts create mode 100644 src/generated/app-server/v2/ConfigLayerMetadata.ts create mode 100644 src/generated/app-server/v2/ConfigLayerSource.ts create mode 100644 src/generated/app-server/v2/ConfigReadParams.ts create mode 100644 src/generated/app-server/v2/ConfigReadResponse.ts create mode 100644 src/generated/app-server/v2/ConfigRequirements.ts create mode 100644 src/generated/app-server/v2/ConfigRequirementsReadResponse.ts create mode 100644 src/generated/app-server/v2/ConfigValueWriteParams.ts create mode 100644 src/generated/app-server/v2/ConfigWarningNotification.ts create mode 100644 src/generated/app-server/v2/ConfigWriteResponse.ts create mode 100644 src/generated/app-server/v2/ConfiguredHookHandler.ts create mode 100644 src/generated/app-server/v2/ConfiguredHookMatcherGroup.ts create mode 100644 src/generated/app-server/v2/ContextCompactedNotification.ts create mode 100644 src/generated/app-server/v2/CreditsSnapshot.ts create mode 100644 src/generated/app-server/v2/DeprecationNoticeNotification.ts create mode 100644 src/generated/app-server/v2/DeviceKeyAlgorithm.ts create mode 100644 src/generated/app-server/v2/DeviceKeyCreateParams.ts create mode 100644 src/generated/app-server/v2/DeviceKeyCreateResponse.ts create mode 100644 src/generated/app-server/v2/DeviceKeyProtectionClass.ts create mode 100644 src/generated/app-server/v2/DeviceKeyProtectionPolicy.ts create mode 100644 src/generated/app-server/v2/DeviceKeyPublicParams.ts create mode 100644 src/generated/app-server/v2/DeviceKeyPublicResponse.ts create mode 100644 src/generated/app-server/v2/DeviceKeySignParams.ts create mode 100644 src/generated/app-server/v2/DeviceKeySignPayload.ts create mode 100644 src/generated/app-server/v2/DeviceKeySignResponse.ts create mode 100644 src/generated/app-server/v2/DynamicToolCallOutputContentItem.ts create mode 100644 src/generated/app-server/v2/DynamicToolCallParams.ts create mode 100644 src/generated/app-server/v2/DynamicToolCallResponse.ts create mode 100644 src/generated/app-server/v2/DynamicToolCallStatus.ts create mode 100644 src/generated/app-server/v2/DynamicToolSpec.ts create mode 100644 src/generated/app-server/v2/ErrorNotification.ts create mode 100644 src/generated/app-server/v2/ExecPolicyAmendment.ts create mode 100644 src/generated/app-server/v2/ExperimentalFeature.ts create mode 100644 src/generated/app-server/v2/ExperimentalFeatureEnablementSetParams.ts create mode 100644 src/generated/app-server/v2/ExperimentalFeatureEnablementSetResponse.ts create mode 100644 src/generated/app-server/v2/ExperimentalFeatureListParams.ts create mode 100644 src/generated/app-server/v2/ExperimentalFeatureListResponse.ts create mode 100644 src/generated/app-server/v2/ExperimentalFeatureStage.ts create mode 100644 src/generated/app-server/v2/ExternalAgentConfigDetectParams.ts create mode 100644 src/generated/app-server/v2/ExternalAgentConfigDetectResponse.ts create mode 100644 src/generated/app-server/v2/ExternalAgentConfigImportCompletedNotification.ts create mode 100644 src/generated/app-server/v2/ExternalAgentConfigImportParams.ts create mode 100644 src/generated/app-server/v2/ExternalAgentConfigImportResponse.ts create mode 100644 src/generated/app-server/v2/ExternalAgentConfigMigrationItem.ts create mode 100644 src/generated/app-server/v2/ExternalAgentConfigMigrationItemType.ts create mode 100644 src/generated/app-server/v2/FeedbackUploadParams.ts create mode 100644 src/generated/app-server/v2/FeedbackUploadResponse.ts create mode 100644 src/generated/app-server/v2/FileChangeApprovalDecision.ts create mode 100644 src/generated/app-server/v2/FileChangeOutputDeltaNotification.ts create mode 100644 src/generated/app-server/v2/FileChangePatchUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/FileChangeRequestApprovalParams.ts create mode 100644 src/generated/app-server/v2/FileChangeRequestApprovalResponse.ts create mode 100644 src/generated/app-server/v2/FileSystemAccessMode.ts create mode 100644 src/generated/app-server/v2/FileSystemPath.ts create mode 100644 src/generated/app-server/v2/FileSystemSandboxEntry.ts create mode 100644 src/generated/app-server/v2/FileSystemSpecialPath.ts create mode 100644 src/generated/app-server/v2/FileUpdateChange.ts create mode 100644 src/generated/app-server/v2/FsChangedNotification.ts create mode 100644 src/generated/app-server/v2/FsCopyParams.ts create mode 100644 src/generated/app-server/v2/FsCopyResponse.ts create mode 100644 src/generated/app-server/v2/FsCreateDirectoryParams.ts create mode 100644 src/generated/app-server/v2/FsCreateDirectoryResponse.ts create mode 100644 src/generated/app-server/v2/FsGetMetadataParams.ts create mode 100644 src/generated/app-server/v2/FsGetMetadataResponse.ts create mode 100644 src/generated/app-server/v2/FsReadDirectoryEntry.ts create mode 100644 src/generated/app-server/v2/FsReadDirectoryParams.ts create mode 100644 src/generated/app-server/v2/FsReadDirectoryResponse.ts create mode 100644 src/generated/app-server/v2/FsReadFileParams.ts create mode 100644 src/generated/app-server/v2/FsReadFileResponse.ts create mode 100644 src/generated/app-server/v2/FsRemoveParams.ts create mode 100644 src/generated/app-server/v2/FsRemoveResponse.ts create mode 100644 src/generated/app-server/v2/FsUnwatchParams.ts create mode 100644 src/generated/app-server/v2/FsUnwatchResponse.ts create mode 100644 src/generated/app-server/v2/FsWatchParams.ts create mode 100644 src/generated/app-server/v2/FsWatchResponse.ts create mode 100644 src/generated/app-server/v2/FsWriteFileParams.ts create mode 100644 src/generated/app-server/v2/FsWriteFileResponse.ts create mode 100644 src/generated/app-server/v2/GetAccountParams.ts create mode 100644 src/generated/app-server/v2/GetAccountRateLimitsResponse.ts create mode 100644 src/generated/app-server/v2/GetAccountResponse.ts create mode 100644 src/generated/app-server/v2/GitInfo.ts create mode 100644 src/generated/app-server/v2/GrantedPermissionProfile.ts create mode 100644 src/generated/app-server/v2/GuardianApprovalReview.ts create mode 100644 src/generated/app-server/v2/GuardianApprovalReviewAction.ts create mode 100644 src/generated/app-server/v2/GuardianApprovalReviewStatus.ts create mode 100644 src/generated/app-server/v2/GuardianCommandSource.ts create mode 100644 src/generated/app-server/v2/GuardianRiskLevel.ts create mode 100644 src/generated/app-server/v2/GuardianUserAuthorization.ts create mode 100644 src/generated/app-server/v2/GuardianWarningNotification.ts create mode 100644 src/generated/app-server/v2/HookCompletedNotification.ts create mode 100644 src/generated/app-server/v2/HookErrorInfo.ts create mode 100644 src/generated/app-server/v2/HookEventName.ts create mode 100644 src/generated/app-server/v2/HookExecutionMode.ts create mode 100644 src/generated/app-server/v2/HookHandlerType.ts create mode 100644 src/generated/app-server/v2/HookMetadata.ts create mode 100644 src/generated/app-server/v2/HookMigration.ts create mode 100644 src/generated/app-server/v2/HookOutputEntry.ts create mode 100644 src/generated/app-server/v2/HookOutputEntryKind.ts create mode 100644 src/generated/app-server/v2/HookPromptFragment.ts create mode 100644 src/generated/app-server/v2/HookRunStatus.ts create mode 100644 src/generated/app-server/v2/HookRunSummary.ts create mode 100644 src/generated/app-server/v2/HookScope.ts create mode 100644 src/generated/app-server/v2/HookSource.ts create mode 100644 src/generated/app-server/v2/HookStartedNotification.ts create mode 100644 src/generated/app-server/v2/HookTrustStatus.ts create mode 100644 src/generated/app-server/v2/HooksListEntry.ts create mode 100644 src/generated/app-server/v2/HooksListParams.ts create mode 100644 src/generated/app-server/v2/HooksListResponse.ts create mode 100644 src/generated/app-server/v2/ItemCompletedNotification.ts create mode 100644 src/generated/app-server/v2/ItemGuardianApprovalReviewCompletedNotification.ts create mode 100644 src/generated/app-server/v2/ItemGuardianApprovalReviewStartedNotification.ts create mode 100644 src/generated/app-server/v2/ItemStartedNotification.ts create mode 100644 src/generated/app-server/v2/ListMcpServerStatusParams.ts create mode 100644 src/generated/app-server/v2/ListMcpServerStatusResponse.ts create mode 100644 src/generated/app-server/v2/LoginAccountParams.ts create mode 100644 src/generated/app-server/v2/LoginAccountResponse.ts create mode 100644 src/generated/app-server/v2/LogoutAccountResponse.ts create mode 100644 src/generated/app-server/v2/ManagedHooksRequirements.ts create mode 100644 src/generated/app-server/v2/MarketplaceAddParams.ts create mode 100644 src/generated/app-server/v2/MarketplaceAddResponse.ts create mode 100644 src/generated/app-server/v2/MarketplaceInterface.ts create mode 100644 src/generated/app-server/v2/MarketplaceLoadErrorInfo.ts create mode 100644 src/generated/app-server/v2/MarketplaceRemoveParams.ts create mode 100644 src/generated/app-server/v2/MarketplaceRemoveResponse.ts create mode 100644 src/generated/app-server/v2/MarketplaceUpgradeErrorInfo.ts create mode 100644 src/generated/app-server/v2/MarketplaceUpgradeParams.ts create mode 100644 src/generated/app-server/v2/MarketplaceUpgradeResponse.ts create mode 100644 src/generated/app-server/v2/McpAuthStatus.ts create mode 100644 src/generated/app-server/v2/McpElicitationArrayType.ts create mode 100644 src/generated/app-server/v2/McpElicitationBooleanSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationBooleanType.ts create mode 100644 src/generated/app-server/v2/McpElicitationConstOption.ts create mode 100644 src/generated/app-server/v2/McpElicitationEnumSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationLegacyTitledEnumSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationMultiSelectEnumSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationNumberSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationNumberType.ts create mode 100644 src/generated/app-server/v2/McpElicitationObjectType.ts create mode 100644 src/generated/app-server/v2/McpElicitationPrimitiveSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationSingleSelectEnumSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationStringFormat.ts create mode 100644 src/generated/app-server/v2/McpElicitationStringSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationStringType.ts create mode 100644 src/generated/app-server/v2/McpElicitationTitledEnumItems.ts create mode 100644 src/generated/app-server/v2/McpElicitationTitledMultiSelectEnumSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationTitledSingleSelectEnumSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationUntitledEnumItems.ts create mode 100644 src/generated/app-server/v2/McpElicitationUntitledMultiSelectEnumSchema.ts create mode 100644 src/generated/app-server/v2/McpElicitationUntitledSingleSelectEnumSchema.ts create mode 100644 src/generated/app-server/v2/McpResourceReadParams.ts create mode 100644 src/generated/app-server/v2/McpResourceReadResponse.ts create mode 100644 src/generated/app-server/v2/McpServerElicitationAction.ts create mode 100644 src/generated/app-server/v2/McpServerElicitationRequestParams.ts create mode 100644 src/generated/app-server/v2/McpServerElicitationRequestResponse.ts create mode 100644 src/generated/app-server/v2/McpServerMigration.ts create mode 100644 src/generated/app-server/v2/McpServerOauthLoginCompletedNotification.ts create mode 100644 src/generated/app-server/v2/McpServerOauthLoginParams.ts create mode 100644 src/generated/app-server/v2/McpServerOauthLoginResponse.ts create mode 100644 src/generated/app-server/v2/McpServerRefreshResponse.ts create mode 100644 src/generated/app-server/v2/McpServerStartupState.ts create mode 100644 src/generated/app-server/v2/McpServerStatus.ts create mode 100644 src/generated/app-server/v2/McpServerStatusDetail.ts create mode 100644 src/generated/app-server/v2/McpServerStatusUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/McpServerToolCallParams.ts create mode 100644 src/generated/app-server/v2/McpServerToolCallResponse.ts create mode 100644 src/generated/app-server/v2/McpToolCallError.ts create mode 100644 src/generated/app-server/v2/McpToolCallProgressNotification.ts create mode 100644 src/generated/app-server/v2/McpToolCallResult.ts create mode 100644 src/generated/app-server/v2/McpToolCallStatus.ts create mode 100644 src/generated/app-server/v2/MemoryCitation.ts create mode 100644 src/generated/app-server/v2/MemoryCitationEntry.ts create mode 100644 src/generated/app-server/v2/MemoryResetResponse.ts create mode 100644 src/generated/app-server/v2/MergeStrategy.ts create mode 100644 src/generated/app-server/v2/MigrationDetails.ts create mode 100644 src/generated/app-server/v2/MockExperimentalMethodParams.ts create mode 100644 src/generated/app-server/v2/MockExperimentalMethodResponse.ts create mode 100644 src/generated/app-server/v2/Model.ts create mode 100644 src/generated/app-server/v2/ModelAvailabilityNux.ts create mode 100644 src/generated/app-server/v2/ModelListParams.ts create mode 100644 src/generated/app-server/v2/ModelListResponse.ts create mode 100644 src/generated/app-server/v2/ModelProviderCapabilitiesReadParams.ts create mode 100644 src/generated/app-server/v2/ModelProviderCapabilitiesReadResponse.ts create mode 100644 src/generated/app-server/v2/ModelRerouteReason.ts create mode 100644 src/generated/app-server/v2/ModelReroutedNotification.ts create mode 100644 src/generated/app-server/v2/ModelServiceTier.ts create mode 100644 src/generated/app-server/v2/ModelUpgradeInfo.ts create mode 100644 src/generated/app-server/v2/ModelVerification.ts create mode 100644 src/generated/app-server/v2/ModelVerificationNotification.ts create mode 100644 src/generated/app-server/v2/NetworkAccess.ts create mode 100644 src/generated/app-server/v2/NetworkApprovalContext.ts create mode 100644 src/generated/app-server/v2/NetworkApprovalProtocol.ts create mode 100644 src/generated/app-server/v2/NetworkDomainPermission.ts create mode 100644 src/generated/app-server/v2/NetworkPolicyAmendment.ts create mode 100644 src/generated/app-server/v2/NetworkPolicyRuleAction.ts create mode 100644 src/generated/app-server/v2/NetworkRequirements.ts create mode 100644 src/generated/app-server/v2/NetworkUnixSocketPermission.ts create mode 100644 src/generated/app-server/v2/NonSteerableTurnKind.ts create mode 100644 src/generated/app-server/v2/OverriddenMetadata.ts create mode 100644 src/generated/app-server/v2/PatchApplyStatus.ts create mode 100644 src/generated/app-server/v2/PatchChangeKind.ts create mode 100644 src/generated/app-server/v2/PermissionGrantScope.ts create mode 100644 src/generated/app-server/v2/PermissionProfile.ts create mode 100644 src/generated/app-server/v2/PermissionProfileFileSystemPermissions.ts create mode 100644 src/generated/app-server/v2/PermissionProfileModificationParams.ts create mode 100644 src/generated/app-server/v2/PermissionProfileNetworkPermissions.ts create mode 100644 src/generated/app-server/v2/PermissionProfileSelectionParams.ts create mode 100644 src/generated/app-server/v2/PermissionsRequestApprovalParams.ts create mode 100644 src/generated/app-server/v2/PermissionsRequestApprovalResponse.ts create mode 100644 src/generated/app-server/v2/PlanDeltaNotification.ts create mode 100644 src/generated/app-server/v2/PluginAuthPolicy.ts create mode 100644 src/generated/app-server/v2/PluginAvailability.ts create mode 100644 src/generated/app-server/v2/PluginDetail.ts create mode 100644 src/generated/app-server/v2/PluginHookSummary.ts create mode 100644 src/generated/app-server/v2/PluginInstallParams.ts create mode 100644 src/generated/app-server/v2/PluginInstallPolicy.ts create mode 100644 src/generated/app-server/v2/PluginInstallResponse.ts create mode 100644 src/generated/app-server/v2/PluginInterface.ts create mode 100644 src/generated/app-server/v2/PluginListMarketplaceKind.ts create mode 100644 src/generated/app-server/v2/PluginListParams.ts create mode 100644 src/generated/app-server/v2/PluginListResponse.ts create mode 100644 src/generated/app-server/v2/PluginMarketplaceEntry.ts create mode 100644 src/generated/app-server/v2/PluginReadParams.ts create mode 100644 src/generated/app-server/v2/PluginReadResponse.ts create mode 100644 src/generated/app-server/v2/PluginShareContext.ts create mode 100644 src/generated/app-server/v2/PluginShareDeleteParams.ts create mode 100644 src/generated/app-server/v2/PluginShareDeleteResponse.ts create mode 100644 src/generated/app-server/v2/PluginShareDiscoverability.ts create mode 100644 src/generated/app-server/v2/PluginShareListItem.ts create mode 100644 src/generated/app-server/v2/PluginShareListParams.ts create mode 100644 src/generated/app-server/v2/PluginShareListResponse.ts create mode 100644 src/generated/app-server/v2/PluginSharePrincipal.ts create mode 100644 src/generated/app-server/v2/PluginSharePrincipalType.ts create mode 100644 src/generated/app-server/v2/PluginShareSaveParams.ts create mode 100644 src/generated/app-server/v2/PluginShareSaveResponse.ts create mode 100644 src/generated/app-server/v2/PluginShareTarget.ts create mode 100644 src/generated/app-server/v2/PluginShareUpdateDiscoverability.ts create mode 100644 src/generated/app-server/v2/PluginShareUpdateTargetsParams.ts create mode 100644 src/generated/app-server/v2/PluginShareUpdateTargetsResponse.ts create mode 100644 src/generated/app-server/v2/PluginSkillReadParams.ts create mode 100644 src/generated/app-server/v2/PluginSkillReadResponse.ts create mode 100644 src/generated/app-server/v2/PluginSource.ts create mode 100644 src/generated/app-server/v2/PluginSummary.ts create mode 100644 src/generated/app-server/v2/PluginUninstallParams.ts create mode 100644 src/generated/app-server/v2/PluginUninstallResponse.ts create mode 100644 src/generated/app-server/v2/PluginsMigration.ts create mode 100644 src/generated/app-server/v2/ProcessExitedNotification.ts create mode 100644 src/generated/app-server/v2/ProcessKillParams.ts create mode 100644 src/generated/app-server/v2/ProcessKillResponse.ts create mode 100644 src/generated/app-server/v2/ProcessOutputDeltaNotification.ts create mode 100644 src/generated/app-server/v2/ProcessOutputStream.ts create mode 100644 src/generated/app-server/v2/ProcessResizePtyParams.ts create mode 100644 src/generated/app-server/v2/ProcessResizePtyResponse.ts create mode 100644 src/generated/app-server/v2/ProcessSpawnParams.ts create mode 100644 src/generated/app-server/v2/ProcessSpawnResponse.ts create mode 100644 src/generated/app-server/v2/ProcessTerminalSize.ts create mode 100644 src/generated/app-server/v2/ProcessWriteStdinParams.ts create mode 100644 src/generated/app-server/v2/ProcessWriteStdinResponse.ts create mode 100644 src/generated/app-server/v2/ProfileV2.ts create mode 100644 src/generated/app-server/v2/RateLimitReachedType.ts create mode 100644 src/generated/app-server/v2/RateLimitSnapshot.ts create mode 100644 src/generated/app-server/v2/RateLimitWindow.ts create mode 100644 src/generated/app-server/v2/RawResponseItemCompletedNotification.ts create mode 100644 src/generated/app-server/v2/ReasoningEffortOption.ts create mode 100644 src/generated/app-server/v2/ReasoningSummaryPartAddedNotification.ts create mode 100644 src/generated/app-server/v2/ReasoningSummaryTextDeltaNotification.ts create mode 100644 src/generated/app-server/v2/ReasoningTextDeltaNotification.ts create mode 100644 src/generated/app-server/v2/RemoteControlClientConnectionAudience.ts create mode 100644 src/generated/app-server/v2/RemoteControlClientEnrollmentAudience.ts create mode 100644 src/generated/app-server/v2/RemoteControlConnectionStatus.ts create mode 100644 src/generated/app-server/v2/RemoteControlStatusChangedNotification.ts create mode 100644 src/generated/app-server/v2/RequestPermissionProfile.ts create mode 100644 src/generated/app-server/v2/ResidencyRequirement.ts create mode 100644 src/generated/app-server/v2/ReviewDelivery.ts create mode 100644 src/generated/app-server/v2/ReviewStartParams.ts create mode 100644 src/generated/app-server/v2/ReviewStartResponse.ts create mode 100644 src/generated/app-server/v2/ReviewTarget.ts create mode 100644 src/generated/app-server/v2/SandboxMode.ts create mode 100644 src/generated/app-server/v2/SandboxPolicy.ts create mode 100644 src/generated/app-server/v2/SandboxWorkspaceWrite.ts create mode 100644 src/generated/app-server/v2/SendAddCreditsNudgeEmailParams.ts create mode 100644 src/generated/app-server/v2/SendAddCreditsNudgeEmailResponse.ts create mode 100644 src/generated/app-server/v2/ServerRequestResolvedNotification.ts create mode 100644 src/generated/app-server/v2/SessionMigration.ts create mode 100644 src/generated/app-server/v2/SessionSource.ts create mode 100644 src/generated/app-server/v2/SkillDependencies.ts create mode 100644 src/generated/app-server/v2/SkillErrorInfo.ts create mode 100644 src/generated/app-server/v2/SkillInterface.ts create mode 100644 src/generated/app-server/v2/SkillMetadata.ts create mode 100644 src/generated/app-server/v2/SkillScope.ts create mode 100644 src/generated/app-server/v2/SkillSummary.ts create mode 100644 src/generated/app-server/v2/SkillToolDependency.ts create mode 100644 src/generated/app-server/v2/SkillsChangedNotification.ts create mode 100644 src/generated/app-server/v2/SkillsConfigWriteParams.ts create mode 100644 src/generated/app-server/v2/SkillsConfigWriteResponse.ts create mode 100644 src/generated/app-server/v2/SkillsListEntry.ts create mode 100644 src/generated/app-server/v2/SkillsListExtraRootsForCwd.ts create mode 100644 src/generated/app-server/v2/SkillsListParams.ts create mode 100644 src/generated/app-server/v2/SkillsListResponse.ts create mode 100644 src/generated/app-server/v2/SortDirection.ts create mode 100644 src/generated/app-server/v2/SubagentMigration.ts create mode 100644 src/generated/app-server/v2/TerminalInteractionNotification.ts create mode 100644 src/generated/app-server/v2/TextElement.ts create mode 100644 src/generated/app-server/v2/TextPosition.ts create mode 100644 src/generated/app-server/v2/TextRange.ts create mode 100644 src/generated/app-server/v2/Thread.ts create mode 100644 src/generated/app-server/v2/ThreadActiveFlag.ts create mode 100644 src/generated/app-server/v2/ThreadApproveGuardianDeniedActionParams.ts create mode 100644 src/generated/app-server/v2/ThreadApproveGuardianDeniedActionResponse.ts create mode 100644 src/generated/app-server/v2/ThreadArchiveParams.ts create mode 100644 src/generated/app-server/v2/ThreadArchiveResponse.ts create mode 100644 src/generated/app-server/v2/ThreadArchivedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadBackgroundTerminalsCleanParams.ts create mode 100644 src/generated/app-server/v2/ThreadBackgroundTerminalsCleanResponse.ts create mode 100644 src/generated/app-server/v2/ThreadClosedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadCompactStartParams.ts create mode 100644 src/generated/app-server/v2/ThreadCompactStartResponse.ts create mode 100644 src/generated/app-server/v2/ThreadDecrementElicitationParams.ts create mode 100644 src/generated/app-server/v2/ThreadDecrementElicitationResponse.ts create mode 100644 src/generated/app-server/v2/ThreadForkParams.ts create mode 100644 src/generated/app-server/v2/ThreadForkResponse.ts create mode 100644 src/generated/app-server/v2/ThreadGoal.ts create mode 100644 src/generated/app-server/v2/ThreadGoalClearParams.ts create mode 100644 src/generated/app-server/v2/ThreadGoalClearResponse.ts create mode 100644 src/generated/app-server/v2/ThreadGoalClearedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadGoalGetParams.ts create mode 100644 src/generated/app-server/v2/ThreadGoalGetResponse.ts create mode 100644 src/generated/app-server/v2/ThreadGoalSetParams.ts create mode 100644 src/generated/app-server/v2/ThreadGoalSetResponse.ts create mode 100644 src/generated/app-server/v2/ThreadGoalStatus.ts create mode 100644 src/generated/app-server/v2/ThreadGoalUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadIncrementElicitationParams.ts create mode 100644 src/generated/app-server/v2/ThreadIncrementElicitationResponse.ts create mode 100644 src/generated/app-server/v2/ThreadInjectItemsParams.ts create mode 100644 src/generated/app-server/v2/ThreadInjectItemsResponse.ts create mode 100644 src/generated/app-server/v2/ThreadItem.ts create mode 100644 src/generated/app-server/v2/ThreadListParams.ts create mode 100644 src/generated/app-server/v2/ThreadListResponse.ts create mode 100644 src/generated/app-server/v2/ThreadLoadedListParams.ts create mode 100644 src/generated/app-server/v2/ThreadLoadedListResponse.ts create mode 100644 src/generated/app-server/v2/ThreadMemoryModeSetParams.ts create mode 100644 src/generated/app-server/v2/ThreadMemoryModeSetResponse.ts create mode 100644 src/generated/app-server/v2/ThreadMetadataGitInfoUpdateParams.ts create mode 100644 src/generated/app-server/v2/ThreadMetadataUpdateParams.ts create mode 100644 src/generated/app-server/v2/ThreadMetadataUpdateResponse.ts create mode 100644 src/generated/app-server/v2/ThreadNameUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadReadParams.ts create mode 100644 src/generated/app-server/v2/ThreadReadResponse.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeAppendAudioParams.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeAppendAudioResponse.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeAppendTextParams.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeAppendTextResponse.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeAudioChunk.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeClosedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeErrorNotification.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeItemAddedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeListVoicesParams.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeListVoicesResponse.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeOutputAudioDeltaNotification.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeSdpNotification.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeStartParams.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeStartResponse.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeStartTransport.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeStartedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeStopParams.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeStopResponse.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeTranscriptDeltaNotification.ts create mode 100644 src/generated/app-server/v2/ThreadRealtimeTranscriptDoneNotification.ts create mode 100644 src/generated/app-server/v2/ThreadResumeParams.ts create mode 100644 src/generated/app-server/v2/ThreadResumeResponse.ts create mode 100644 src/generated/app-server/v2/ThreadRollbackParams.ts create mode 100644 src/generated/app-server/v2/ThreadRollbackResponse.ts create mode 100644 src/generated/app-server/v2/ThreadSetNameParams.ts create mode 100644 src/generated/app-server/v2/ThreadSetNameResponse.ts create mode 100644 src/generated/app-server/v2/ThreadShellCommandParams.ts create mode 100644 src/generated/app-server/v2/ThreadShellCommandResponse.ts create mode 100644 src/generated/app-server/v2/ThreadSortKey.ts create mode 100644 src/generated/app-server/v2/ThreadSource.ts create mode 100644 src/generated/app-server/v2/ThreadSourceKind.ts create mode 100644 src/generated/app-server/v2/ThreadStartParams.ts create mode 100644 src/generated/app-server/v2/ThreadStartResponse.ts create mode 100644 src/generated/app-server/v2/ThreadStartSource.ts create mode 100644 src/generated/app-server/v2/ThreadStartedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadStatus.ts create mode 100644 src/generated/app-server/v2/ThreadStatusChangedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadTokenUsage.ts create mode 100644 src/generated/app-server/v2/ThreadTokenUsageUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadTurnsItemsListParams.ts create mode 100644 src/generated/app-server/v2/ThreadTurnsItemsListResponse.ts create mode 100644 src/generated/app-server/v2/ThreadTurnsListParams.ts create mode 100644 src/generated/app-server/v2/ThreadTurnsListResponse.ts create mode 100644 src/generated/app-server/v2/ThreadUnarchiveParams.ts create mode 100644 src/generated/app-server/v2/ThreadUnarchiveResponse.ts create mode 100644 src/generated/app-server/v2/ThreadUnarchivedNotification.ts create mode 100644 src/generated/app-server/v2/ThreadUnsubscribeParams.ts create mode 100644 src/generated/app-server/v2/ThreadUnsubscribeResponse.ts create mode 100644 src/generated/app-server/v2/ThreadUnsubscribeStatus.ts create mode 100644 src/generated/app-server/v2/TokenUsageBreakdown.ts create mode 100644 src/generated/app-server/v2/ToolRequestUserInputAnswer.ts create mode 100644 src/generated/app-server/v2/ToolRequestUserInputOption.ts create mode 100644 src/generated/app-server/v2/ToolRequestUserInputParams.ts create mode 100644 src/generated/app-server/v2/ToolRequestUserInputQuestion.ts create mode 100644 src/generated/app-server/v2/ToolRequestUserInputResponse.ts create mode 100644 src/generated/app-server/v2/ToolsV2.ts create mode 100644 src/generated/app-server/v2/Turn.ts create mode 100644 src/generated/app-server/v2/TurnCompletedNotification.ts create mode 100644 src/generated/app-server/v2/TurnDiffUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/TurnEnvironmentParams.ts create mode 100644 src/generated/app-server/v2/TurnError.ts create mode 100644 src/generated/app-server/v2/TurnInterruptParams.ts create mode 100644 src/generated/app-server/v2/TurnInterruptResponse.ts create mode 100644 src/generated/app-server/v2/TurnItemsView.ts create mode 100644 src/generated/app-server/v2/TurnPlanStep.ts create mode 100644 src/generated/app-server/v2/TurnPlanStepStatus.ts create mode 100644 src/generated/app-server/v2/TurnPlanUpdatedNotification.ts create mode 100644 src/generated/app-server/v2/TurnStartParams.ts create mode 100644 src/generated/app-server/v2/TurnStartResponse.ts create mode 100644 src/generated/app-server/v2/TurnStartedNotification.ts create mode 100644 src/generated/app-server/v2/TurnStatus.ts create mode 100644 src/generated/app-server/v2/TurnSteerParams.ts create mode 100644 src/generated/app-server/v2/TurnSteerResponse.ts create mode 100644 src/generated/app-server/v2/UserInput.ts create mode 100644 src/generated/app-server/v2/WarningNotification.ts create mode 100644 src/generated/app-server/v2/WebSearchAction.ts create mode 100644 src/generated/app-server/v2/WindowsSandboxReadiness.ts create mode 100644 src/generated/app-server/v2/WindowsSandboxReadinessResponse.ts create mode 100644 src/generated/app-server/v2/WindowsSandboxSetupCompletedNotification.ts create mode 100644 src/generated/app-server/v2/WindowsSandboxSetupMode.ts create mode 100644 src/generated/app-server/v2/WindowsSandboxSetupStartParams.ts create mode 100644 src/generated/app-server/v2/WindowsSandboxSetupStartResponse.ts create mode 100644 src/generated/app-server/v2/WindowsWorldWritableWarningNotification.ts create mode 100644 src/generated/app-server/v2/WriteStatus.ts create mode 100644 src/generated/app-server/v2/index.ts create mode 100644 src/main.ts create mode 100644 src/panel/app-server-logs.ts create mode 100644 src/panel/collaboration-mode.ts create mode 100644 src/panel/controller.ts create mode 100644 src/panel/diagnostics.ts create mode 100644 src/panel/hook-display.ts create mode 100644 src/panel/model-runtime.ts create mode 100644 src/panel/request-state.ts create mode 100644 src/panel/runtime-settings.ts create mode 100644 src/panel/runtime-state.ts create mode 100644 src/panel/runtime-view.ts create mode 100644 src/panel/session-controller.ts create mode 100644 src/panel/slash-commands.ts create mode 100644 src/panel/thread-history.ts create mode 100644 src/panel/thread-naming.ts create mode 100644 src/panel/thread-rename.ts create mode 100644 src/settings-data.ts create mode 100644 src/settings-tab.ts create mode 100644 src/settings.ts create mode 100644 src/state/panel-state.ts create mode 100644 src/threads.ts create mode 100644 src/user-input/model.ts create mode 100644 src/utils.ts create mode 100644 src/view/components.ts create mode 100644 src/view/composer.ts create mode 100644 src/view/config.ts create mode 100644 src/view/dom.ts create mode 100644 src/view/execution-state.ts create mode 100644 src/view/message-stream.ts create mode 100644 src/view/pending-request-message.ts create mode 100644 src/view/scroll.ts create mode 100644 src/view/tool-result.ts create mode 100644 src/view/toolbar.ts create mode 100644 src/view/work-items.ts create mode 100644 src/view/work-message.ts create mode 100644 styles.css create mode 100644 tests/app-server-client.test.ts create mode 100644 tests/app-server-compatibility.test.ts create mode 100644 tests/app-server-logs.test.ts create mode 100644 tests/approvals.test.ts create mode 100644 tests/collaboration-mode.test.ts create mode 100644 tests/composer-suggestions.test.ts create mode 100644 tests/connection-manager.test.ts create mode 100644 tests/display-model.test.ts create mode 100644 tests/mocks/obsidian.ts create mode 100644 tests/panel-controller.test.ts create mode 100644 tests/runtime-settings.test.ts create mode 100644 tests/service-tier.test.ts create mode 100644 tests/settings-tab.test.ts create mode 100644 tests/settings.test.ts create mode 100644 tests/slash-commands.test.ts create mode 100644 tests/thread-naming.test.ts create mode 100644 tests/user-input.test.ts create mode 100644 tests/view-dom.test.ts create mode 100644 tests/view-renderers.test.ts create mode 100644 tests/view-scroll.test.ts create mode 100644 tests/wikilink-context.test.ts create mode 100644 tsconfig.json create mode 100644 versions.json create mode 100644 vitest.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..444a9551 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +main.js +data.json +*.log +.DS_Store +dist/ +tmp/ diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..e8e8f812 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "printWidth": 140, + "trailingComma": "all" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..7b2c1d79 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 murashit + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..fc13ed56 --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +# Codex Panel + +Codex Panel is a desktop-only Obsidian plugin that opens a right-sidebar panel for Codex. It starts `codex app-server` locally over stdio and uses the current vault root as the Codex working directory. + +The plugin does not manage Codex runtime policy itself. Model, reasoning effort, sandbox, approvals, network access, MCP, hooks, and related behavior are resolved by Codex for the vault root, including a vault-local `.codex/config.toml` when present. + +## Status + +Codex Panel is currently distributed as a beta plugin for BRAT/manual installation. It is developed and tested with Codex CLI 0.130.0. + +The plugin depends on the experimental `codex app-server` API. Later Codex CLI releases may require regenerated app-server bindings or compatibility fixes. + +## Requirements + +- Obsidian desktop app. +- Codex CLI installed, authenticated, and available as `codex`, or a custom executable path configured in the plugin settings. +- A vault where Codex is allowed to work according to your Codex CLI configuration. + +## Installation + +### BRAT + +1. Install and enable the BRAT plugin in Obsidian. +2. In BRAT, choose `Add Beta plugin`. +3. Enter the repository URL: + +```text +https://github.com/murashit/codex-panel +``` + +4. Enable `Codex Panel` from Obsidian's Community plugins list. +5. Open the panel from the ribbon or command palette. + +### Manual + +Download the release assets and place them in: + +```text +/.obsidian/plugins/codex-panel/ +``` + +Required release assets: + +- `main.js` +- `manifest.json` +- `styles.css` + +Then reload Obsidian and enable `Codex Panel` from Community plugins. + +## Getting Started + +Open the command palette and run: + +- `Codex Panel: Open panel` +- `Codex Panel: New chat` + +If Obsidian cannot find `codex`, open the plugin settings and set `Codex executable` to an absolute path such as `/opt/homebrew/bin/codex`. + +The status dot in the panel opens connection controls, diagnostics, usage limits, and the effective Codex config for the current vault. The effective config view is diagnostic only; it does not save settings. + +## Features + +- Start, resume, rename, and archive Codex threads from Obsidian. +- Stream user, assistant, reasoning, command, tool, hook, and file-change events. +- Respond to command, file, permission, and Plan mode approval requests. +- Toggle Plan mode, fast mode, model override, and reasoning effort override for subsequent turns. +- Send steering messages during a running turn, or interrupt the turn when the composer is empty. +- Show recent chat history, paged older turns, context usage, connection diagnostics, and effective config. +- Inspect and manage discovered Codex hooks, including enabled state, trust status, and current hash. +- Complete vault wikilinks, slash commands, and enabled Codex skills in the composer. +- Resolve Obsidian wikilinks in sent messages into Codex file mentions when the target exists. + +## Slash Commands + +- `/compact`: request context compaction for the active thread. +- `/fast`: toggle fast service tier for subsequent turns. +- `/plan`: toggle Plan mode for subsequent turns. Add text after the command to toggle mode and send that text immediately, for example `/plan OK, implement it`. +- `/status`: show current connection, thread, runtime, and context status. +- `/doctor`: show connection diagnostics. +- `/model`: show, set, or clear model override. +- `/effort`: show, set, or clear reasoning effort override. +- `/help`: list slash commands. + +## Privacy and Security + +Codex Panel does not make its own network requests. It exchanges data with the configured `codex app-server` process over stdio. Messages, steering input, approvals, resolved file mentions, thread history requests, hook status requests, effective config requests, and archived-thread management requests are sent to Codex app-server and then handled according to the user's Codex CLI configuration, authentication, model provider, sandbox, approval, MCP, hook, and network settings. + +The plugin stores only panel settings in Obsidian plugin data: the Codex executable path and optional automatic thread naming model/effort overrides. It does not store API keys. + +Obsidian wikilinks in sent messages are resolved into structured file mentions only when the target file exists. The visible message text is preserved, unresolved wikilinks are not expanded, and note bodies are not automatically attached by the panel. + +## Development + +```sh +npm ci +npm run format +npm run check +npm run build +``` + +Run `npm run format` after edits and before `npm run check` so Prettier-only issues are fixed upfront. `npm run check` runs TypeScript type checking, unit tests, ESLint, Prettier check, and a production esbuild bundle. + +`main.js`, `data.json`, and `node_modules/` are ignored by Git. `main.js` is still the file Obsidian loads, so run `npm run build` or `npm run build:prod` after source changes. + +The app-server TypeScript bindings in `src/generated/app-server/` are generated from the installed Codex CLI: + +```sh +npm run generate:app-server-types +npm run check +``` + +The generation script uses `codex app-server generate-ts --experimental` because the panel depends on experimental app-server fields such as collaboration mode and generated v2 types. diff --git a/esbuild.config.mjs b/esbuild.config.mjs new file mode 100644 index 00000000..02099f11 --- /dev/null +++ b/esbuild.config.mjs @@ -0,0 +1,25 @@ +import esbuild from "esbuild"; + +const production = process.argv.includes("--production"); +const watch = process.argv.includes("--watch"); + +const context = await esbuild.context({ + entryPoints: ["src/main.ts"], + bundle: true, + external: ["obsidian", "electron"], + format: "cjs", + platform: "node", + target: "es2022", + outfile: "main.js", + sourcemap: production ? false : "inline", + minify: production, + logLevel: "info", +}); + +if (watch) { + await context.watch(); + console.log("Watching Codex Panel plugin..."); +} else { + await context.rebuild(); + await context.dispose(); +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..0dbdcd85 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,38 @@ +import tsParser from "@typescript-eslint/parser"; +import tsPlugin from "@typescript-eslint/eslint-plugin"; + +export default [ + { + ignores: ["main.js", "node_modules/**", "src/generated/**"], + }, + { + files: ["src/**/*.ts", "tests/**/*.ts"], + languageOptions: { + ecmaVersion: 2022, + sourceType: "module", + parser: tsParser, + parserOptions: { + project: "./tsconfig.json", + tsconfigRootDir: import.meta.dirname, + }, + globals: { + HTMLElement: "readonly", + HTMLTextAreaElement: "readonly", + KeyboardEvent: "readonly", + NodeJS: "readonly", + console: "readonly", + document: "readonly", + requestAnimationFrame: "readonly", + }, + }, + plugins: { + "@typescript-eslint": tsPlugin, + }, + rules: { + "@typescript-eslint/consistent-type-imports": "error", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], + }, + }, +]; diff --git a/manifest.json b/manifest.json new file mode 100644 index 00000000..5e12fe7a --- /dev/null +++ b/manifest.json @@ -0,0 +1,9 @@ +{ + "id": "codex-panel", + "name": "Codex Panel", + "version": "0.1.0", + "minAppVersion": "1.5.0", + "description": "Run Codex from an Obsidian side panel.", + "author": "murashit", + "isDesktopOnly": true +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..47922743 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3594 @@ +{ + "name": "codex-panel", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "codex-panel", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^25.6.0", + "@typescript-eslint/eslint-plugin": "^8.59.2", + "@typescript-eslint/parser": "^8.59.2", + "esbuild": "^0.25.0", + "eslint": "^10.3.0", + "jsdom": "^29.1.1", + "obsidian": "^1.12.3", + "prettier": "^3.8.3", + "typescript": "^6.0.3", + "vitest": "^4.1.5" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", + "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.38.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz", + "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", + "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz", + "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", + "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.127.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", + "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", + "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", + "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz", + "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/codemirror": { + "version": "5.60.8", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", + "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/tern": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.0" + } + }, + "node_modules/@types/tern": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", + "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/type-utils": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", + "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", + "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.5", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/obsidian": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz", + "integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/codemirror": "5.60.8", + "moment": "2.29.4" + }, + "peerDependencies": { + "@codemirror/state": "6.5.0", + "@codemirror/view": "6.38.6" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", + "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.127.0", + "@rolldown/pluginutils": "1.0.0-rc.17" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-x64": "1.0.0-rc.17", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz", + "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.30" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz", + "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vitest": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", + "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.10", + "rolldown": "1.0.0-rc.17", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..4d202b6e --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "codex-panel", + "version": "0.1.0", + "description": "Obsidian side panel for Codex app-server.", + "main": "main.js", + "author": "murashit", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/murashit/codex-panel.git" + }, + "bugs": { + "url": "https://github.com/murashit/codex-panel/issues" + }, + "homepage": "https://github.com/murashit/codex-panel#readme", + "scripts": { + "build": "node esbuild.config.mjs", + "build:prod": "node esbuild.config.mjs --production", + "dev": "node esbuild.config.mjs --watch", + "format": "prettier --write \"src/**/*.{ts,tsx,js,mjs,json,css,md}\" \"tests/**/*.{ts,tsx,js,mjs,json,css,md}\" \"*.{json,mjs,md,css}\" \"!src/generated/**\"", + "format:check": "prettier --check \"src/**/*.{ts,tsx,js,mjs,json,css,md}\" \"tests/**/*.{ts,tsx,js,mjs,json,css,md}\" \"*.{json,mjs,md,css}\" \"!src/generated/**\"", + "generate:app-server-types": "codex app-server generate-ts --experimental --out src/generated/app-server", + "lint": "eslint src tests --max-warnings=0", + "test": "vitest run", + "typecheck": "tsc -p tsconfig.json --noEmit", + "check": "npm run typecheck && npm run test && npm run lint && npm run format:check && npm run build:prod" + }, + "devDependencies": { + "@types/node": "^25.6.0", + "@typescript-eslint/eslint-plugin": "^8.59.2", + "@typescript-eslint/parser": "^8.59.2", + "esbuild": "^0.25.0", + "eslint": "^10.3.0", + "jsdom": "^29.1.1", + "obsidian": "^1.12.3", + "prettier": "^3.8.3", + "typescript": "^6.0.3", + "vitest": "^4.1.5" + } +} diff --git a/src/app-server/client.ts b/src/app-server/client.ts new file mode 100644 index 00000000..dfbf77b4 --- /dev/null +++ b/src/app-server/client.ts @@ -0,0 +1,421 @@ +import type { InitializeResponse } from "../generated/app-server/InitializeResponse"; +import type { CollaborationMode } from "../generated/app-server/CollaborationMode"; +import type { RequestId } from "../generated/app-server/RequestId"; +import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; +import type { ConfigReadResponse } from "../generated/app-server/v2/ConfigReadResponse"; +import type { ConfigWriteResponse } from "../generated/app-server/v2/ConfigWriteResponse"; +import type { GetAccountRateLimitsResponse } from "../generated/app-server/v2/GetAccountRateLimitsResponse"; +import type { HookMetadata } from "../generated/app-server/v2/HookMetadata"; +import type { HooksListResponse } from "../generated/app-server/v2/HooksListResponse"; +import type { ModelListResponse } from "../generated/app-server/v2/ModelListResponse"; +import type { SkillsListResponse } from "../generated/app-server/v2/SkillsListResponse"; +import type { ThreadArchiveResponse } from "../generated/app-server/v2/ThreadArchiveResponse"; +import type { ThreadListResponse } from "../generated/app-server/v2/ThreadListResponse"; +import type { ThreadResumeResponse } from "../generated/app-server/v2/ThreadResumeResponse"; +import type { ThreadSetNameResponse } from "../generated/app-server/v2/ThreadSetNameResponse"; +import type { SortDirection } from "../generated/app-server/v2/SortDirection"; +import type { ThreadStartResponse } from "../generated/app-server/v2/ThreadStartResponse"; +import type { ThreadTurnsListResponse } from "../generated/app-server/v2/ThreadTurnsListResponse"; +import type { ThreadUnarchiveResponse } from "../generated/app-server/v2/ThreadUnarchiveResponse"; +import type { TurnItemsView } from "../generated/app-server/v2/TurnItemsView"; +import type { TurnStartResponse } from "../generated/app-server/v2/TurnStartResponse"; +import type { TurnSteerResponse } from "../generated/app-server/v2/TurnSteerResponse"; +import type { UserInput } from "../generated/app-server/v2/UserInput"; +import { CLIENT_VERSION } from "../constants"; +import { StdioAppServerTransport, type AppServerTransport, type AppServerTransportHandlers } from "./transport"; +import type { ClientRequestMethod, ClientRequestParams, PendingRequest, RpcInboundMessage, RpcOutboundMessage } from "./types"; +import type { ServerNotification } from "../generated/app-server/ServerNotification"; +import type { ServerRequest } from "../generated/app-server/ServerRequest"; +import type { JsonValue } from "../generated/app-server/serde_json/JsonValue"; +import type { ServiceTierRequest } from "./service-tier"; + +const DEFAULT_REQUEST_TIMEOUT_MS = 120_000; + +export interface AppServerClientHandlers { + onNotification: (notification: ServerNotification) => void; + onServerRequest: (request: ServerRequest) => void; + onLog: (message: string) => void; + onExit: (code: number | null, signal: NodeJS.Signals | null) => void; +} + +export type AppServerTransportFactory = (handlers: AppServerTransportHandlers) => AppServerTransport; + +interface ClientResponseByMethod { + initialize: InitializeResponse; + "config/batchWrite": ConfigWriteResponse; + "config/read": ConfigReadResponse; + "hooks/list": HooksListResponse; + "thread/start": ThreadStartResponse; + "thread/resume": ThreadResumeResponse; + "thread/list": ThreadListResponse; + "thread/archive": ThreadArchiveResponse; + "thread/unarchive": ThreadUnarchiveResponse; + "thread/name/set": ThreadSetNameResponse; + "thread/turns/list": ThreadTurnsListResponse; + "skills/list": SkillsListResponse; + "model/list": ModelListResponse; + "account/rateLimits/read": GetAccountRateLimitsResponse; + "thread/compact/start": Record; + "turn/start": TurnStartResponse; + "turn/steer": TurnSteerResponse; + "turn/interrupt": unknown; +} + +type TypedClientRequestMethod = Extract; + +function toUserInput(input: string | UserInput[]): UserInput[] { + if (typeof input !== "string") return input; + return [{ type: "text", text: input, text_elements: [] }]; +} + +export class AppServerClient { + private transport: AppServerTransport | null = null; + private nextId = 1; + private pending = new Map(); + private initialized = false; + private initResponse: InitializeResponse | null = null; + + constructor( + private readonly codexPath: string, + private readonly cwd: string, + private readonly handlers: AppServerClientHandlers, + private readonly requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS, + private readonly transportFactory?: AppServerTransportFactory, + ) {} + + async connect(): Promise { + if (this.transport?.isRunning()) { + throw new Error("Codex app-server is already running."); + } + + const transportHandlers: AppServerTransportHandlers = { + onLine: (line) => this.handleLine(line), + onLog: this.handlers.onLog, + onError: (error) => this.rejectAll(error), + onExit: (code, signal) => { + this.initialized = false; + this.initResponse = null; + this.rejectAll(new Error(`Codex app-server exited: ${code ?? signal ?? "unknown"}`)); + this.handlers.onExit(code, signal); + }, + }; + this.transport = this.transportFactory + ? this.transportFactory(transportHandlers) + : new StdioAppServerTransport(this.codexPath, this.cwd, transportHandlers); + this.transport.start(); + + const init = await this.request("initialize", { + clientInfo: { + name: "obsidian_codex_panel", + title: "Codex Panel", + version: CLIENT_VERSION, + }, + capabilities: { + experimentalApi: true, + }, + }); + this.notify({ method: "initialized" }); + this.initialized = true; + this.initResponse = init; + return init; + } + + disconnect(): void { + this.initialized = false; + this.initResponse = null; + this.transport?.stop(); + this.transport = null; + this.rejectAll(new Error("Codex app-server disconnected.")); + } + + isConnected(): boolean { + return this.initialized && this.transport !== null && this.transport.isRunning(); + } + + get initializeResponse(): InitializeResponse { + if (!this.initResponse) throw new Error("Codex app-server has not initialized."); + return this.initResponse; + } + + readEffectiveConfig(cwd: string): Promise { + return this.request("config/read", { cwd, includeLayers: false }); + } + + listHooks(cwd: string): Promise { + return this.request("hooks/list", { cwds: [cwd] }); + } + + trustHook(hook: HookMetadata): Promise { + return this.writeHookState(hook.key, { + enabled: true, + trusted_hash: hook.currentHash, + }); + } + + setHookEnabled(hook: HookMetadata, enabled: boolean): Promise { + const state: { [key: string]: JsonValue } = { enabled }; + if (hook.trustStatus === "trusted") { + state.trusted_hash = hook.currentHash; + } + return this.writeHookState(hook.key, state); + } + + startThread(cwd: string, serviceTier?: ServiceTierRequest): Promise { + return this.request("thread/start", { + cwd, + serviceName: "codex-panel", + ...(serviceTier !== undefined ? { serviceTier } : {}), + experimentalRawEvents: false, + persistExtendedHistory: false, + }); + } + + startEphemeralThread(cwd: string, serviceName: string, developerInstructions: string): Promise { + return this.request("thread/start", { + cwd, + serviceName, + developerInstructions, + ephemeral: true, + sandbox: "read-only", + approvalPolicy: "never", + environments: [], + experimentalRawEvents: false, + persistExtendedHistory: false, + }); + } + + resumeThread(threadId: string, cwd: string): Promise { + return this.request("thread/resume", { + threadId, + cwd, + excludeTurns: true, + persistExtendedHistory: false, + }); + } + + listThreads(cwd: string, archived = false): Promise { + return this.request("thread/list", { + cwd, + limit: 20, + archived, + sortKey: "updated_at", + sortDirection: "desc", + }); + } + + archiveThread(threadId: string): Promise { + return this.request("thread/archive", { threadId }); + } + + unarchiveThread(threadId: string): Promise { + return this.request("thread/unarchive", { threadId }); + } + + setThreadName(threadId: string, name: string): Promise { + return this.request("thread/name/set", { threadId, name }); + } + + threadTurnsList( + threadId: string, + cursor: string | null = null, + limit = 20, + sortDirection: SortDirection = "desc", + itemsView: TurnItemsView = "full", + ): Promise { + return this.request("thread/turns/list", { + threadId, + cursor, + limit, + sortDirection, + itemsView, + }); + } + + listSkills(cwd: string): Promise { + return this.request("skills/list", { + cwds: [cwd], + forceReload: false, + }); + } + + listModels(includeHidden = false): Promise { + return this.request("model/list", { + includeHidden, + limit: 100, + }); + } + + readAccountRateLimits(): Promise { + return this.request("account/rateLimits/read", undefined); + } + + compactThread(threadId: string): Promise> { + return this.request("thread/compact/start", { threadId }); + } + + startTurn( + threadId: string, + cwd: string, + input: string | UserInput[], + serviceTier?: ServiceTierRequest, + collaborationMode?: CollaborationMode | null, + model?: string | null, + effort?: ReasoningEffort | null, + ): Promise { + const params: ClientRequestParams<"turn/start"> & { collaborationMode?: CollaborationMode | null } = { + threadId, + cwd, + ...(serviceTier !== undefined ? { serviceTier } : {}), + input: toUserInput(input), + }; + if (collaborationMode) params.collaborationMode = collaborationMode; + if (model !== undefined) params.model = model; + if (effort !== undefined) params.effort = effort; + return this.request("turn/start", params); + } + + startStructuredTurn( + threadId: string, + cwd: string, + text: string, + outputSchema: JsonValue, + model?: string, + effort?: ReasoningEffort, + ): Promise { + const params: ClientRequestParams<"turn/start"> = { + threadId, + cwd, + input: [ + { + type: "text", + text, + text_elements: [], + }, + ], + outputSchema, + }; + if (model !== undefined) params.model = model; + if (effort !== undefined) params.effort = effort; + return this.request("turn/start", params); + } + + steerTurn(threadId: string, expectedTurnId: string, input: string | UserInput[]): Promise { + return this.request("turn/steer", { + threadId, + expectedTurnId, + input: toUserInput(input), + }); + } + + interruptTurn(threadId: string, turnId: string): Promise { + return this.request("turn/interrupt", { threadId, turnId }); + } + + private writeHookState(key: string, state: { [key: string]: JsonValue }): Promise { + return this.request("config/batchWrite", { + edits: [ + { + keyPath: "hooks.state", + value: { + [key]: state, + }, + mergeStrategy: "upsert", + }, + ], + reloadUserConfig: true, + }); + } + + respondToServerRequest(requestId: RequestId, result: unknown): void { + this.send({ id: requestId, result }); + } + + rejectServerRequest(requestId: RequestId, code: number, message: string): void { + this.send({ id: requestId, error: { code, message } }); + } + + private request(method: M, params: ClientRequestParams): Promise { + const id = this.nextId++; + const promise = new Promise((resolve, reject) => { + const timeout = globalThis.setTimeout(() => { + this.pending.delete(id); + reject(new Error(`Codex app-server request timed out: ${method}`)); + }, this.requestTimeoutMs); + this.pending.set(id, { + method, + resolve: resolve as (value: unknown) => void, + reject, + timeout, + }); + }); + + try { + this.send({ id, method, params } as RpcOutboundMessage); + } catch (error) { + const pending = this.pending.get(id); + if (pending) { + globalThis.clearTimeout(pending.timeout); + this.pending.delete(id); + } + throw error; + } + + return promise; + } + + private notify(message: RpcOutboundMessage): void { + this.send(message); + } + + private send(message: RpcOutboundMessage): void { + if (!this.transport?.isRunning()) { + throw new Error("Codex app-server is not running."); + } + this.transport.send(message); + } + + private handleLine(line: string): void { + if (line.trim().length === 0) return; + + let message: RpcInboundMessage; + try { + message = JSON.parse(line) as RpcInboundMessage; + } catch { + this.handlers.onLog(`Invalid app-server JSON: ${line}`); + return; + } + + if ("id" in message && "method" in message) { + this.handlers.onServerRequest(message as ServerRequest); + return; + } + + if ("id" in message) { + const pending = this.pending.get(message.id); + if (!pending) { + this.handlers.onLog(`Orphan app-server response: ${JSON.stringify(message)}`); + return; + } + globalThis.clearTimeout(pending.timeout); + this.pending.delete(message.id); + if ("error" in message && message.error) { + pending.reject(new Error(message.error.message || "Codex app-server request failed.")); + } else { + pending.resolve(message.result); + } + return; + } + + if ("method" in message) { + this.handlers.onNotification(message as ServerNotification); + } + } + + private rejectAll(error: Error): void { + for (const pending of this.pending.values()) { + globalThis.clearTimeout(pending.timeout); + pending.reject(error); + } + this.pending.clear(); + } +} diff --git a/src/app-server/compatibility.ts b/src/app-server/compatibility.ts new file mode 100644 index 00000000..0e654dd6 --- /dev/null +++ b/src/app-server/compatibility.ts @@ -0,0 +1,31 @@ +import type { InitializeResponse } from "../generated/app-server/InitializeResponse"; + +export type CapabilityProbeState = "unknown" | "ok" | "failed"; + +export interface AppServerCompatibility { + modelList: CapabilityProbeState; + modelListError: string | null; +} + +export function createAppServerCompatibility(): AppServerCompatibility { + return { + modelList: "unknown", + modelListError: null, + }; +} + +export function appServerIdentity(initializeResponse: InitializeResponse | null): string { + return initializeResponse?.userAgent || "(not connected)"; +} + +export function appServerPlatform(initializeResponse: InitializeResponse | null): string { + if (!initializeResponse) return "(not connected)"; + const family = initializeResponse.platformFamily || "unknown"; + const os = initializeResponse.platformOs || "unknown"; + return `${os}/${family}`; +} + +export function compatibilitySummary(compatibility: AppServerCompatibility): string { + const modelList = compatibility.modelList === "failed" ? `model/list failed` : `model/list ${compatibility.modelList}`; + return `${modelList}; Plan mode uses experimental collaborationMode override`; +} diff --git a/src/app-server/connection-manager.ts b/src/app-server/connection-manager.ts new file mode 100644 index 00000000..eb981c3b --- /dev/null +++ b/src/app-server/connection-manager.ts @@ -0,0 +1,114 @@ +import type { InitializeResponse } from "../generated/app-server/InitializeResponse"; +import type { ServerNotification } from "../generated/app-server/ServerNotification"; +import type { ServerRequest } from "../generated/app-server/ServerRequest"; +import { AppServerClient, type AppServerClientHandlers } from "./client"; + +export interface ConnectionManagerHandlers { + onNotification: (notification: ServerNotification) => void; + onServerRequest: (request: ServerRequest) => void; + onLog: (message: string) => void; + onExit: () => void; +} + +export type AppServerClientFactory = (codexPath: string, cwd: string, handlers: AppServerClientHandlers) => AppServerClient; + +export class StaleConnectionError extends Error { + constructor() { + super("Stale Codex app-server connection ignored."); + this.name = "StaleConnectionError"; + } +} + +export class ConnectionManager { + private client: AppServerClient | null = null; + private connectPromise: Promise | null = null; + private generation = 0; + private disposed = false; + + constructor( + private readonly codexPath: () => string, + private readonly cwd: string, + private readonly handlers: ConnectionManagerHandlers, + private readonly clientFactory: AppServerClientFactory = (codexPath, cwd, handlers) => new AppServerClient(codexPath, cwd, handlers), + ) {} + + currentClient(): AppServerClient | null { + return this.client?.isConnected() ? this.client : null; + } + + isConnected(): boolean { + return Boolean(this.currentClient()); + } + + async connect(): Promise { + this.disposed = false; + if (this.client?.isConnected()) { + return this.client.initializeResponse; + } + if (this.connectPromise) return this.connectPromise; + + const generation = ++this.generation; + const client = this.clientFactory(this.codexPath(), this.cwd, { + onNotification: (notification) => { + if (this.isStale(generation)) return; + this.handlers.onNotification(notification); + }, + onServerRequest: (request) => { + if (this.isStale(generation)) return; + this.handlers.onServerRequest(request); + }, + onLog: (message) => { + if (this.isStale(generation)) return; + this.handlers.onLog(message); + }, + onExit: () => { + if (this.isStale(generation)) return; + this.client = null; + this.connectPromise = null; + this.handlers.onExit(); + }, + }); + this.client = client; + this.connectPromise = client + .connect() + .then((response) => { + if (this.isStale(generation)) { + client.disconnect(); + throw new StaleConnectionError(); + } + return response; + }) + .catch((error: unknown) => { + if (this.isStale(generation)) { + throw error instanceof StaleConnectionError ? error : new StaleConnectionError(); + } + if (!this.isStale(generation) && this.client === client) { + this.client = null; + client.disconnect(); + } + throw error; + }) + .finally(() => { + if (!this.isStale(generation)) this.connectPromise = null; + }); + + return this.connectPromise; + } + + reconnect(): void { + this.disconnect(); + this.disposed = false; + } + + disconnect(): void { + this.disposed = true; + this.generation += 1; + this.connectPromise = null; + this.client?.disconnect(); + this.client = null; + } + + private isStale(generation: number): boolean { + return this.disposed || generation !== this.generation; + } +} diff --git a/src/app-server/service-tier.ts b/src/app-server/service-tier.ts new file mode 100644 index 00000000..dd3acdb5 --- /dev/null +++ b/src/app-server/service-tier.ts @@ -0,0 +1,14 @@ +export type ServiceTier = "fast" | "standard"; +export type ServiceTierRequest = "fast" | null | undefined; + +export function parseServiceTier(value: unknown): ServiceTier | null { + if (value === "fast" || value === "priority") return "fast"; + if (value === "standard" || value === "default" || value === "flex") return "standard"; + return null; +} + +export function serviceTierRequestValue(value: ServiceTier | null): ServiceTierRequest { + if (value === "fast") return "fast"; + if (value === "standard") return null; + return undefined; +} diff --git a/src/app-server/session-client.ts b/src/app-server/session-client.ts new file mode 100644 index 00000000..8e80b38b --- /dev/null +++ b/src/app-server/session-client.ts @@ -0,0 +1,22 @@ +import { AppServerClient } from "./client"; + +export async function withAppServerSession( + codexPath: string, + cwd: string, + operation: (client: AppServerClient) => Promise, +): Promise { + let client!: AppServerClient; + client = new AppServerClient(codexPath, cwd, { + onNotification: () => undefined, + onServerRequest: (request) => client.rejectServerRequest(request.id, -32601, "This Codex Panel view does not handle server requests."), + onLog: () => undefined, + onExit: () => undefined, + }); + + try { + await client.connect(); + return await operation(client); + } finally { + client.disconnect(); + } +} diff --git a/src/app-server/transport.ts b/src/app-server/transport.ts new file mode 100644 index 00000000..27e56ca0 --- /dev/null +++ b/src/app-server/transport.ts @@ -0,0 +1,98 @@ +import { spawn, type ChildProcessWithoutNullStreams } from "child_process"; +import * as readline from "readline"; + +import type { RpcOutboundMessage } from "./types"; + +export interface AppServerTransport { + start(): void; + send(message: RpcOutboundMessage): void; + stop(): void; + isRunning(): boolean; +} + +export interface AppServerTransportHandlers { + onLine: (line: string) => void; + onLog: (message: string) => void; + onExit: (code: number | null, signal: NodeJS.Signals | null) => void; + onError: (error: Error) => void; +} + +export class StdioAppServerTransport implements AppServerTransport { + private process: ChildProcessWithoutNullStreams | null = null; + private reader: readline.Interface | null = null; + private stderrBuffer = ""; + + constructor( + private readonly codexPath: string, + private readonly cwd: string, + private readonly handlers: AppServerTransportHandlers, + ) {} + + start(): void { + if (this.process) { + throw new Error("Codex app-server is already running."); + } + + this.process = spawn(this.codexPath, ["app-server"], { + cwd: this.cwd, + stdio: ["pipe", "pipe", "pipe"], + env: process.env, + }); + + this.process.once("error", (error) => { + this.reader?.close(); + this.reader = null; + this.process = null; + this.handlers.onError(error instanceof Error ? error : new Error(String(error))); + }); + + this.process.once("exit", (code, signal) => { + this.reader?.close(); + this.reader = null; + this.process = null; + this.handlers.onExit(code, signal); + }); + + this.process.stderr.on("data", (chunk: Buffer) => { + this.handleStderr(chunk.toString("utf8")); + }); + this.process.stdin.on("error", (error) => { + this.handlers.onError(error instanceof Error ? error : new Error(String(error))); + }); + + this.reader = readline.createInterface({ input: this.process.stdout }); + this.reader.on("line", (line) => this.handlers.onLine(line)); + } + + send(message: RpcOutboundMessage): void { + if (!this.process || this.process.killed || this.process.stdin.destroyed || !this.process.stdin.writable) { + throw new Error("Codex app-server is not running."); + } + this.process.stdin.write(`${JSON.stringify(message)}\n`, (error) => { + if (error) this.handlers.onError(error instanceof Error ? error : new Error(String(error))); + }); + } + + stop(): void { + this.reader?.close(); + this.reader = null; + if (this.process && !this.process.killed) { + this.process.kill(); + } + this.process = null; + } + + isRunning(): boolean { + return this.process !== null && !this.process.killed; + } + + private handleStderr(text: string): void { + this.stderrBuffer += text; + const lines = this.stderrBuffer.split(/\r?\n/); + this.stderrBuffer = lines.pop() ?? ""; + for (const line of lines) { + const trimmed = line.trim(); + if (trimmed.length > 0) this.handlers.onLog(trimmed); + } + } +} diff --git a/src/app-server/types.ts b/src/app-server/types.ts new file mode 100644 index 00000000..2e840b7a --- /dev/null +++ b/src/app-server/types.ts @@ -0,0 +1,30 @@ +import type { ClientNotification } from "../generated/app-server/ClientNotification"; +import type { ClientRequest } from "../generated/app-server/ClientRequest"; +import type { RequestId } from "../generated/app-server/RequestId"; +import type { ServerNotification } from "../generated/app-server/ServerNotification"; +import type { ServerRequest } from "../generated/app-server/ServerRequest"; + +export type ClientRequestMethod = ClientRequest["method"]; +export type ClientRequestParams = Extract["params"]; +export type ClientRequestMessage = Extract; + +export interface RpcError { + code?: number; + message: string; + data?: unknown; +} + +export type RpcOutboundMessage = + | ClientRequest + | ClientNotification + | { id: RequestId; result: unknown } + | { id: RequestId; error: RpcError }; + +export type RpcInboundMessage = ServerNotification | ServerRequest | { id: RequestId; result?: unknown; error?: RpcError }; + +export interface PendingRequest { + method: ClientRequestMethod; + reject: (reason: Error) => void; + resolve: (value: unknown) => void; + timeout: ReturnType; +} diff --git a/src/approvals/model.ts b/src/approvals/model.ts new file mode 100644 index 00000000..7eb0890e --- /dev/null +++ b/src/approvals/model.ts @@ -0,0 +1,152 @@ +import type { RequestId } from "../generated/app-server/RequestId"; +import type { ServerRequest } from "../generated/app-server/ServerRequest"; +import type { CommandExecutionRequestApprovalResponse } from "../generated/app-server/v2/CommandExecutionRequestApprovalResponse"; +import type { FileChangeRequestApprovalResponse } from "../generated/app-server/v2/FileChangeRequestApprovalResponse"; +import type { GrantedPermissionProfile } from "../generated/app-server/v2/GrantedPermissionProfile"; +import type { PermissionsRequestApprovalResponse } from "../generated/app-server/v2/PermissionsRequestApprovalResponse"; +import { jsonPreview } from "../utils"; + +export type ApprovalAction = "accept" | "accept-session" | "decline" | "cancel"; +export type ApprovalRequest = Extract< + ServerRequest, + { + method: "item/commandExecution/requestApproval" | "item/fileChange/requestApproval" | "item/permissions/requestApproval"; + } +>; + +export type PendingApproval = ApprovalRequest extends infer Request + ? Request extends ApprovalRequest + ? { + requestId: RequestId; + method: Request["method"]; + params: Request["params"]; + } + : never + : never; + +export function toPendingApproval(request: ServerRequest): PendingApproval | null { + if (!isApprovalRequest(request)) return null; + switch (request.method) { + case "item/commandExecution/requestApproval": + case "item/fileChange/requestApproval": + case "item/permissions/requestApproval": + return { + requestId: request.id, + method: request.method, + params: request.params, + } as PendingApproval; + } +} + +export function isApprovalRequest(request: ServerRequest): request is ApprovalRequest { + return ( + request.method === "item/commandExecution/requestApproval" || + request.method === "item/fileChange/requestApproval" || + request.method === "item/permissions/requestApproval" + ); +} + +export function approvalResponse(approval: PendingApproval, action: ApprovalAction): unknown { + if (approval.method === "item/commandExecution/requestApproval") { + return { + decision: commandDecision(action), + } satisfies CommandExecutionRequestApprovalResponse; + } + + if (approval.method === "item/fileChange/requestApproval") { + return { + decision: fileChangeDecision(action), + } satisfies FileChangeRequestApprovalResponse; + } + + if (approval.method === "item/permissions/requestApproval") { + return { + scope: action === "accept-session" ? "session" : "turn", + permissions: action === "accept" || action === "accept-session" ? grantedPermissions(approval.params.permissions) : {}, + } satisfies PermissionsRequestApprovalResponse; + } + + throw new Error("Unsupported approval method."); +} + +export function approvalTitle(approval: PendingApproval): string { + if (approval.method.includes("commandExecution")) return "Command approval"; + if (approval.method.includes("fileChange")) return "File change approval"; + if (approval.method.includes("permissions")) return "Permission approval"; + return approval.method; +} + +export function approvalSummary(approval: PendingApproval): string { + const params = approval.params as Record; + if (approval.method.includes("commandExecution")) { + return typeof params.command === "string" ? params.command : "Command execution requested."; + } + if (approval.method.includes("fileChange")) { + return typeof params.grantRoot === "string" + ? `grant root: ${params.grantRoot}` + : typeof params.reason === "string" + ? params.reason + : "Allow file changes?"; + } + if (approval.method.includes("permissions")) { + return [ + `cwd: ${typeof params.cwd === "string" ? params.cwd : "(unknown)"}`, + typeof params.reason === "string" ? params.reason : "", + jsonPreview(params.permissions), + ] + .filter(Boolean) + .join("\n"); + } + return jsonPreview(params); +} + +export function approvalDetails(approval: PendingApproval): Array<{ key: string; value: string }> { + const params = approval.params as Record; + const rows: Array<{ key: string; value: string }> = [ + { key: "method", value: approval.method }, + { key: "cwd", value: stringValue(params.cwd, "(unknown)") }, + ]; + addOptional(rows, "reason", params.reason); + addOptional(rows, "grant root", params.grantRoot); + addOptional(rows, "command", Array.isArray(params.command) ? params.command.join(" ") : params.command); + addOptional(rows, "actions", params.commandActions); + addOptional(rows, "permissions", params.permissions); + addOptional(rows, "future command rule", params.proposedExecpolicyAmendment); + addOptional(rows, "future network rules", params.proposedNetworkPolicyAmendments); + if (Object.keys(params).length > 0) rows.push({ key: "raw", value: jsonPreview(params) }); + return rows; +} + +function commandDecision(action: ApprovalAction): CommandExecutionRequestApprovalResponse["decision"] { + if (action === "accept") return "accept"; + if (action === "accept-session") return "acceptForSession"; + if (action === "cancel") return "cancel"; + return "decline"; +} + +function fileChangeDecision(action: ApprovalAction): FileChangeRequestApprovalResponse["decision"] { + if (action === "accept") return "accept"; + if (action === "accept-session") return "acceptForSession"; + if (action === "cancel") return "cancel"; + return "decline"; +} + +function grantedPermissions(requested: unknown): GrantedPermissionProfile { + const source = requested as { network?: unknown; fileSystem?: unknown } | null; + const granted: GrantedPermissionProfile = {}; + if (source?.network) granted.network = source.network as GrantedPermissionProfile["network"]; + if (source?.fileSystem) granted.fileSystem = source.fileSystem as GrantedPermissionProfile["fileSystem"]; + return granted; +} + +function addOptional(rows: Array<{ key: string; value: string }>, key: string, value: unknown): void { + if (value === null || value === undefined) return; + rows.push({ key, value: stringValue(value) }); +} + +function stringValue(value: unknown, fallback = ""): string { + if (typeof value === "string") return value; + if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value); + if (value === null || value === undefined) return fallback; + return jsonPreview(value); +} diff --git a/src/composer/suggestions.ts b/src/composer/suggestions.ts new file mode 100644 index 00000000..1522397a --- /dev/null +++ b/src/composer/suggestions.ts @@ -0,0 +1,146 @@ +import type { SkillMetadata } from "../generated/app-server/v2/SkillMetadata"; +import { SLASH_COMMANDS, type SlashCommandName } from "../panel/slash-commands"; + +export interface ComposerSuggestion { + display: string; + detail: string; + replacement: string; + start: number; + appendSpaceOnInsert?: boolean; +} + +export interface NoteCandidate { + basename: string; + path: string; + mtime: number; +} + +export function parseSlashCommand(text: string): { command: SlashCommandName; args: string } | null { + const match = text.match(/^\/([A-Za-z-]+)(?:\s+([\s\S]*))?$/); + if (!match) return null; + const command = match[1] as SlashCommandName; + if (!SLASH_COMMANDS.some((item) => item.command === `/${command}`)) return null; + return { command, args: match[2]?.trim() ?? "" }; +} + +export function activeComposerSuggestions(beforeCursor: string, notes: NoteCandidate[], skills: SkillMetadata[]): ComposerSuggestion[] { + return ( + activeWikiLinkSuggestions(beforeCursor, notes) ?? + activeSlashCommandSuggestions(beforeCursor) ?? + activeSkillSuggestions(beforeCursor, skills) ?? + [] + ); +} + +export function applyComposerSuggestionInsertion( + value: string, + cursor: number, + suggestion: ComposerSuggestion, +): { value: string; cursor: number } { + const suffix = value.slice(cursor); + const appendSpace = suggestion.appendSpaceOnInsert === true && !suggestion.replacement.endsWith(" ") && !/^\s/.test(suffix); + const replacement = `${suggestion.replacement}${appendSpace ? " " : ""}`; + const nextValue = `${value.slice(0, suggestion.start)}${replacement}${suffix}`; + return { value: nextValue, cursor: suggestion.start + replacement.length }; +} + +export function composerSuggestionNavigationDirection(event: Pick): 1 | -1 | null { + if (event.ctrlKey && !event.metaKey && !event.altKey && event.key.toLowerCase() === "n") return 1; + if (event.ctrlKey && !event.metaKey && !event.altKey && event.key.toLowerCase() === "p") return -1; + if (event.metaKey || event.ctrlKey || event.altKey) return null; + if (event.key === "ArrowDown") return 1; + if (event.key === "ArrowUp") return -1; + return null; +} + +export function nextComposerSuggestionIndex(current: number, length: number, direction: 1 | -1): number { + if (length <= 0) return 0; + return (current + direction + length) % length; +} + +export function activeWikiLinkSuggestions(beforeCursor: string, notes: NoteCandidate[]): ComposerSuggestion[] | null { + const start = beforeCursor.lastIndexOf("[["); + if (start === -1) return null; + + const queryText = beforeCursor.slice(start + 2); + if (queryText.includes("]]") || queryText.includes("\n") || queryText.length > 120) return null; + return findWikiLinkSuggestions(queryText, start, notes); +} + +export function findWikiLinkSuggestions(queryText: string, start: number, notes: NoteCandidate[]): ComposerSuggestion[] { + const query = queryText.toLowerCase().trim(); + const basenameCounts = new Map(); + for (const file of notes) { + basenameCounts.set(file.basename, (basenameCounts.get(file.basename) ?? 0) + 1); + } + + return notes + .map((file) => { + const basename = file.basename.toLowerCase(); + const path = file.path.toLowerCase(); + const score = query.length === 0 ? 3 : basename.startsWith(query) ? 0 : basename.includes(query) ? 1 : path.includes(query) ? 2 : -1; + return { file, score }; + }) + .filter((item) => item.score !== -1) + .sort( + (a, b) => + a.score - b.score || + b.file.mtime - a.file.mtime || + a.file.basename.localeCompare(b.file.basename) || + a.file.path.localeCompare(b.file.path), + ) + .slice(0, 8) + .map(({ file }) => { + const duplicateBasename = (basenameCounts.get(file.basename) ?? 0) > 1; + const target = duplicateBasename ? file.path.replace(/\.md$/i, "") : file.basename; + return { + display: file.basename, + detail: file.path, + replacement: `[[${target}]]`, + start, + }; + }); +} + +export function activeSlashCommandSuggestions(beforeCursor: string): ComposerSuggestion[] | null { + const match = beforeCursor.match(/(?:^|\n)(\/[A-Za-z-]*)$/); + if (!match || match.index === undefined) return null; + + const query = match[1].toLowerCase(); + const start = match.index + match[0].lastIndexOf("/"); + return SLASH_COMMANDS.filter((item) => item.command.toLowerCase().startsWith(query)) + .slice(0, 8) + .map((item) => ({ + display: item.command, + detail: item.detail, + replacement: item.command, + start, + appendSpaceOnInsert: true, + })); +} + +export function activeSkillSuggestions(beforeCursor: string, skills: SkillMetadata[]): ComposerSuggestion[] | null { + const match = beforeCursor.match(/(^|[\s([{])\$([^\s\])}]{0,120})$/); + if (!match || match.index === undefined) return null; + + const prefix = match[1] ?? ""; + const query = (match[2] ?? "").toLowerCase(); + const start = match.index + prefix.length; + return skills + .filter((skill) => skill.name.toLowerCase().includes(query)) + .sort((a, b) => { + const aName = a.name.toLowerCase(); + const bName = b.name.toLowerCase(); + const aScore = aName.startsWith(query) ? 0 : 1; + const bScore = bName.startsWith(query) ? 0 : 1; + return aScore - bScore || a.name.localeCompare(b.name); + }) + .slice(0, 8) + .map((skill) => ({ + display: `$${skill.name}`, + detail: skill.shortDescription ?? skill.interface?.shortDescription ?? skill.description, + replacement: `$${skill.name}`, + start, + appendSpaceOnInsert: true, + })); +} diff --git a/src/composer/wikilink-context.ts b/src/composer/wikilink-context.ts new file mode 100644 index 00000000..852f99e2 --- /dev/null +++ b/src/composer/wikilink-context.ts @@ -0,0 +1,62 @@ +import type { UserInput } from "../generated/app-server/v2/UserInput"; + +export interface ParsedWikiLink { + raw: string; + target: string; + subpath: string; + display: string; +} + +export type WikiLinkMentionResolver = (target: string) => { name: string; path: string } | null; + +const WIKILINK_PATTERN = /\[\[([^\]\n]+?)\]\]/g; + +export function parsedWikiLinks(text: string): ParsedWikiLink[] { + const links: ParsedWikiLink[] = []; + const seen = new Set(); + for (const match of text.matchAll(WIKILINK_PATTERN)) { + const raw = match[1]?.trim() ?? ""; + const link = parseWikiLink(raw); + if (!link) continue; + const key = `${link.target}${link.subpath}`; + if (seen.has(key)) continue; + seen.add(key); + links.push(link); + } + return links; +} + +export function userInputWithWikiLinkMentions(text: string, resolveMention: WikiLinkMentionResolver): UserInput[] { + const input: UserInput[] = [{ type: "text", text, text_elements: [] }]; + const seenPaths = new Set(); + + for (const link of parsedWikiLinks(text)) { + const mention = resolveMention(link.target); + if (!mention || seenPaths.has(mention.path)) continue; + seenPaths.add(mention.path); + input.push({ type: "mention", name: mention.name, path: mention.path }); + } + + return input; +} + +function parseWikiLink(raw: string): ParsedWikiLink | null { + const separator = raw.indexOf("|"); + const linkPart = (separator === -1 ? raw : raw.slice(0, separator)).trim(); + const display = separator === -1 ? "" : raw.slice(separator + 1).trim(); + if (!linkPart) return null; + + const subpathStart = firstSubpathIndex(linkPart); + const target = subpathStart === -1 ? linkPart : linkPart.slice(0, subpathStart).trim(); + const subpath = subpathStart === -1 ? "" : linkPart.slice(subpathStart).trim(); + if (!target) return null; + return { raw, target, subpath, display }; +} + +function firstSubpathIndex(linkPart: string): number { + const headingIndex = linkPart.indexOf("#"); + const blockIndex = linkPart.indexOf("^"); + if (headingIndex === -1) return blockIndex; + if (blockIndex === -1) return headingIndex; + return Math.min(headingIndex, blockIndex); +} diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 00000000..ef2c1019 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,3 @@ +export const VIEW_TYPE_CODEX_PANEL = "codex-panel-view"; +export const DEFAULT_CODEX_PATH = "codex"; +export const CLIENT_VERSION = "0.1.0"; diff --git a/src/display/agent.ts b/src/display/agent.ts new file mode 100644 index 00000000..727d41e4 --- /dev/null +++ b/src/display/agent.ts @@ -0,0 +1,109 @@ +import type { ThreadItem } from "../generated/app-server/v2/ThreadItem"; +import type { AgentRunSummary, AgentRunSummaryAgent, AgentStateDisplay, DisplayItem, ExecutionState } from "./types"; +import { agentActivitySummaryLabel, agentMessagePreview } from "./labels"; +import { classifyExecutionState } from "./state"; + +const ACTIVE_AGENT_PREVIEW_LIMIT = 96; +const ACTIVE_AGENT_PREVIEW_COUNT = 3; +type AgentRunState = "running" | "completed" | "failed"; +type CollabAgentToolCallItem = Extract; + +export function agentDisplayItem(item: CollabAgentToolCallItem, turnId?: string): DisplayItem { + const agents = agentStatesDisplay(item.agentsStates); + const receiverText = item.receiverThreadIds.length > 0 ? `\ntargets: ${item.receiverThreadIds.join(", ")}` : ""; + const promptText = item.prompt ? `\n${item.prompt}` : ""; + return { + id: item.id, + kind: "agent", + role: "tool", + text: `${agentActivitySummaryLabel(item.tool)}\nstatus: ${item.status}${receiverText}${promptText}`, + turnId, + itemId: item.id, + tool: item.tool, + status: item.status, + senderThreadId: item.senderThreadId, + receiverThreadIds: item.receiverThreadIds, + prompt: item.prompt, + model: item.model, + reasoningEffort: item.reasoningEffort, + agents, + state: collabAgentExecutionState(item.status, item.receiverThreadIds, agents), + }; +} + +export function activeAgentRunSummary(items: DisplayItem[], activeTurnId: string | null, busy: boolean): AgentRunSummary | null { + if (!busy || !activeTurnId) return null; + + const agentStatuses = new Map(); + for (const item of items) { + if (item.kind !== "agent" || item.turnId !== activeTurnId) continue; + if (item.agents.length > 0) { + for (const agent of item.agents) { + agentStatuses.set(agent.threadId, agent); + } + } else { + for (const threadId of item.receiverThreadIds) { + agentStatuses.set(threadId, { threadId, status: item.status, message: null }); + } + } + } + + if (agentStatuses.size === 0) return null; + + const summary = { running: 0, completed: 0, failed: 0, agents: [] as AgentRunSummaryAgent[], additionalAgents: 0 }; + const agents = [...agentStatuses.values()]; + for (const agent of agents) { + const state = agentRunState(agent.status); + summary[state] += 1; + } + + if (summary.running === 0 && summary.failed === 0) return null; + + const visibleAgents = agents.sort(compareActiveAgentStates).slice(0, ACTIVE_AGENT_PREVIEW_COUNT); + summary.agents = visibleAgents.map((agent) => ({ + threadId: agent.threadId, + status: agent.status, + messagePreview: agentMessagePreview(agent.message, ACTIVE_AGENT_PREVIEW_LIMIT), + })); + summary.additionalAgents = Math.max(0, agents.length - visibleAgents.length); + + return summary; +} + +function agentStatesDisplay(states: CollabAgentToolCallItem["agentsStates"]): AgentStateDisplay[] { + return Object.entries(states ?? {}) + .map(([threadId, state]) => ({ + threadId, + status: state?.status ?? "unknown", + message: state?.message ?? null, + })) + .sort((a, b) => a.threadId.localeCompare(b.threadId)); +} + +function collabAgentExecutionState(status: string, receiverThreadIds: string[], agents: AgentStateDisplay[]): ExecutionState { + if (agents.some((agent) => classifyExecutionState({ status: agent.status }) === "failed")) return "failed"; + if (agents.some((agent) => classifyExecutionState({ status: agent.status }) === "running")) return "running"; + if (agents.length > 0 && agents.every((agent) => classifyExecutionState({ status: agent.status }) === "completed")) { + return "completed"; + } + if (receiverThreadIds.length > 0 && classifyExecutionState({ status }) === "completed") return "running"; + const state = classifyExecutionState({ status }); + if (state) return state; + return null; +} + +function agentRunState(status: string): AgentRunState { + return classifyExecutionState({ status }) ?? "running"; +} + +function compareActiveAgentStates(a: AgentStateDisplay, b: AgentStateDisplay): number { + const stateDiff = agentRunStatePriority(agentRunState(a.status)) - agentRunStatePriority(agentRunState(b.status)); + if (stateDiff !== 0) return stateDiff; + return a.threadId.localeCompare(b.threadId); +} + +function agentRunStatePriority(state: AgentRunState): number { + if (state === "failed") return 0; + if (state === "running") return 1; + return 2; +} diff --git a/src/display/labels.ts b/src/display/labels.ts new file mode 100644 index 00000000..9ccf528c --- /dev/null +++ b/src/display/labels.ts @@ -0,0 +1,45 @@ +import type { TurnPlanStep } from "../generated/app-server/v2/TurnPlanStep"; +import { truncate } from "../utils"; +import type { AgentRunSummary } from "./types"; + +export function taskStatusMarker(status: TurnPlanStep["status"]): string { + if (status === "completed") return "[x]"; + if (status === "inProgress") return "[>]"; + return "[ ]"; +} + +export function agentActivitySummaryLabel(tool: string): string { + if (tool === "spawnAgent") return "Spawn agent"; + if (tool === "sendInput") return "Send input to agent"; + if (tool === "resumeAgent") return "Resume agent"; + if (tool === "wait") return "Wait for agent"; + if (tool === "closeAgent") return "Close agent"; + return `Agent ${tool}`; +} + +export function agentActivityMetaLabel(tool: string): string { + if (tool === "spawnAgent") return "spawn"; + if (tool === "sendInput") return "send input"; + if (tool === "resumeAgent") return "resume"; + if (tool === "wait") return "wait"; + if (tool === "closeAgent") return "close"; + return tool; +} + +export function agentRunSummaryLabel(summary: AgentRunSummary): string { + const parts: string[] = []; + if (summary.failed > 0) parts.push(`${summary.failed} failed`); + if (summary.running > 0) parts.push(`${summary.running} running`); + if (summary.completed > 0) parts.push(`${summary.completed} done`); + return `Agents ${parts.join(", ")}`; +} + +export function agentMessagePreview(message: string | null, maxLength: number): string | null { + if (!message) return null; + const firstLine = message + .split(/\r?\n/) + .map((line) => line.trim()) + .find((line) => line.length > 0); + if (!firstLine) return null; + return truncate(firstLine.replace(/\s+/g, " "), maxLength); +} diff --git a/src/display/model.ts b/src/display/model.ts new file mode 100644 index 00000000..14eeb8ad --- /dev/null +++ b/src/display/model.ts @@ -0,0 +1,851 @@ +import type { DisplayBlock, DisplayDetailSection, DisplayFileChange, DisplayItem, DisplayKind } from "./types"; +import type { ThreadItem } from "../generated/app-server/v2/ThreadItem"; +import type { Turn } from "../generated/app-server/v2/Turn"; +import type { TurnPlanStep } from "../generated/app-server/v2/TurnPlanStep"; +import { inputToText, jsonPreview, truncate } from "../utils"; +import { taskStatusMarker } from "./labels"; +import { agentDisplayItem } from "./agent"; +import { classifyExecutionState, executionState } from "./state"; +export { activeAgentRunSummary, agentDisplayItem } from "./agent"; +export { classifyExecutionState, executionState, executionStateLabel } from "./state"; +export { createAutoReviewResultItem, createReviewResultItem } from "./review"; + +export function displayItemsFromTurns(turns: Turn[]): DisplayItem[] { + const sortedTurns = [...turns].sort((a, b) => (a.startedAt ?? 0) - (b.startedAt ?? 0)); + const items: DisplayItem[] = []; + for (const turn of sortedTurns) { + for (const item of turn.items ?? []) { + const displayItem = displayItemFromThreadItem(item, turn.id); + if (displayItem) items.push(displayItem); + } + } + return items; +} + +export function displayItemFromThreadItem(item: ThreadItem, turnId?: string): DisplayItem | null { + if (shouldSuppressThreadItem(item)) return null; + + if (item.type === "userMessage") { + return { + id: item.id, + kind: "message", + role: "user", + text: inputToText(item.content), + turnId, + itemId: item.id, + markdown: true, + }; + } + + if (item.type === "agentMessage") { + return { + id: item.id, + kind: "message", + role: "assistant", + text: item.text, + turnId, + itemId: item.id, + markdown: true, + }; + } + + if (item.type === "commandExecution") { + return commandDisplayItem(item, turnId); + } + + if (item.type === "fileChange") { + return fileChangeDisplayItem(item, turnId); + } + + if (item.type === "plan") { + return { + id: item.id, + kind: "message", + role: "assistant", + text: normalizeProposedPlanMarkdown(item.text), + turnId, + itemId: item.id, + markdown: true, + }; + } + + if (item.type === "hookPrompt") { + return { + id: item.id, + kind: "hook", + role: "tool", + text: item.fragments.map((fragment) => fragment.text).join("\n\n") || "Hook prompt", + turnId, + itemId: item.id, + }; + } + + if (item.type === "reasoning") { + return { + id: item.id, + kind: "reasoning", + role: "tool", + text: reasoningText(item), + turnId, + itemId: item.id, + }; + } + + if (item.type === "mcpToolCall") { + const name = `${item.server}.${item.tool}`; + const target = jsonTargetLabel(item.arguments); + const failure = item.error?.message ? truncate(item.error.message, 96) : failedStatusLabel(item.status); + return { + id: item.id, + kind: "tool", + role: "tool", + text: compactToolSummary(null, target, statusQualifier(item.status, failure)), + toolLabel: name, + turnId, + itemId: item.id, + status: item.status, + details: [ + { title: "Arguments JSON", body: jsonPreview(item.arguments) }, + ...(item.result ? [{ title: "Result JSON", body: jsonPreview(item.result) }] : []), + ...(item.error ? [{ title: "Error JSON", body: jsonPreview(item.error) }] : []), + ], + output: "", + state: classifyExecutionState({ status: item.status }), + }; + } + + if (item.type === "dynamicToolCall") { + const name = `${item.namespace ? `${item.namespace}.` : ""}${item.tool}`; + const target = jsonTargetLabel(item.arguments); + const failure = item.success === false ? "failed" : failedStatusLabel(item.status); + return { + id: item.id, + kind: "tool", + role: "tool", + text: compactToolSummary(null, target, statusQualifier(item.status, failure)), + toolLabel: name, + turnId, + itemId: item.id, + status: item.status, + details: [ + { title: "Arguments JSON", body: jsonPreview(item.arguments) }, + ...(item.contentItems ? [{ title: "Result JSON", body: jsonPreview(item.contentItems) }] : []), + ], + output: "", + state: item.success === false ? "failed" : classifyExecutionState({ status: item.status }), + }; + } + + if (item.type === "collabAgentToolCall") { + return agentDisplayItem(item, turnId); + } + + if (item.type === "webSearch") { + return { + id: item.id, + kind: "tool", + role: "tool", + text: webSearchSummary(item), + toolLabel: "web search", + turnId, + itemId: item.id, + details: webSearchDetails(item), + output: "", + }; + } + + if (item.type === "imageView") { + return { + id: item.id, + kind: "tool", + role: "tool", + text: compactToolSummary(null, item.path), + toolLabel: "imageView", + turnId, + itemId: item.id, + }; + } + + if (item.type === "imageGeneration") { + const target = item.savedPath ?? item.result ?? item.revisedPrompt ?? null; + return { + id: item.id, + kind: "tool", + role: "tool", + text: compactToolSummary(null, target, statusQualifier(item.status, failedStatusLabel(item.status))), + toolLabel: "imageGeneration", + turnId, + itemId: item.id, + status: item.status, + details: [ + ...(item.savedPath ? [{ title: "Saved path", body: item.savedPath }] : []), + ...(item.revisedPrompt ? [{ title: "Revised prompt", body: item.revisedPrompt }] : []), + ...(item.result ? [{ title: "Result", body: item.result }] : []), + ], + output: "", + state: classifyExecutionState({ status: item.status }), + }; + } + + if (item.type === "enteredReviewMode" || item.type === "exitedReviewMode") { + return { + id: item.id, + kind: "tool", + role: "tool", + text: item.type === "enteredReviewMode" ? "Entered review mode" : "Exited review mode", + toolLabel: item.type, + turnId, + itemId: item.id, + output: item.review, + }; + } + + if (item.type === "contextCompaction") { + return { + id: item.id, + kind: "tool", + role: "tool", + text: "Context compaction", + toolLabel: "contextCompaction", + turnId, + itemId: item.id, + }; + } + + return null; +} + +type CommandExecutionItem = Extract; +type CommandAction = CommandExecutionItem["commandActions"][number]; +type FileChangeItem = Extract; +type ReasoningItem = Extract; +type WebSearchItem = Extract; + +const TOOL_SUMMARY_LIMIT = 140; + +function reasoningText(item: ReasoningItem): string { + return [...item.summary, ...item.content] + .map((part) => part.trim()) + .filter(Boolean) + .join("\n\n"); +} + +function compactToolSummary(label: string | null, target?: string | null, qualifier?: string | null): string { + const targetText = target?.trim(); + const base = label ? (targetText ? `${label}: ${targetText}` : label) : (targetText ?? "details"); + return truncate(qualifier ? `${base} (${qualifier})` : base, TOOL_SUMMARY_LIMIT); +} + +function statusQualifier(status: unknown, failure?: string | null): string | null { + if (status === "declined") return "declined"; + if (status === "failed") return failure || "failed"; + return null; +} + +function failedStatusLabel(status: unknown): string | null { + if (status === "failed") return "failed"; + if (status === "declined") return "declined"; + return null; +} + +function commandTargetLabel(item: CommandExecutionItem): string { + const action = representativeCommandAction(item.commandActions); + if (action?.type === "search") { + const query = commandActionValue(action.query); + const path = commandActionValue(action.path); + if (query && path) return `${quoteInline(query)} in ${path}`; + if (query) return quoteInline(query); + if (path) return path; + } + if (action?.type === "read") return commandReadTargetLabel(action, item.cwd); + if (action?.type === "listFiles") return commandActionValue(action.path) ?? "workspace"; + return unwrapShellLoginCommand(firstCommandLine(item.command)); +} + +function commandActionLabel(item: CommandExecutionItem): string { + const action = representativeCommandAction(item.commandActions); + if (action?.type === "read") return "read"; + if (action?.type === "search") return "search"; + if (action?.type === "listFiles") return "list files"; + return "command"; +} + +function representativeCommandAction(actions: CommandAction[]): CommandAction | null { + return ( + actions.find((action) => action.type === "read") ?? + actions.find((action) => action.type === "search") ?? + actions.find((action) => action.type === "listFiles") ?? + actions[0] ?? + null + ); +} + +function commandActionValue(value: string | null): string | null { + const trimmed = value?.trim(); + return trimmed ? trimmed : null; +} + +function commandReadTargetLabel(action: Extract, cwd: string): string { + const path = commandActionValue(action.path); + if (path) return pathRelativeToWorkspace(path, cwd); + return action.name; +} + +function firstCommandLine(command: string): string { + return ( + command + .split("\n") + .map((line) => line.trim()) + .find(Boolean) ?? command.trim() + ); +} + +function unwrapShellLoginCommand(command: string): string { + const match = command.match(/^(?:\/bin\/)?zsh\s+-lc\s+(.+)$/); + if (!match) return command; + return unquoteShellCommand(match[1].trim()); +} + +function unquoteShellCommand(value: string): string { + if (value.length < 2) return value; + const quote = value[0]; + if ((quote !== "'" && quote !== '"') || value[value.length - 1] !== quote) return value; + const inner = value.slice(1, -1); + return quote === "'" ? inner.replace(/'\\''/g, "'") : inner.replace(/\\(["\\$`])/g, "$1"); +} + +function quoteInline(value: string): string { + return value.includes(" ") ? JSON.stringify(value) : value; +} + +function fileChangeTargetLabel(changes: DisplayFileChange[]): string { + if (changes.length === 0) return "no files"; + if (changes.length === 1) return changes[0]?.path ?? "1 file"; + return `${changes.length} files`; +} + +function webSearchTarget(item: WebSearchItem): string | null { + if (item.action?.type === "openPage") return item.action.url; + if (item.action?.type === "findInPage") return item.action.pattern ?? item.action.url; + if (item.action?.type === "search") return webSearchQueryList(item.action.query, item.action.queries, item.query); + return item.query; +} + +function webSearchSummary(item: WebSearchItem): string { + const actionType = item.action?.type ?? (item.query ? "search" : "web search"); + const label = webSearchActionLabel(actionType); + return compactToolSummary(label, webSearchTarget(item)); +} + +function webSearchActionLabel(actionType: string): string { + if (actionType === "openPage") return "open page"; + if (actionType === "findInPage") return "find in page"; + if (actionType === "search") return "search"; + if (actionType === "other") return "other"; + return actionType; +} + +function webSearchQueryList( + query: string | null | undefined, + queries: string[] | null | undefined, + fallback?: string | null, +): string | null { + const values = [query, ...(queries ?? []), fallback].map((value) => value?.trim()).filter((value): value is string => Boolean(value)); + const unique = [...new Set(values)]; + if (unique.length === 0) return null; + if (unique.length === 1) return unique[0] ?? null; + return unique.join("; "); +} + +function webSearchDetails(item: WebSearchItem): DisplayDetailSection[] { + const rows: Array<{ key: string; value: string }> = []; + if (item.action) rows.push({ key: "action", value: webSearchActionLabel(item.action.type) }); + if (item.action?.type === "search") { + const queries = webSearchQueryList(item.action.query, item.action.queries, item.query); + if (queries) rows.push({ key: "query", value: queries }); + } else if (item.action?.type === "openPage") { + if (item.action.url) rows.push({ key: "url", value: item.action.url }); + } else if (item.action?.type === "findInPage") { + if (item.action.pattern) rows.push({ key: "pattern", value: item.action.pattern }); + if (item.action.url) rows.push({ key: "url", value: item.action.url }); + } else if (item.query) { + rows.push({ key: "query", value: item.query }); + } + + return rows.length > 0 ? [{ title: "web search", rows }] : []; +} + +function jsonTargetLabel(value: unknown): string | null { + const direct = jsonTargetPrimitive(value); + if (direct) return direct; + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + + const record = value as Record; + const priorityKeys = [ + "q", + "query", + "search_query", + "url", + "ref_id", + "path", + "file", + "filename", + "ticker", + "location", + "team", + "league", + "id", + "target", + "command", + ]; + + for (const key of priorityKeys) { + const target = jsonTargetPrimitive(record[key]); + if (target) return target; + } + + const firstEntry = Object.entries(record).find(([, entryValue]) => jsonTargetPrimitive(entryValue)); + return firstEntry ? jsonTargetPrimitive(firstEntry[1]) : null; +} + +function jsonTargetPrimitive(value: unknown): string | null { + if (typeof value === "string" && value.trim().length > 0) return value.trim(); + if (typeof value === "number" || typeof value === "boolean") return String(value); + if (!Array.isArray(value)) return null; + for (const item of value) { + const target = jsonTargetLabel(item); + if (target) return target; + } + return null; +} + +export function commandDisplayItem(item: CommandExecutionItem, turnId?: string): DisplayItem { + const exitCode = typeof item.exitCode === "number" ? item.exitCode : undefined; + const durationMs = typeof item.durationMs === "number" ? item.durationMs : undefined; + const target = commandTargetLabel(item); + const qualifier = + typeof exitCode === "number" && exitCode !== 0 ? `exit ${exitCode}` : statusQualifier(item.status, failedStatusLabel(item.status)); + return { + id: item.id, + kind: "command", + role: "tool", + actionLabel: commandActionLabel(item), + text: compactToolSummary(null, target, qualifier), + turnId, + itemId: item.id, + command: item.command, + cwd: item.cwd ?? "(unknown)", + status: item.status ?? "(unknown)", + exitCode, + durationMs, + output: item.aggregatedOutput ?? "", + state: classifyExecutionState({ exitCode, status: item.status }), + }; +} + +export function fileChangeDisplayItem(item: FileChangeItem, turnId?: string): DisplayItem { + const changes = normalizeFileChanges(item.changes ?? []); + const qualifier = statusQualifier(item.status, failedStatusLabel(item.status)); + return { + id: item.id, + kind: "fileChange", + role: "tool", + text: compactToolSummary(null, fileChangeTargetLabel(changes), qualifier), + turnId, + itemId: item.id, + status: item.status, + changes, + state: classifyExecutionState({ status: item.status }), + }; +} + +export function planProgressDisplayItem(turnId: string, explanation: string | null, plan: TurnPlanStep[]): DisplayItem { + const lines = plan.map((step) => `${taskStatusMarker(step.status)} ${step.step}`); + const body = [explanation?.trim(), ...lines].filter((line): line is string => Boolean(line && line.length > 0)).join("\n"); + const status = plan.some((step) => step.status === "inProgress" || step.status === "pending") ? "inProgress" : "completed"; + return { + id: `plan-progress-${turnId}`, + kind: "taskProgress", + role: "tool", + text: body || "Plan updated", + turnId, + itemId: `plan-progress-${turnId}`, + explanation: explanation?.trim() || null, + steps: plan.map((step) => ({ step: step.step, status: step.status })), + status, + state: classifyExecutionState({ status }), + }; +} + +export function normalizeFileChanges(changes: unknown[]): DisplayFileChange[] { + return changes.flatMap((change) => { + if (!change || typeof change !== "object") return []; + const record = change as { kind?: unknown; path?: unknown; diff?: unknown; unified_diff?: unknown }; + return [ + { + kind: typeof record.kind === "string" ? record.kind : "changed", + path: typeof record.path === "string" && record.path.length > 0 ? record.path : "(unknown)", + diff: typeof record.diff === "string" ? record.diff : typeof record.unified_diff === "string" ? record.unified_diff : "", + }, + ]; + }); +} + +export function shouldSuppressThreadItem(item: { type: string }): boolean { + return item.type === "outputMessage" || item.type === "toolOutputMessage"; +} + +export function shouldSuppressLifecycleItem(item: ThreadItem): boolean { + return item.type === "agentMessage" || item.type === "userMessage"; +} + +export function upsertDisplayItem(items: DisplayItem[], next: DisplayItem): DisplayItem[] { + const index = items.findIndex((item) => item.id === next.id); + if (index === -1) return [...items, next]; + const copy = [...items]; + const previous = copy[index]; + copy[index] = { + ...previous, + ...next, + output: mergeOutput(previous, next), + changes: mergeChanges(previous, next), + } as DisplayItem; + return copy; +} + +function mergeOutput(previous: DisplayItem, next: DisplayItem): string | undefined { + const previousOutput = "output" in previous ? previous.output : undefined; + const nextOutput = "output" in next ? next.output : undefined; + return nextOutput && nextOutput.length > 0 ? nextOutput : previousOutput; +} + +function mergeChanges(previous: DisplayItem, next: DisplayItem): DisplayFileChange[] | undefined { + const previousChanges = previous.kind === "fileChange" ? previous.changes : undefined; + const nextChanges = next.kind === "fileChange" ? next.changes : undefined; + return nextChanges && nextChanges.length > 0 ? nextChanges : previousChanges; +} + +export function appendAssistantDelta(items: DisplayItem[], itemId: string, turnId: string, delta: string): DisplayItem[] { + const index = items.findIndex((item) => item.itemId === itemId && item.kind === "message" && item.role === "assistant"); + if (index !== -1) { + return items.map((item, itemIndex) => + itemIndex === index && item.kind === "message" + ? { + ...item, + text: `${item.text}${delta}`, + turnId: item.turnId ?? turnId, + markdown: false, + } + : item, + ); + } + return [ + ...items, + { + id: itemId, + kind: "message", + role: "assistant", + text: delta, + turnId, + itemId, + markdown: false, + }, + ]; +} + +export function completeReasoningItems(items: DisplayItem[], turnId: string): DisplayItem[] { + return items.map((item) => + item.kind === "reasoning" && item.turnId === turnId + ? { + ...item, + status: "completed", + state: "completed", + } + : item, + ); +} + +export function appendPlanDelta(items: DisplayItem[], itemId: string, turnId: string, delta: string): DisplayItem[] { + const index = items.findIndex((item) => item.itemId === itemId && item.kind === "message" && item.role === "assistant"); + if (index !== -1) { + return items.map((item, itemIndex) => + itemIndex === index && item.kind === "message" + ? { + ...item, + text: normalizeProposedPlanMarkdown(`${item.text}${delta}`), + turnId: item.turnId ?? turnId, + markdown: false, + } + : item, + ); + } + return [ + ...items, + { + id: itemId, + kind: "message", + role: "assistant", + text: normalizeProposedPlanMarkdown(delta), + turnId, + itemId, + markdown: false, + }, + ]; +} + +export function normalizeProposedPlanMarkdown(text: string): string { + return text + .replace(/^\s*\s*\n?/i, "") + .replace(/\n?\s*<\/proposed_plan>\s*$/i, "") + .trim(); +} + +export function appendItemText( + items: DisplayItem[], + itemId: string, + turnId: string, + label: string, + delta: string, + kind: Extract = "tool", +): DisplayItem[] { + const index = items.findIndex((item) => item.itemId === itemId); + if (index !== -1) { + return items.map((item, itemIndex) => (itemIndex === index ? { ...item, text: `${item.text}${delta}` } : item)); + } + return [ + ...items, + { + id: itemId, + kind, + role: "tool", + text: `${label}: ${delta}`, + turnId, + itemId, + }, + ]; +} + +export function appendToolOutput( + items: DisplayItem[], + itemId: string, + turnId: string, + delta: string, + fallbackLabel: string, +): DisplayItem[] { + const index = items.findIndex((item) => item.itemId === itemId); + if (index !== -1) { + return items.map((item, itemIndex) => + itemIndex === index && (item.kind === "tool" || item.kind === "hook" || item.kind === "reasoning") + ? ({ ...item, output: `${item.output ?? ""}${delta}` } as DisplayItem) + : item, + ); + } + return [ + ...items, + { + id: itemId, + kind: "tool", + role: "tool", + text: "details", + toolLabel: fallbackLabel, + turnId, + itemId, + output: delta, + }, + ]; +} + +export function appendItemOutput( + items: DisplayItem[], + itemId: string, + turnId: string, + delta: string, + kind: "command" | "fileChange", + fallbackText: string, +): DisplayItem[] { + const index = items.findIndex((item) => item.itemId === itemId); + if (index !== -1) { + return items.map((item, itemIndex) => + itemIndex === index && (item.kind === "command" || item.kind === "fileChange") + ? ({ ...item, output: `${item.output ?? ""}${delta}` } as DisplayItem) + : item, + ); + } + return [ + ...items, + { + id: itemId, + kind, + role: "tool", + text: fallbackText, + turnId, + itemId, + output: delta, + ...(kind === "fileChange" + ? { + status: "inProgress", + changes: [], + } + : { + command: fallbackText, + cwd: "(unknown)", + status: "running", + }), + }, + ] as DisplayItem[]; +} + +export function displayBlocksForItems(items: DisplayItem[], activeTurnId: string | null, workspaceRoot?: string | null): DisplayBlock[] { + const visibleItems = items.filter(shouldShowDisplayItem); + const orderedItems = activeTurnId ? moveActiveTaskProgressToEnd(visibleItems, activeTurnId) : visibleItems; + const editedFilesByTurn = editedFilesForTurns(visibleItems, workspaceRoot); + const finalAssistantIdByTurn = finalAssistantItemsByTurn(visibleItems); + const groupedTurnIds = new Set([...finalAssistantIdByTurn.keys()].filter((turnId) => turnId !== activeTurnId)); + + const groupedActivities = new Map(); + for (const item of orderedItems) { + if (!item.turnId || !groupedTurnIds.has(item.turnId) || !isCompletedTurnDetailItem(item, finalAssistantIdByTurn)) continue; + const group = groupedActivities.get(item.turnId) ?? []; + group.push(item); + groupedActivities.set(item.turnId, group); + } + + const emittedGroups = new Set(); + const blocks: DisplayBlock[] = []; + for (const item of orderedItems) { + const turnId = item.turnId; + if (turnId && groupedActivities.has(turnId) && isCompletedTurnDetailItem(item, finalAssistantIdByTurn)) { + if (!emittedGroups.has(turnId)) { + const groupItems = groupedActivities.get(turnId) ?? []; + blocks.push({ + type: "activityGroup", + id: `turn-${turnId}-activity`, + turnId, + summary: turnActivitySummary(groupItems), + items: groupItems, + }); + emittedGroups.add(turnId); + } + continue; + } + blocks.push({ type: "item", item: itemWithEditedFiles(item, editedFilesByTurn, finalAssistantIdByTurn) }); + } + + return blocks; +} + +function moveActiveTaskProgressToEnd(items: DisplayItem[], activeTurnId: string): DisplayItem[] { + const activeTaskProgress = items.filter((item) => item.kind === "taskProgress" && item.turnId === activeTurnId); + if (activeTaskProgress.length === 0) return items; + return [...items.filter((item) => item.kind !== "taskProgress" || item.turnId !== activeTurnId), ...activeTaskProgress]; +} + +function shouldShowDisplayItem(item: DisplayItem): boolean { + return item.kind !== "reasoning" || executionState(item) !== "completed" || item.text.trim().length > 0; +} + +function isCompletedTurnDetailItem(item: DisplayItem, finalAssistantIdByTurn: Map): boolean { + if (!item.turnId || item.role === "user") return false; + return finalAssistantIdByTurn.get(item.turnId) !== item.id; +} + +function finalAssistantItemsByTurn(items: DisplayItem[]): Map { + const finalAssistantIdByTurn = new Map(); + for (const item of items) { + if (!item.turnId || !isFinalAssistantMessage(item)) continue; + finalAssistantIdByTurn.set(item.turnId, item.id); + } + return finalAssistantIdByTurn; +} + +function isFinalAssistantMessage(item: DisplayItem): boolean { + return item.kind === "message" && item.role === "assistant" && item.markdown !== false; +} + +function itemWithEditedFiles( + item: DisplayItem, + editedFilesByTurn: Map, + finalAssistantIdByTurn: Map, +): DisplayItem { + if (!item.turnId || finalAssistantIdByTurn.get(item.turnId) !== item.id) return item; + if (item.kind !== "message") return item; + const editedFiles = editedFilesByTurn.get(item.turnId); + if (!editedFiles || editedFiles.length === 0) return item; + return { ...item, editedFiles }; +} + +function editedFilesForTurns(items: DisplayItem[], workspaceRoot?: string | null): Map { + const byTurn = new Map>(); + for (const item of items) { + if (!item.turnId || item.kind !== "fileChange") continue; + const files = editedFilesForItem(item, workspaceRoot); + if (files.length === 0) continue; + const set = byTurn.get(item.turnId) ?? new Set(); + files.forEach((file) => set.add(file)); + byTurn.set(item.turnId, set); + } + + return new Map([...byTurn].map(([turnId, files]) => [turnId, [...files].sort((a, b) => a.localeCompare(b))])); +} + +function editedFilesForItem(item: DisplayItem, workspaceRoot?: string | null): string[] { + if (item.kind !== "fileChange") return []; + return item.changes.flatMap((change) => + change.path && change.path !== "(unknown)" ? [pathRelativeToWorkspace(change.path, workspaceRoot)] : [], + ); +} + +export function pathRelativeToWorkspace(path: string, workspaceRoot?: string | null): string { + const normalizedPath = path.replace(/\\/g, "/").replace(/^\.\//, ""); + const root = workspaceRoot?.replace(/\\/g, "/").replace(/\/+$/, ""); + if (!root) return normalizedPath; + if (normalizedPath === root) return "."; + return normalizedPath.startsWith(`${root}/`) ? normalizedPath.slice(root.length + 1) : normalizedPath; +} + +function turnActivitySummary(items: DisplayItem[]): string { + const parts = [ + countMatchingLabel(items, (item) => item.kind === "message" && item.role === "assistant", "response", "responses"), + countLabel(items, "taskProgress", "task progress"), + countLabel(items, "agent", "agent"), + countLabel(items, "command", "command"), + countLabel(items, "fileChange", "file change"), + countLabel(items, "tool", "tool"), + countLabel(items, "hook", "hook"), + countLabel(items, "reasoning", "thought", "thought notes"), + countLabel(items, "approvalResult", "approval"), + countLabel(items, "userInputResult", "input"), + countLabel(items, "reviewResult", "review"), + ].filter((part): part is string => Boolean(part)); + + if (parts.length === 0) return "Work details"; + return `Work details: ${parts.join(", ")}`; +} + +function countMatchingLabel( + items: DisplayItem[], + predicate: (item: DisplayItem) => boolean, + label: string, + pluralLabel = `${label}s`, +): string | null { + const count = items.filter(predicate).length; + if (count === 0) return null; + if (count === 1) return label; + return `${count} ${pluralLabel}`; +} + +function countLabel(items: DisplayItem[], kind: DisplayKind, label: string, pluralLabel = `${label}s`): string | null { + const count = items.filter((item) => item.kind === kind).length; + if (count === 0) return null; + if (count === 1) return label; + return `${count} ${pluralLabel}`; +} + +export function createSystemItem(text: string): DisplayItem { + return { + id: `system-${Date.now()}-${Math.random().toString(36).slice(2)}`, + kind: "system", + role: "system", + text, + }; +} diff --git a/src/display/review.ts b/src/display/review.ts new file mode 100644 index 00000000..2e732471 --- /dev/null +++ b/src/display/review.ts @@ -0,0 +1,173 @@ +import type { FileSystemPath } from "../generated/app-server/v2/FileSystemPath"; +import type { GuardianApprovalReviewAction } from "../generated/app-server/v2/GuardianApprovalReviewAction"; +import type { ItemGuardianApprovalReviewCompletedNotification } from "../generated/app-server/v2/ItemGuardianApprovalReviewCompletedNotification"; +import type { ItemGuardianApprovalReviewStartedNotification } from "../generated/app-server/v2/ItemGuardianApprovalReviewStartedNotification"; +import type { DisplayItem } from "./types"; +import { classifyExecutionState } from "./state"; + +type AutoReviewNotification = ItemGuardianApprovalReviewStartedNotification | ItemGuardianApprovalReviewCompletedNotification; +type DisplayRow = { key: string; value: string }; + +export function createReviewResultItem(text: string): DisplayItem { + const parsed = parseAutomaticApprovalReviewMessage(text); + if (parsed) { + return { + id: `review-${Date.now()}-${Math.random().toString(36).slice(2)}`, + kind: "reviewResult", + role: "tool", + text: parsed.summary, + markdown: false, + state: classifyExecutionState({ status: parsed.status }), + details: [{ title: "Review", rows: parsed.rows }], + }; + } + return { + id: `review-${Date.now()}-${Math.random().toString(36).slice(2)}`, + kind: "reviewResult", + role: "tool", + text, + markdown: false, + }; +} + +export function createAutoReviewResultItem(params: AutoReviewNotification): DisplayItem { + const completed = "decisionSource" in params; + const status = params.review.status; + const action = autoReviewActionLabel(params.action); + const text = completed ? `Auto-review ${status}: ${action}` : `Auto-review started: ${action}`; + const rows = [ + { key: "status", value: status }, + ...("decisionSource" in params ? [{ key: "source", value: params.decisionSource }] : []), + ...(params.review.riskLevel ? [{ key: "risk", value: params.review.riskLevel }] : []), + ...(params.review.userAuthorization ? [{ key: "authorization", value: params.review.userAuthorization }] : []), + ...autoReviewActionRows(params.action), + ...(params.targetItemId ? [{ key: "target", value: params.targetItemId }] : []), + ...(params.review.rationale ? [{ key: "rationale", value: params.review.rationale }] : []), + ]; + return { + id: `review-${params.reviewId}`, + kind: "reviewResult", + role: "tool", + text, + turnId: params.turnId, + markdown: false, + state: completed ? classifyExecutionState({ status }) : "running", + details: [{ title: "Review", rows }], + }; +} + +function parseAutomaticApprovalReviewMessage( + text: string, +): { status: string; summary: string; rows: Array<{ key: string; value: string }> } | null { + const match = /^Automatic approval review\s+([a-zA-Z][\w-]*)(?:\s+\(([^)]*)\))?:\s*(.+)$/i.exec(text.trim()); + if (!match) return null; + + const status = match[1]?.trim() ?? "review"; + const fieldText = match[2]?.trim(); + const message = match[3]?.trim() ?? ""; + const rows = [{ key: "status", value: status }]; + for (const field of fieldText?.split(",") ?? []) { + const fieldMatch = /^\s*([^:]+):\s*(.+?)\s*$/.exec(field); + if (fieldMatch?.[1] && fieldMatch[2]) rows.push({ key: fieldMatch[1].trim(), value: fieldMatch[2].trim() }); + } + if (message) rows.push({ key: "message", value: message }); + + return { + status, + summary: message ? `Auto-review ${status}: ${message}` : `Auto-review ${status}`, + rows, + }; +} + +function autoReviewActionRows(action: GuardianApprovalReviewAction): DisplayRow[] { + if (action.type === "command") { + return [ + { key: "action", value: "command" }, + { key: "command", value: action.command }, + { key: "cwd", value: action.cwd }, + { key: "action source", value: action.source }, + ]; + } + if (action.type === "execve") { + return [ + { key: "action", value: "execve" }, + { key: "program", value: action.program }, + { key: "argv", value: action.argv.join(" ") }, + { key: "cwd", value: action.cwd }, + { key: "action source", value: action.source }, + ]; + } + if (action.type === "applyPatch") { + return [ + { key: "action", value: "apply patch" }, + { key: "cwd", value: action.cwd }, + { key: "files", value: action.files.length > 0 ? action.files.join("\n") : "(none)" }, + ]; + } + if (action.type === "networkAccess") { + return [ + { key: "action", value: "network access" }, + { key: "target", value: action.target }, + { key: "protocol", value: action.protocol }, + { key: "host", value: action.host }, + { key: "port", value: String(action.port) }, + ]; + } + if (action.type === "mcpToolCall") { + return [ + { key: "action", value: "MCP tool call" }, + { key: "server", value: action.server }, + { key: "tool", value: action.toolName }, + ...(action.toolTitle ? [{ key: "title", value: action.toolTitle }] : []), + ...(action.connectorName ? [{ key: "connector", value: action.connectorName }] : []), + ...(action.connectorId ? [{ key: "connector id", value: action.connectorId }] : []), + ]; + } + if (action.type === "requestPermissions") { + return [ + { key: "action", value: "request permissions" }, + ...(action.reason ? [{ key: "reason", value: action.reason }] : []), + ...permissionRows(action.permissions), + ]; + } + return [{ key: "action", value: "review action" }]; +} + +function permissionRows(permissions: Extract["permissions"]): DisplayRow[] { + const rows: DisplayRow[] = []; + if (permissions.network?.enabled !== null && permissions.network?.enabled !== undefined) { + rows.push({ key: "network", value: permissions.network.enabled ? "enabled" : "disabled" }); + } + const fileSystem = permissions.fileSystem; + if (!fileSystem) return rows; + if (fileSystem.entries && fileSystem.entries.length > 0) { + rows.push({ + key: "filesystem", + value: fileSystem.entries.map((entry) => `${fileSystemPathLabel(entry.path)} (${entry.access})`).join("\n"), + }); + } + if (fileSystem.read && fileSystem.read.length > 0) rows.push({ key: "read", value: fileSystem.read.join("\n") }); + if (fileSystem.write && fileSystem.write.length > 0) rows.push({ key: "write", value: fileSystem.write.join("\n") }); + if (fileSystem.globScanMaxDepth !== null && fileSystem.globScanMaxDepth !== undefined) { + rows.push({ key: "glob depth", value: String(fileSystem.globScanMaxDepth) }); + } + return rows; +} + +function fileSystemPathLabel(path: FileSystemPath): string { + if (path.type === "path") return path.path; + if (path.type === "glob_pattern") return path.pattern; + if (path.value.kind === "project_roots") return path.value.subpath ? `project_roots/${path.value.subpath}` : "project_roots"; + if (path.value.kind === "unknown") return path.value.subpath ? `${path.value.path}/${path.value.subpath}` : path.value.path; + return path.value.kind; +} + +function autoReviewActionLabel(action: GuardianApprovalReviewAction): string { + if (action.type === "command") return action.command; + if (action.type === "execve") return [action.program, ...action.argv].join(" "); + if (action.type === "applyPatch") return `apply patch (${action.files.length} files)`; + if (action.type === "networkAccess") return `${action.protocol}://${action.host}:${action.port}`; + if (action.type === "mcpToolCall") return `${action.server}.${action.toolName}`; + if (action.type === "requestPermissions") return action.reason ?? "permission request"; + return "review action"; +} diff --git a/src/display/signature.ts b/src/display/signature.ts new file mode 100644 index 00000000..f3ce5dfd --- /dev/null +++ b/src/display/signature.ts @@ -0,0 +1,56 @@ +import { executionState } from "./model"; +import type { DisplayItem } from "./types"; + +export interface DisplayItemSignatureContext { + busy: boolean; + activeTurnId: string | null; + displayItems: DisplayItem[]; + workspaceRoot?: string | null; +} + +export function displayItemSignature(item: DisplayItem, context: DisplayItemSignatureContext): string { + return [ + item.id, + item.kind, + item.role, + item.turnId ?? "", + item.itemId ?? "", + item.text, + "markdown" in item ? String(item.markdown ?? true) : "", + "output" in item ? (item.output ?? "") : "", + "details" in item ? JSON.stringify(item.details ?? []) : "", + item.kind === "message" ? (item.editedFiles?.join("\n") ?? "") : "", + item.kind === "reasoning" && isReasoningActive(item, context) ? "reasoning-active" : "", + executionState(item) ?? "", + item.kind === "fileChange" ? JSON.stringify(item.changes) : "", + item.kind === "fileChange" ? (context.workspaceRoot ?? "") : "", + item.kind === "taskProgress" ? JSON.stringify({ explanation: item.explanation, steps: item.steps, status: item.status }) : "", + item.kind === "agent" + ? JSON.stringify({ + tool: item.tool, + status: item.status, + senderThreadId: item.senderThreadId, + receiverThreadIds: item.receiverThreadIds, + prompt: item.prompt, + model: item.model, + reasoningEffort: item.reasoningEffort, + agents: item.agents, + }) + : "", + item.kind === "command" ? [item.command, item.cwd, item.status, item.exitCode ?? "", item.durationMs ?? ""].join("\n") : "", + item.kind === "fileChange" ? item.status : "", + item.kind === "tool" || item.kind === "taskProgress" || item.kind === "agent" || item.kind === "hook" || item.kind === "reasoning" + ? (item.status ?? "") + : "", + ].join("\u0000"); +} + +export function isReasoningActive( + item: DisplayItem, + context: Pick, +): boolean { + if (!context.busy || !context.activeTurnId || item.turnId !== context.activeTurnId) return false; + if (executionState(item) === "completed") return false; + const latestActiveTurnItem = [...context.displayItems].reverse().find((candidate) => candidate.turnId === context.activeTurnId); + return latestActiveTurnItem?.id === item.id; +} diff --git a/src/display/state.ts b/src/display/state.ts new file mode 100644 index 00000000..296896e9 --- /dev/null +++ b/src/display/state.ts @@ -0,0 +1,38 @@ +import type { DisplayItem, ExecutionState } from "./types"; + +export function executionState(item: DisplayItem): ExecutionState { + if (item.state) return item.state; + const exitCode = item.kind === "command" ? item.exitCode : undefined; + const status = + item.kind === "command" || + item.kind === "fileChange" || + item.kind === "tool" || + item.kind === "taskProgress" || + item.kind === "agent" || + item.kind === "hook" || + item.kind === "reasoning" + ? item.status + : undefined; + return classifyExecutionState({ exitCode, status }); +} + +export function classifyExecutionState(input: { exitCode?: number; status?: unknown }): ExecutionState { + if (typeof input.exitCode === "number" && input.exitCode !== 0) return "failed"; + + const statusText = [input.status] + .filter((value) => value !== null && value !== undefined) + .join(" ") + .toLowerCase(); + + if (/(fail|error|errored|notfound|not_found|missing|denied|declin|cancel|reject|aborted)/.test(statusText)) return "failed"; + if (/(running|in[_ -]?progress|queued|pending|started)/.test(statusText)) return "running"; + if (/(completed|complete|success|succeeded|approved|allowed|applied|finished|done)/.test(statusText)) return "completed"; + if (typeof input.exitCode === "number" && input.exitCode === 0) return "completed"; + return null; +} + +export function executionStateLabel(state: Exclude): string { + if (state === "running") return "Running"; + if (state === "failed") return "Failed"; + return "Done"; +} diff --git a/src/display/tool-view.ts b/src/display/tool-view.ts new file mode 100644 index 00000000..d6311872 --- /dev/null +++ b/src/display/tool-view.ts @@ -0,0 +1,139 @@ +import { executionState, pathRelativeToWorkspace } from "./model"; +import type { + CommandDisplayItem, + DisplayDetailSection, + DisplayFileChange, + DisplayItem, + ExecutionState, + FileChangeDisplayItem, + ReviewResultDisplayItem, + ToolDisplayItem, +} from "./types"; + +export type ToolResultDisplayItem = CommandDisplayItem | FileChangeDisplayItem | ToolDisplayItem | ReviewResultDisplayItem; + +export type ToolResultDetailSection = + | { kind: "meta"; title?: string; rows: Array<{ key: string; value: string }> } + | { kind: "output"; title: string; body: string } + | { kind: "diff"; title: string; diff: string }; + +export interface ToolResultView { + className: string; + label: string; + summary: string; + detailsKey: string; + details: ToolResultDetailSection[]; + state: ExecutionState; +} + +export function toolResultView(item: ToolResultDisplayItem, workspaceRoot?: string | null): ToolResultView { + if (item.kind === "command") return commandToolView(item); + if (item.kind === "fileChange") return fileChangeToolView(item, workspaceRoot); + if (item.kind === "reviewResult") return reviewToolView(item); + return genericToolView(item); +} + +function commandToolView(item: CommandDisplayItem): ToolResultView { + const details: ToolResultDetailSection[] = [ + { + kind: "meta", + rows: [ + { key: "command", value: item.command }, + { key: "cwd", value: item.cwd }, + { key: "status", value: item.status }, + ...(item.exitCode !== null && item.exitCode !== undefined ? [{ key: "exit", value: String(item.exitCode) }] : []), + ...(item.durationMs !== null && item.durationMs !== undefined ? [{ key: "duration", value: `${item.durationMs}ms` }] : []), + ], + }, + ...outputSection("Output", item.output), + ]; + return { + className: "codex-panel__message codex-panel__message--tool codex-panel__tool-item", + label: item.actionLabel ?? "command", + summary: item.text, + detailsKey: `${item.id}:command-details`, + details, + state: executionState(item), + }; +} + +function fileChangeToolView(item: FileChangeDisplayItem, workspaceRoot?: string | null): ToolResultView { + const displayChanges = item.changes.map((change) => ({ + ...change, + displayPath: change.path && change.path !== "(unknown)" ? pathRelativeToWorkspace(change.path, workspaceRoot) : change.path, + })); + const details: ToolResultDetailSection[] = [ + { + kind: "meta", + rows: [ + { key: "status", value: item.status }, + { key: "files", value: String(item.changes.length) }, + ], + }, + ...displayChanges.map((change) => ({ + kind: "diff" as const, + title: `${change.kind ?? "changed"} ${change.displayPath ?? "(unknown)"}`, + diff: change.diff ?? "", + })), + ...outputSection("Patch output", item.output), + ]; + return { + className: "codex-panel__message codex-panel__message--tool codex-panel__file-change", + label: "file change", + summary: fileChangeSummary(item, displayChanges), + detailsKey: `${item.id}:file-change-details`, + details, + state: executionState(item), + }; +} + +function genericToolView(item: ToolDisplayItem): ToolResultView { + return { + className: `codex-panel__message codex-panel__message--tool codex-panel__tool-item codex-panel__tool-item--${item.kind}`, + label: item.toolLabel ?? item.kind, + summary: item.text, + detailsKey: `${item.id}:details`, + details: [ + ...(item.details ?? []).flatMap(detailSection), + ...outputSection(item.kind === "hook" ? "Hook output" : "Output", item.output), + ], + state: executionState(item), + }; +} + +function reviewToolView(item: ReviewResultDisplayItem): ToolResultView { + return { + className: + "codex-panel__message codex-panel__message--tool codex-panel__message--review-result codex-panel__tool-item codex-panel__tool-item--review", + label: "auto-review", + summary: item.text, + detailsKey: `${item.id}:review-details`, + details: (item.details ?? []).flatMap(reviewDetailSection), + state: executionState(item), + }; +} + +function reviewDetailSection(section: DisplayDetailSection): ToolResultDetailSection[] { + if (section.rows && section.rows.length > 0) return [{ kind: "meta", rows: section.rows }]; + return detailSection(section); +} + +function detailSection(section: DisplayDetailSection): ToolResultDetailSection[] { + if (section.rows && section.rows.length > 0) return [{ kind: "meta", title: section.title, rows: section.rows }]; + if (section.body) return [{ kind: "output", title: section.title ?? "Output", body: section.body }]; + return []; +} + +function outputSection(title: string, body: string | null | undefined): ToolResultDetailSection[] { + return body ? [{ kind: "output", title, body }] : []; +} + +function fileChangeSummary(item: DisplayItem, changes: Array): string { + if (item.kind !== "fileChange") return item.text; + if (changes.length === 0) return item.text; + if (changes.length > 1) return item.text; + const relativePath = changes[0]?.displayPath; + if (!relativePath || relativePath === "(unknown)") return item.text; + const suffixMatch = /\s\(([^)]+)\)$/.exec(item.text); + return suffixMatch ? `${relativePath} (${suffixMatch[1]})` : relativePath; +} diff --git a/src/display/types.ts b/src/display/types.ts new file mode 100644 index 00000000..7df4e93f --- /dev/null +++ b/src/display/types.ts @@ -0,0 +1,161 @@ +export type DisplayKind = + | "message" + | "command" + | "fileChange" + | "tool" + | "taskProgress" + | "agent" + | "hook" + | "reasoning" + | "system" + | "approvalResult" + | "userInputResult" + | "reviewResult"; +export type DisplayRole = "user" | "assistant" | "system" | "tool"; +export type ExecutionState = "running" | "completed" | "failed" | null; + +export interface DisplayBase { + id: string; + kind: DisplayKind; + role: DisplayRole; + text: string; + turnId?: string; + itemId?: string; + state?: ExecutionState; +} + +export interface DisplayDetailMetaRow { + key: string; + value: string; +} + +export interface DisplayDetailSection { + title?: string; + body?: string; + rows?: DisplayDetailMetaRow[]; +} + +export interface MessageDisplayItem extends DisplayBase { + kind: "message"; + role: "user" | "assistant"; + editedFiles?: string[]; + markdown?: boolean; +} + +export interface SystemDisplayItem extends DisplayBase { + kind: "system" | "approvalResult" | "userInputResult"; + role: "system" | "tool"; + markdown?: boolean; + details?: DisplayDetailSection[]; +} + +export interface ReviewResultDisplayItem extends DisplayBase { + kind: "reviewResult"; + role: "tool"; + markdown?: boolean; + details?: DisplayDetailSection[]; +} + +export interface CommandDisplayItem extends DisplayBase { + kind: "command"; + role: "tool"; + actionLabel?: string; + command: string; + cwd: string; + status: string; + exitCode?: number; + durationMs?: number; + output?: string; +} + +export interface DisplayFileChange { + kind: string; + path: string; + diff: string; +} + +export interface FileChangeDisplayItem extends DisplayBase { + kind: "fileChange"; + role: "tool"; + status: string; + changes: DisplayFileChange[]; + output?: string; +} + +export interface ToolDisplayItem extends DisplayBase { + kind: "tool" | "hook" | "reasoning"; + role: "tool"; + toolLabel?: string; + status?: string; + output?: string; + details?: DisplayDetailSection[]; +} + +export interface TaskProgressStep { + step: string; + status: "pending" | "inProgress" | "completed"; +} + +export interface TaskProgressDisplayItem extends DisplayBase { + kind: "taskProgress"; + role: "tool"; + explanation: string | null; + steps: TaskProgressStep[]; + status: string; +} + +export interface AgentStateDisplay { + threadId: string; + status: string; + message: string | null; +} + +export interface AgentRunSummaryAgent { + threadId: string; + status: string; + messagePreview: string | null; +} + +export interface AgentDisplayItem extends DisplayBase { + kind: "agent"; + role: "tool"; + tool: string; + status: string; + senderThreadId: string; + receiverThreadIds: string[]; + prompt: string | null; + model: string | null; + reasoningEffort: string | null; + agents: AgentStateDisplay[]; +} + +export interface AgentRunSummary { + running: number; + completed: number; + failed: number; + agents: AgentRunSummaryAgent[]; + additionalAgents: number; +} + +export type DisplayItem = + | MessageDisplayItem + | SystemDisplayItem + | CommandDisplayItem + | FileChangeDisplayItem + | ToolDisplayItem + | TaskProgressDisplayItem + | AgentDisplayItem + | ReviewResultDisplayItem; + +export type DisplayBlock = + | { + type: "item"; + item: DisplayItem; + } + | { + type: "activityGroup"; + id: string; + turnId: string; + summary: string; + items: DisplayItem[]; + }; diff --git a/src/generated/app-server/AbsolutePathBuf.ts b/src/generated/app-server/AbsolutePathBuf.ts new file mode 100644 index 00000000..dc1cde12 --- /dev/null +++ b/src/generated/app-server/AbsolutePathBuf.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * A path that is guaranteed to be absolute and normalized (though it is not + * guaranteed to be canonicalized or exist on the filesystem). + * + * IMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set + * using [AbsolutePathBufGuard::new]. If no base path is set, the + * deserialization will fail unless the path being deserialized is already + * absolute. + */ +export type AbsolutePathBuf = string; diff --git a/src/generated/app-server/AgentPath.ts b/src/generated/app-server/AgentPath.ts new file mode 100644 index 00000000..6e55ce69 --- /dev/null +++ b/src/generated/app-server/AgentPath.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AgentPath = string; diff --git a/src/generated/app-server/ApplyPatchApprovalParams.ts b/src/generated/app-server/ApplyPatchApprovalParams.ts new file mode 100644 index 00000000..34060772 --- /dev/null +++ b/src/generated/app-server/ApplyPatchApprovalParams.ts @@ -0,0 +1,21 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FileChange } from "./FileChange"; +import type { ThreadId } from "./ThreadId"; + +export type ApplyPatchApprovalParams = { conversationId: ThreadId, +/** + * Use to correlate this with [codex_protocol::protocol::PatchApplyBeginEvent] + * and [codex_protocol::protocol::PatchApplyEndEvent]. + */ +callId: string, fileChanges: { [key in string]?: FileChange }, +/** + * Optional explanatory reason (e.g. request for extra write access). + */ +reason: string | null, +/** + * When set, the agent is asking the user to allow writes under this root + * for the remainder of the session (unclear if this is honored today). + */ +grantRoot: string | null, }; diff --git a/src/generated/app-server/ApplyPatchApprovalResponse.ts b/src/generated/app-server/ApplyPatchApprovalResponse.ts new file mode 100644 index 00000000..e5da8d62 --- /dev/null +++ b/src/generated/app-server/ApplyPatchApprovalResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ReviewDecision } from "./ReviewDecision"; + +export type ApplyPatchApprovalResponse = { decision: ReviewDecision, }; diff --git a/src/generated/app-server/AuthMode.ts b/src/generated/app-server/AuthMode.ts new file mode 100644 index 00000000..210e54c4 --- /dev/null +++ b/src/generated/app-server/AuthMode.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Authentication mode for OpenAI-backed providers. + */ +export type AuthMode = "apikey" | "chatgpt" | "chatgptAuthTokens" | "agentIdentity"; diff --git a/src/generated/app-server/ClientInfo.ts b/src/generated/app-server/ClientInfo.ts new file mode 100644 index 00000000..33339b6b --- /dev/null +++ b/src/generated/app-server/ClientInfo.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ClientInfo = { name: string, title: string | null, version: string, }; diff --git a/src/generated/app-server/ClientNotification.ts b/src/generated/app-server/ClientNotification.ts new file mode 100644 index 00000000..8ce28391 --- /dev/null +++ b/src/generated/app-server/ClientNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ClientNotification = { "method": "initialized" }; diff --git a/src/generated/app-server/ClientRequest.ts b/src/generated/app-server/ClientRequest.ts new file mode 100644 index 00000000..0a74c59e --- /dev/null +++ b/src/generated/app-server/ClientRequest.ts @@ -0,0 +1,105 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FuzzyFileSearchParams } from "./FuzzyFileSearchParams"; +import type { FuzzyFileSearchSessionStartParams } from "./FuzzyFileSearchSessionStartParams"; +import type { FuzzyFileSearchSessionStopParams } from "./FuzzyFileSearchSessionStopParams"; +import type { FuzzyFileSearchSessionUpdateParams } from "./FuzzyFileSearchSessionUpdateParams"; +import type { GetAuthStatusParams } from "./GetAuthStatusParams"; +import type { GetConversationSummaryParams } from "./GetConversationSummaryParams"; +import type { GitDiffToRemoteParams } from "./GitDiffToRemoteParams"; +import type { InitializeParams } from "./InitializeParams"; +import type { RequestId } from "./RequestId"; +import type { AppsListParams } from "./v2/AppsListParams"; +import type { CancelLoginAccountParams } from "./v2/CancelLoginAccountParams"; +import type { CollaborationModeListParams } from "./v2/CollaborationModeListParams"; +import type { CommandExecParams } from "./v2/CommandExecParams"; +import type { CommandExecResizeParams } from "./v2/CommandExecResizeParams"; +import type { CommandExecTerminateParams } from "./v2/CommandExecTerminateParams"; +import type { CommandExecWriteParams } from "./v2/CommandExecWriteParams"; +import type { ConfigBatchWriteParams } from "./v2/ConfigBatchWriteParams"; +import type { ConfigReadParams } from "./v2/ConfigReadParams"; +import type { ConfigValueWriteParams } from "./v2/ConfigValueWriteParams"; +import type { ExperimentalFeatureEnablementSetParams } from "./v2/ExperimentalFeatureEnablementSetParams"; +import type { ExperimentalFeatureListParams } from "./v2/ExperimentalFeatureListParams"; +import type { ExternalAgentConfigDetectParams } from "./v2/ExternalAgentConfigDetectParams"; +import type { ExternalAgentConfigImportParams } from "./v2/ExternalAgentConfigImportParams"; +import type { FeedbackUploadParams } from "./v2/FeedbackUploadParams"; +import type { FsCopyParams } from "./v2/FsCopyParams"; +import type { FsCreateDirectoryParams } from "./v2/FsCreateDirectoryParams"; +import type { FsGetMetadataParams } from "./v2/FsGetMetadataParams"; +import type { FsReadDirectoryParams } from "./v2/FsReadDirectoryParams"; +import type { FsReadFileParams } from "./v2/FsReadFileParams"; +import type { FsRemoveParams } from "./v2/FsRemoveParams"; +import type { FsUnwatchParams } from "./v2/FsUnwatchParams"; +import type { FsWatchParams } from "./v2/FsWatchParams"; +import type { FsWriteFileParams } from "./v2/FsWriteFileParams"; +import type { GetAccountParams } from "./v2/GetAccountParams"; +import type { HooksListParams } from "./v2/HooksListParams"; +import type { ListMcpServerStatusParams } from "./v2/ListMcpServerStatusParams"; +import type { LoginAccountParams } from "./v2/LoginAccountParams"; +import type { MarketplaceAddParams } from "./v2/MarketplaceAddParams"; +import type { MarketplaceRemoveParams } from "./v2/MarketplaceRemoveParams"; +import type { MarketplaceUpgradeParams } from "./v2/MarketplaceUpgradeParams"; +import type { McpResourceReadParams } from "./v2/McpResourceReadParams"; +import type { McpServerOauthLoginParams } from "./v2/McpServerOauthLoginParams"; +import type { McpServerToolCallParams } from "./v2/McpServerToolCallParams"; +import type { MockExperimentalMethodParams } from "./v2/MockExperimentalMethodParams"; +import type { ModelListParams } from "./v2/ModelListParams"; +import type { ModelProviderCapabilitiesReadParams } from "./v2/ModelProviderCapabilitiesReadParams"; +import type { PluginInstallParams } from "./v2/PluginInstallParams"; +import type { PluginListParams } from "./v2/PluginListParams"; +import type { PluginReadParams } from "./v2/PluginReadParams"; +import type { PluginShareDeleteParams } from "./v2/PluginShareDeleteParams"; +import type { PluginShareListParams } from "./v2/PluginShareListParams"; +import type { PluginShareSaveParams } from "./v2/PluginShareSaveParams"; +import type { PluginShareUpdateTargetsParams } from "./v2/PluginShareUpdateTargetsParams"; +import type { PluginSkillReadParams } from "./v2/PluginSkillReadParams"; +import type { PluginUninstallParams } from "./v2/PluginUninstallParams"; +import type { ProcessKillParams } from "./v2/ProcessKillParams"; +import type { ProcessResizePtyParams } from "./v2/ProcessResizePtyParams"; +import type { ProcessSpawnParams } from "./v2/ProcessSpawnParams"; +import type { ProcessWriteStdinParams } from "./v2/ProcessWriteStdinParams"; +import type { ReviewStartParams } from "./v2/ReviewStartParams"; +import type { SendAddCreditsNudgeEmailParams } from "./v2/SendAddCreditsNudgeEmailParams"; +import type { SkillsConfigWriteParams } from "./v2/SkillsConfigWriteParams"; +import type { SkillsListParams } from "./v2/SkillsListParams"; +import type { ThreadApproveGuardianDeniedActionParams } from "./v2/ThreadApproveGuardianDeniedActionParams"; +import type { ThreadArchiveParams } from "./v2/ThreadArchiveParams"; +import type { ThreadBackgroundTerminalsCleanParams } from "./v2/ThreadBackgroundTerminalsCleanParams"; +import type { ThreadCompactStartParams } from "./v2/ThreadCompactStartParams"; +import type { ThreadDecrementElicitationParams } from "./v2/ThreadDecrementElicitationParams"; +import type { ThreadForkParams } from "./v2/ThreadForkParams"; +import type { ThreadGoalClearParams } from "./v2/ThreadGoalClearParams"; +import type { ThreadGoalGetParams } from "./v2/ThreadGoalGetParams"; +import type { ThreadGoalSetParams } from "./v2/ThreadGoalSetParams"; +import type { ThreadIncrementElicitationParams } from "./v2/ThreadIncrementElicitationParams"; +import type { ThreadInjectItemsParams } from "./v2/ThreadInjectItemsParams"; +import type { ThreadListParams } from "./v2/ThreadListParams"; +import type { ThreadLoadedListParams } from "./v2/ThreadLoadedListParams"; +import type { ThreadMemoryModeSetParams } from "./v2/ThreadMemoryModeSetParams"; +import type { ThreadMetadataUpdateParams } from "./v2/ThreadMetadataUpdateParams"; +import type { ThreadReadParams } from "./v2/ThreadReadParams"; +import type { ThreadRealtimeAppendAudioParams } from "./v2/ThreadRealtimeAppendAudioParams"; +import type { ThreadRealtimeAppendTextParams } from "./v2/ThreadRealtimeAppendTextParams"; +import type { ThreadRealtimeListVoicesParams } from "./v2/ThreadRealtimeListVoicesParams"; +import type { ThreadRealtimeStartParams } from "./v2/ThreadRealtimeStartParams"; +import type { ThreadRealtimeStopParams } from "./v2/ThreadRealtimeStopParams"; +import type { ThreadResumeParams } from "./v2/ThreadResumeParams"; +import type { ThreadRollbackParams } from "./v2/ThreadRollbackParams"; +import type { ThreadSetNameParams } from "./v2/ThreadSetNameParams"; +import type { ThreadShellCommandParams } from "./v2/ThreadShellCommandParams"; +import type { ThreadStartParams } from "./v2/ThreadStartParams"; +import type { ThreadTurnsItemsListParams } from "./v2/ThreadTurnsItemsListParams"; +import type { ThreadTurnsListParams } from "./v2/ThreadTurnsListParams"; +import type { ThreadUnarchiveParams } from "./v2/ThreadUnarchiveParams"; +import type { ThreadUnsubscribeParams } from "./v2/ThreadUnsubscribeParams"; +import type { TurnInterruptParams } from "./v2/TurnInterruptParams"; +import type { TurnStartParams } from "./v2/TurnStartParams"; +import type { TurnSteerParams } from "./v2/TurnSteerParams"; +import type { WindowsSandboxSetupStartParams } from "./v2/WindowsSandboxSetupStartParams"; + +/** + * Request from the client to the server. + */ +export type ClientRequest = { "method": "initialize", id: RequestId, params: InitializeParams, } | { "method": "thread/start", id: RequestId, params: ThreadStartParams, } | { "method": "thread/resume", id: RequestId, params: ThreadResumeParams, } | { "method": "thread/fork", id: RequestId, params: ThreadForkParams, } | { "method": "thread/archive", id: RequestId, params: ThreadArchiveParams, } | { "method": "thread/unsubscribe", id: RequestId, params: ThreadUnsubscribeParams, } | { "method": "thread/increment_elicitation", id: RequestId, params: ThreadIncrementElicitationParams, } | { "method": "thread/decrement_elicitation", id: RequestId, params: ThreadDecrementElicitationParams, } | { "method": "thread/name/set", id: RequestId, params: ThreadSetNameParams, } | { "method": "thread/goal/set", id: RequestId, params: ThreadGoalSetParams, } | { "method": "thread/goal/get", id: RequestId, params: ThreadGoalGetParams, } | { "method": "thread/goal/clear", id: RequestId, params: ThreadGoalClearParams, } | { "method": "thread/metadata/update", id: RequestId, params: ThreadMetadataUpdateParams, } | { "method": "thread/memoryMode/set", id: RequestId, params: ThreadMemoryModeSetParams, } | { "method": "memory/reset", id: RequestId, params: undefined, } | { "method": "thread/unarchive", id: RequestId, params: ThreadUnarchiveParams, } | { "method": "thread/compact/start", id: RequestId, params: ThreadCompactStartParams, } | { "method": "thread/shellCommand", id: RequestId, params: ThreadShellCommandParams, } | { "method": "thread/approveGuardianDeniedAction", id: RequestId, params: ThreadApproveGuardianDeniedActionParams, } | { "method": "thread/backgroundTerminals/clean", id: RequestId, params: ThreadBackgroundTerminalsCleanParams, } | { "method": "thread/rollback", id: RequestId, params: ThreadRollbackParams, } | { "method": "thread/list", id: RequestId, params: ThreadListParams, } | { "method": "thread/loaded/list", id: RequestId, params: ThreadLoadedListParams, } | { "method": "thread/read", id: RequestId, params: ThreadReadParams, } | { "method": "thread/turns/list", id: RequestId, params: ThreadTurnsListParams, } | { "method": "thread/turns/items/list", id: RequestId, params: ThreadTurnsItemsListParams, } | { "method": "thread/inject_items", id: RequestId, params: ThreadInjectItemsParams, } | { "method": "skills/list", id: RequestId, params: SkillsListParams, } | { "method": "hooks/list", id: RequestId, params: HooksListParams, } | { "method": "marketplace/add", id: RequestId, params: MarketplaceAddParams, } | { "method": "marketplace/remove", id: RequestId, params: MarketplaceRemoveParams, } | { "method": "marketplace/upgrade", id: RequestId, params: MarketplaceUpgradeParams, } | { "method": "plugin/list", id: RequestId, params: PluginListParams, } | { "method": "plugin/read", id: RequestId, params: PluginReadParams, } | { "method": "plugin/skill/read", id: RequestId, params: PluginSkillReadParams, } | { "method": "plugin/share/save", id: RequestId, params: PluginShareSaveParams, } | { "method": "plugin/share/updateTargets", id: RequestId, params: PluginShareUpdateTargetsParams, } | { "method": "plugin/share/list", id: RequestId, params: PluginShareListParams, } | { "method": "plugin/share/delete", id: RequestId, params: PluginShareDeleteParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "fs/readFile", id: RequestId, params: FsReadFileParams, } | { "method": "fs/writeFile", id: RequestId, params: FsWriteFileParams, } | { "method": "fs/createDirectory", id: RequestId, params: FsCreateDirectoryParams, } | { "method": "fs/getMetadata", id: RequestId, params: FsGetMetadataParams, } | { "method": "fs/readDirectory", id: RequestId, params: FsReadDirectoryParams, } | { "method": "fs/remove", id: RequestId, params: FsRemoveParams, } | { "method": "fs/copy", id: RequestId, params: FsCopyParams, } | { "method": "fs/watch", id: RequestId, params: FsWatchParams, } | { "method": "fs/unwatch", id: RequestId, params: FsUnwatchParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "plugin/install", id: RequestId, params: PluginInstallParams, } | { "method": "plugin/uninstall", id: RequestId, params: PluginUninstallParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/steer", id: RequestId, params: TurnSteerParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "thread/realtime/start", id: RequestId, params: ThreadRealtimeStartParams, } | { "method": "thread/realtime/appendAudio", id: RequestId, params: ThreadRealtimeAppendAudioParams, } | { "method": "thread/realtime/appendText", id: RequestId, params: ThreadRealtimeAppendTextParams, } | { "method": "thread/realtime/stop", id: RequestId, params: ThreadRealtimeStopParams, } | { "method": "thread/realtime/listVoices", id: RequestId, params: ThreadRealtimeListVoicesParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "modelProvider/capabilities/read", id: RequestId, params: ModelProviderCapabilitiesReadParams, } | { "method": "experimentalFeature/list", id: RequestId, params: ExperimentalFeatureListParams, } | { "method": "experimentalFeature/enablement/set", id: RequestId, params: ExperimentalFeatureEnablementSetParams, } | { "method": "collaborationMode/list", id: RequestId, params: CollaborationModeListParams, } | { "method": "mock/experimentalMethod", id: RequestId, params: MockExperimentalMethodParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "method": "mcpServer/resource/read", id: RequestId, params: McpResourceReadParams, } | { "method": "mcpServer/tool/call", id: RequestId, params: McpServerToolCallParams, } | { "method": "windowsSandbox/setupStart", id: RequestId, params: WindowsSandboxSetupStartParams, } | { "method": "windowsSandbox/readiness", id: RequestId, params: undefined, } | { "method": "account/login/start", id: RequestId, params: LoginAccountParams, } | { "method": "account/login/cancel", id: RequestId, params: CancelLoginAccountParams, } | { "method": "account/logout", id: RequestId, params: undefined, } | { "method": "account/rateLimits/read", id: RequestId, params: undefined, } | { "method": "account/sendAddCreditsNudgeEmail", id: RequestId, params: SendAddCreditsNudgeEmailParams, } | { "method": "feedback/upload", id: RequestId, params: FeedbackUploadParams, } | { "method": "command/exec", id: RequestId, params: CommandExecParams, } | { "method": "command/exec/write", id: RequestId, params: CommandExecWriteParams, } | { "method": "command/exec/terminate", id: RequestId, params: CommandExecTerminateParams, } | { "method": "command/exec/resize", id: RequestId, params: CommandExecResizeParams, } | { "method": "process/spawn", id: RequestId, params: ProcessSpawnParams, } | { "method": "process/writeStdin", id: RequestId, params: ProcessWriteStdinParams, } | { "method": "process/kill", id: RequestId, params: ProcessKillParams, } | { "method": "process/resizePty", id: RequestId, params: ProcessResizePtyParams, } | { "method": "config/read", id: RequestId, params: ConfigReadParams, } | { "method": "externalAgentConfig/detect", id: RequestId, params: ExternalAgentConfigDetectParams, } | { "method": "externalAgentConfig/import", id: RequestId, params: ExternalAgentConfigImportParams, } | { "method": "config/value/write", id: RequestId, params: ConfigValueWriteParams, } | { "method": "config/batchWrite", id: RequestId, params: ConfigBatchWriteParams, } | { "method": "configRequirements/read", id: RequestId, params: undefined, } | { "method": "account/read", id: RequestId, params: GetAccountParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearchParams, } | { "method": "fuzzyFileSearch/sessionStart", id: RequestId, params: FuzzyFileSearchSessionStartParams, } | { "method": "fuzzyFileSearch/sessionUpdate", id: RequestId, params: FuzzyFileSearchSessionUpdateParams, } | { "method": "fuzzyFileSearch/sessionStop", id: RequestId, params: FuzzyFileSearchSessionStopParams, }; diff --git a/src/generated/app-server/CollaborationMode.ts b/src/generated/app-server/CollaborationMode.ts new file mode 100644 index 00000000..0f60f5d1 --- /dev/null +++ b/src/generated/app-server/CollaborationMode.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ModeKind } from "./ModeKind"; +import type { Settings } from "./Settings"; + +/** + * Collaboration mode for a Codex session. + */ +export type CollaborationMode = { mode: ModeKind, settings: Settings, }; diff --git a/src/generated/app-server/ContentItem.ts b/src/generated/app-server/ContentItem.ts new file mode 100644 index 00000000..21cd8d02 --- /dev/null +++ b/src/generated/app-server/ContentItem.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ImageDetail } from "./ImageDetail"; + +export type ContentItem = { "type": "input_text", text: string, } | { "type": "input_image", image_url: string, detail?: ImageDetail, } | { "type": "output_text", text: string, }; diff --git a/src/generated/app-server/ConversationGitInfo.ts b/src/generated/app-server/ConversationGitInfo.ts new file mode 100644 index 00000000..ff0da838 --- /dev/null +++ b/src/generated/app-server/ConversationGitInfo.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ConversationGitInfo = { sha: string | null, branch: string | null, origin_url: string | null, }; diff --git a/src/generated/app-server/ConversationSummary.ts b/src/generated/app-server/ConversationSummary.ts new file mode 100644 index 00000000..2cc2a057 --- /dev/null +++ b/src/generated/app-server/ConversationSummary.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ConversationGitInfo } from "./ConversationGitInfo"; +import type { SessionSource } from "./SessionSource"; +import type { ThreadId } from "./ThreadId"; + +export type ConversationSummary = { conversationId: ThreadId, path: string, preview: string, timestamp: string | null, updatedAt: string | null, modelProvider: string, cwd: string, cliVersion: string, source: SessionSource, gitInfo: ConversationGitInfo | null, }; diff --git a/src/generated/app-server/ExecCommandApprovalParams.ts b/src/generated/app-server/ExecCommandApprovalParams.ts new file mode 100644 index 00000000..b7470c15 --- /dev/null +++ b/src/generated/app-server/ExecCommandApprovalParams.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ParsedCommand } from "./ParsedCommand"; +import type { ThreadId } from "./ThreadId"; + +export type ExecCommandApprovalParams = { conversationId: ThreadId, +/** + * Use to correlate this with [codex_protocol::protocol::ExecCommandBeginEvent] + * and [codex_protocol::protocol::ExecCommandEndEvent]. + */ +callId: string, +/** + * Identifier for this specific approval callback. + */ +approvalId: string | null, command: Array, cwd: string, reason: string | null, parsedCmd: Array, }; diff --git a/src/generated/app-server/ExecCommandApprovalResponse.ts b/src/generated/app-server/ExecCommandApprovalResponse.ts new file mode 100644 index 00000000..ce1a5216 --- /dev/null +++ b/src/generated/app-server/ExecCommandApprovalResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ReviewDecision } from "./ReviewDecision"; + +export type ExecCommandApprovalResponse = { decision: ReviewDecision, }; diff --git a/src/generated/app-server/ExecPolicyAmendment.ts b/src/generated/app-server/ExecPolicyAmendment.ts new file mode 100644 index 00000000..98e2626c --- /dev/null +++ b/src/generated/app-server/ExecPolicyAmendment.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Proposed execpolicy change to allow commands starting with this prefix. + * + * The `command` tokens form the prefix that would be added as an execpolicy + * `prefix_rule(..., decision="allow")`, letting the agent bypass approval for + * commands that start with this token sequence. + */ +export type ExecPolicyAmendment = Array; diff --git a/src/generated/app-server/FileChange.ts b/src/generated/app-server/FileChange.ts new file mode 100644 index 00000000..8eaac9e8 --- /dev/null +++ b/src/generated/app-server/FileChange.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FileChange = { "type": "add", content: string, } | { "type": "delete", content: string, } | { "type": "update", unified_diff: string, move_path: string | null, }; diff --git a/src/generated/app-server/ForcedLoginMethod.ts b/src/generated/app-server/ForcedLoginMethod.ts new file mode 100644 index 00000000..c6959088 --- /dev/null +++ b/src/generated/app-server/ForcedLoginMethod.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ForcedLoginMethod = "chatgpt" | "api"; diff --git a/src/generated/app-server/FunctionCallOutputBody.ts b/src/generated/app-server/FunctionCallOutputBody.ts new file mode 100644 index 00000000..6bcb7e25 --- /dev/null +++ b/src/generated/app-server/FunctionCallOutputBody.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FunctionCallOutputContentItem } from "./FunctionCallOutputContentItem"; + +export type FunctionCallOutputBody = string | Array; diff --git a/src/generated/app-server/FunctionCallOutputContentItem.ts b/src/generated/app-server/FunctionCallOutputContentItem.ts new file mode 100644 index 00000000..fb2996f1 --- /dev/null +++ b/src/generated/app-server/FunctionCallOutputContentItem.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ImageDetail } from "./ImageDetail"; + +/** + * Responses API compatible content items that can be returned by a tool call. + * This is a subset of ContentItem with the types we support as function call outputs. + */ +export type FunctionCallOutputContentItem = { "type": "input_text", text: string, } | { "type": "input_image", image_url: string, detail?: ImageDetail, }; diff --git a/src/generated/app-server/FuzzyFileSearchMatchType.ts b/src/generated/app-server/FuzzyFileSearchMatchType.ts new file mode 100644 index 00000000..60e92f92 --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchMatchType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FuzzyFileSearchMatchType = "file" | "directory"; diff --git a/src/generated/app-server/FuzzyFileSearchParams.ts b/src/generated/app-server/FuzzyFileSearchParams.ts new file mode 100644 index 00000000..02a7a7cf --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FuzzyFileSearchParams = { query: string, roots: Array, cancellationToken: string | null, }; diff --git a/src/generated/app-server/FuzzyFileSearchResponse.ts b/src/generated/app-server/FuzzyFileSearchResponse.ts new file mode 100644 index 00000000..276b9476 --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FuzzyFileSearchResult } from "./FuzzyFileSearchResult"; + +export type FuzzyFileSearchResponse = { files: Array, }; diff --git a/src/generated/app-server/FuzzyFileSearchResult.ts b/src/generated/app-server/FuzzyFileSearchResult.ts new file mode 100644 index 00000000..0ff6bf45 --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchResult.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FuzzyFileSearchMatchType } from "./FuzzyFileSearchMatchType"; + +/** + * Superset of [`codex_file_search::FileMatch`] + */ +export type FuzzyFileSearchResult = { root: string, path: string, match_type: FuzzyFileSearchMatchType, file_name: string, score: number, indices: Array | null, }; diff --git a/src/generated/app-server/FuzzyFileSearchSessionCompletedNotification.ts b/src/generated/app-server/FuzzyFileSearchSessionCompletedNotification.ts new file mode 100644 index 00000000..f4dc7fac --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchSessionCompletedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FuzzyFileSearchSessionCompletedNotification = { sessionId: string, }; diff --git a/src/generated/app-server/FuzzyFileSearchSessionStartParams.ts b/src/generated/app-server/FuzzyFileSearchSessionStartParams.ts new file mode 100644 index 00000000..a43d64ce --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchSessionStartParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FuzzyFileSearchSessionStartParams = { sessionId: string, roots: Array, }; diff --git a/src/generated/app-server/FuzzyFileSearchSessionStartResponse.ts b/src/generated/app-server/FuzzyFileSearchSessionStartResponse.ts new file mode 100644 index 00000000..cfe1399b --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchSessionStartResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FuzzyFileSearchSessionStartResponse = Record; diff --git a/src/generated/app-server/FuzzyFileSearchSessionStopParams.ts b/src/generated/app-server/FuzzyFileSearchSessionStopParams.ts new file mode 100644 index 00000000..c65613e5 --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchSessionStopParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FuzzyFileSearchSessionStopParams = { sessionId: string, }; diff --git a/src/generated/app-server/FuzzyFileSearchSessionStopResponse.ts b/src/generated/app-server/FuzzyFileSearchSessionStopResponse.ts new file mode 100644 index 00000000..a3500fb0 --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchSessionStopResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FuzzyFileSearchSessionStopResponse = Record; diff --git a/src/generated/app-server/FuzzyFileSearchSessionUpdateParams.ts b/src/generated/app-server/FuzzyFileSearchSessionUpdateParams.ts new file mode 100644 index 00000000..888d4689 --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchSessionUpdateParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FuzzyFileSearchSessionUpdateParams = { sessionId: string, query: string, }; diff --git a/src/generated/app-server/FuzzyFileSearchSessionUpdateResponse.ts b/src/generated/app-server/FuzzyFileSearchSessionUpdateResponse.ts new file mode 100644 index 00000000..54b87016 --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchSessionUpdateResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FuzzyFileSearchSessionUpdateResponse = Record; diff --git a/src/generated/app-server/FuzzyFileSearchSessionUpdatedNotification.ts b/src/generated/app-server/FuzzyFileSearchSessionUpdatedNotification.ts new file mode 100644 index 00000000..ba9caa76 --- /dev/null +++ b/src/generated/app-server/FuzzyFileSearchSessionUpdatedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FuzzyFileSearchResult } from "./FuzzyFileSearchResult"; + +export type FuzzyFileSearchSessionUpdatedNotification = { sessionId: string, query: string, files: Array, }; diff --git a/src/generated/app-server/GetAuthStatusParams.ts b/src/generated/app-server/GetAuthStatusParams.ts new file mode 100644 index 00000000..f185a437 --- /dev/null +++ b/src/generated/app-server/GetAuthStatusParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type GetAuthStatusParams = { includeToken: boolean | null, refreshToken: boolean | null, }; diff --git a/src/generated/app-server/GetAuthStatusResponse.ts b/src/generated/app-server/GetAuthStatusResponse.ts new file mode 100644 index 00000000..9a050f41 --- /dev/null +++ b/src/generated/app-server/GetAuthStatusResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AuthMode } from "./AuthMode"; + +export type GetAuthStatusResponse = { authMethod: AuthMode | null, authToken: string | null, requiresOpenaiAuth: boolean | null, }; diff --git a/src/generated/app-server/GetConversationSummaryParams.ts b/src/generated/app-server/GetConversationSummaryParams.ts new file mode 100644 index 00000000..4e000543 --- /dev/null +++ b/src/generated/app-server/GetConversationSummaryParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadId } from "./ThreadId"; + +export type GetConversationSummaryParams = { rolloutPath: string, } | { conversationId: ThreadId, }; diff --git a/src/generated/app-server/GetConversationSummaryResponse.ts b/src/generated/app-server/GetConversationSummaryResponse.ts new file mode 100644 index 00000000..d3dee5d6 --- /dev/null +++ b/src/generated/app-server/GetConversationSummaryResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ConversationSummary } from "./ConversationSummary"; + +export type GetConversationSummaryResponse = { summary: ConversationSummary, }; diff --git a/src/generated/app-server/GitDiffToRemoteParams.ts b/src/generated/app-server/GitDiffToRemoteParams.ts new file mode 100644 index 00000000..535aad3c --- /dev/null +++ b/src/generated/app-server/GitDiffToRemoteParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type GitDiffToRemoteParams = { cwd: string, }; diff --git a/src/generated/app-server/GitDiffToRemoteResponse.ts b/src/generated/app-server/GitDiffToRemoteResponse.ts new file mode 100644 index 00000000..ec6c1515 --- /dev/null +++ b/src/generated/app-server/GitDiffToRemoteResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { GitSha } from "./GitSha"; + +export type GitDiffToRemoteResponse = { sha: GitSha, diff: string, }; diff --git a/src/generated/app-server/GitSha.ts b/src/generated/app-server/GitSha.ts new file mode 100644 index 00000000..701b75aa --- /dev/null +++ b/src/generated/app-server/GitSha.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type GitSha = string; diff --git a/src/generated/app-server/ImageDetail.ts b/src/generated/app-server/ImageDetail.ts new file mode 100644 index 00000000..a48f07c0 --- /dev/null +++ b/src/generated/app-server/ImageDetail.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ImageDetail = "auto" | "low" | "high" | "original"; diff --git a/src/generated/app-server/InitializeCapabilities.ts b/src/generated/app-server/InitializeCapabilities.ts new file mode 100644 index 00000000..5d42cc48 --- /dev/null +++ b/src/generated/app-server/InitializeCapabilities.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Client-declared capabilities negotiated during initialize. + */ +export type InitializeCapabilities = { +/** + * Opt into receiving experimental API methods and fields. + */ +experimentalApi: boolean, +/** + * Exact notification method names that should be suppressed for this + * connection (for example `thread/started`). + */ +optOutNotificationMethods?: Array | null, }; diff --git a/src/generated/app-server/InitializeParams.ts b/src/generated/app-server/InitializeParams.ts new file mode 100644 index 00000000..e48c5ee7 --- /dev/null +++ b/src/generated/app-server/InitializeParams.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ClientInfo } from "./ClientInfo"; +import type { InitializeCapabilities } from "./InitializeCapabilities"; + +export type InitializeParams = { clientInfo: ClientInfo, capabilities: InitializeCapabilities | null, }; diff --git a/src/generated/app-server/InitializeResponse.ts b/src/generated/app-server/InitializeResponse.ts new file mode 100644 index 00000000..f1f79d17 --- /dev/null +++ b/src/generated/app-server/InitializeResponse.ts @@ -0,0 +1,20 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "./AbsolutePathBuf"; + +export type InitializeResponse = { userAgent: string, +/** + * Absolute path to the server's $CODEX_HOME directory. + */ +codexHome: AbsolutePathBuf, +/** + * Platform family for the running app-server target, for example + * `"unix"` or `"windows"`. + */ +platformFamily: string, +/** + * Operating system for the running app-server target, for example + * `"macos"`, `"linux"`, or `"windows"`. + */ +platformOs: string, }; diff --git a/src/generated/app-server/InputModality.ts b/src/generated/app-server/InputModality.ts new file mode 100644 index 00000000..73661938 --- /dev/null +++ b/src/generated/app-server/InputModality.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Canonical user-input modality tags advertised by a model. + */ +export type InputModality = "text" | "image"; diff --git a/src/generated/app-server/InternalSessionSource.ts b/src/generated/app-server/InternalSessionSource.ts new file mode 100644 index 00000000..47417c51 --- /dev/null +++ b/src/generated/app-server/InternalSessionSource.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type InternalSessionSource = "memory_consolidation"; diff --git a/src/generated/app-server/LocalShellAction.ts b/src/generated/app-server/LocalShellAction.ts new file mode 100644 index 00000000..b24847dc --- /dev/null +++ b/src/generated/app-server/LocalShellAction.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { LocalShellExecAction } from "./LocalShellExecAction"; + +export type LocalShellAction = { "type": "exec" } & LocalShellExecAction; diff --git a/src/generated/app-server/LocalShellExecAction.ts b/src/generated/app-server/LocalShellExecAction.ts new file mode 100644 index 00000000..10d41336 --- /dev/null +++ b/src/generated/app-server/LocalShellExecAction.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type LocalShellExecAction = { command: Array, timeout_ms: bigint | null, working_directory: string | null, env: { [key in string]?: string } | null, user: string | null, }; diff --git a/src/generated/app-server/LocalShellStatus.ts b/src/generated/app-server/LocalShellStatus.ts new file mode 100644 index 00000000..00db484a --- /dev/null +++ b/src/generated/app-server/LocalShellStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type LocalShellStatus = "completed" | "in_progress" | "incomplete"; diff --git a/src/generated/app-server/MessagePhase.ts b/src/generated/app-server/MessagePhase.ts new file mode 100644 index 00000000..9e16021b --- /dev/null +++ b/src/generated/app-server/MessagePhase.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Classifies an assistant message as interim commentary or final answer text. + * + * Providers do not emit this consistently, so callers must treat `None` as + * "phase unknown" and keep compatibility behavior for legacy models. + */ +export type MessagePhase = "commentary" | "final_answer"; diff --git a/src/generated/app-server/ModeKind.ts b/src/generated/app-server/ModeKind.ts new file mode 100644 index 00000000..7d2324ad --- /dev/null +++ b/src/generated/app-server/ModeKind.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Initial collaboration mode to use when the TUI starts. + */ +export type ModeKind = "plan" | "default"; diff --git a/src/generated/app-server/NetworkPolicyAmendment.ts b/src/generated/app-server/NetworkPolicyAmendment.ts new file mode 100644 index 00000000..4e5092e4 --- /dev/null +++ b/src/generated/app-server/NetworkPolicyAmendment.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { NetworkPolicyRuleAction } from "./NetworkPolicyRuleAction"; + +export type NetworkPolicyAmendment = { host: string, action: NetworkPolicyRuleAction, }; diff --git a/src/generated/app-server/NetworkPolicyRuleAction.ts b/src/generated/app-server/NetworkPolicyRuleAction.ts new file mode 100644 index 00000000..55ec7003 --- /dev/null +++ b/src/generated/app-server/NetworkPolicyRuleAction.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NetworkPolicyRuleAction = "allow" | "deny"; diff --git a/src/generated/app-server/ParsedCommand.ts b/src/generated/app-server/ParsedCommand.ts new file mode 100644 index 00000000..092476e9 --- /dev/null +++ b/src/generated/app-server/ParsedCommand.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ParsedCommand = { "type": "read", cmd: string, name: string, +/** + * (Best effort) Path to the file being read by the command. When + * possible, this is an absolute path, though when relative, it should + * be resolved against the `cwd`` that will be used to run the command + * to derive the absolute path. + */ +path: string, } | { "type": "list_files", cmd: string, path: string | null, } | { "type": "search", cmd: string, query: string | null, path: string | null, } | { "type": "unknown", cmd: string, }; diff --git a/src/generated/app-server/Personality.ts b/src/generated/app-server/Personality.ts new file mode 100644 index 00000000..45165f4e --- /dev/null +++ b/src/generated/app-server/Personality.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Personality = "none" | "friendly" | "pragmatic"; diff --git a/src/generated/app-server/PlanType.ts b/src/generated/app-server/PlanType.ts new file mode 100644 index 00000000..44891467 --- /dev/null +++ b/src/generated/app-server/PlanType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PlanType = "free" | "go" | "plus" | "pro" | "prolite" | "team" | "self_serve_business_usage_based" | "business" | "enterprise_cbp_usage_based" | "enterprise" | "edu" | "unknown"; diff --git a/src/generated/app-server/RealtimeConversationVersion.ts b/src/generated/app-server/RealtimeConversationVersion.ts new file mode 100644 index 00000000..cedc4bbe --- /dev/null +++ b/src/generated/app-server/RealtimeConversationVersion.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RealtimeConversationVersion = "v1" | "v2"; diff --git a/src/generated/app-server/RealtimeOutputModality.ts b/src/generated/app-server/RealtimeOutputModality.ts new file mode 100644 index 00000000..78e00e71 --- /dev/null +++ b/src/generated/app-server/RealtimeOutputModality.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RealtimeOutputModality = "text" | "audio"; diff --git a/src/generated/app-server/RealtimeVoice.ts b/src/generated/app-server/RealtimeVoice.ts new file mode 100644 index 00000000..c3a434e9 --- /dev/null +++ b/src/generated/app-server/RealtimeVoice.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RealtimeVoice = "alloy" | "arbor" | "ash" | "ballad" | "breeze" | "cedar" | "coral" | "cove" | "echo" | "ember" | "juniper" | "maple" | "marin" | "sage" | "shimmer" | "sol" | "spruce" | "vale" | "verse"; diff --git a/src/generated/app-server/RealtimeVoicesList.ts b/src/generated/app-server/RealtimeVoicesList.ts new file mode 100644 index 00000000..b81cbc0a --- /dev/null +++ b/src/generated/app-server/RealtimeVoicesList.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RealtimeVoice } from "./RealtimeVoice"; + +export type RealtimeVoicesList = { v1: Array, v2: Array, defaultV1: RealtimeVoice, defaultV2: RealtimeVoice, }; diff --git a/src/generated/app-server/ReasoningEffort.ts b/src/generated/app-server/ReasoningEffort.ts new file mode 100644 index 00000000..c0798f43 --- /dev/null +++ b/src/generated/app-server/ReasoningEffort.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning + */ +export type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh"; diff --git a/src/generated/app-server/ReasoningItemContent.ts b/src/generated/app-server/ReasoningItemContent.ts new file mode 100644 index 00000000..fd533796 --- /dev/null +++ b/src/generated/app-server/ReasoningItemContent.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ReasoningItemContent = { "type": "reasoning_text", text: string, } | { "type": "text", text: string, }; diff --git a/src/generated/app-server/ReasoningItemReasoningSummary.ts b/src/generated/app-server/ReasoningItemReasoningSummary.ts new file mode 100644 index 00000000..f01a88a0 --- /dev/null +++ b/src/generated/app-server/ReasoningItemReasoningSummary.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ReasoningItemReasoningSummary = { "type": "summary_text", text: string, }; diff --git a/src/generated/app-server/ReasoningSummary.ts b/src/generated/app-server/ReasoningSummary.ts new file mode 100644 index 00000000..d246ac12 --- /dev/null +++ b/src/generated/app-server/ReasoningSummary.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * A summary of the reasoning performed by the model. This can be useful for + * debugging and understanding the model's reasoning process. + * See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries + */ +export type ReasoningSummary = "auto" | "concise" | "detailed" | "none"; diff --git a/src/generated/app-server/RequestId.ts b/src/generated/app-server/RequestId.ts new file mode 100644 index 00000000..8a771bd0 --- /dev/null +++ b/src/generated/app-server/RequestId.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RequestId = string | number; diff --git a/src/generated/app-server/Resource.ts b/src/generated/app-server/Resource.ts new file mode 100644 index 00000000..6eca7941 --- /dev/null +++ b/src/generated/app-server/Resource.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "./serde_json/JsonValue"; + +/** + * A known resource that the server is capable of reading. + */ +export type Resource = { annotations?: JsonValue, description?: string, mimeType?: string, name: string, size?: number, title?: string, uri: string, icons?: Array, _meta?: JsonValue, }; diff --git a/src/generated/app-server/ResourceContent.ts b/src/generated/app-server/ResourceContent.ts new file mode 100644 index 00000000..f5bcf2d5 --- /dev/null +++ b/src/generated/app-server/ResourceContent.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "./serde_json/JsonValue"; + +/** + * Contents returned when reading a resource from an MCP server. + */ +export type ResourceContent = { +/** + * The URI of this resource. + */ +uri: string, mimeType?: string, text: string, _meta?: JsonValue, } | { +/** + * The URI of this resource. + */ +uri: string, mimeType?: string, blob: string, _meta?: JsonValue, }; diff --git a/src/generated/app-server/ResourceTemplate.ts b/src/generated/app-server/ResourceTemplate.ts new file mode 100644 index 00000000..6dc39512 --- /dev/null +++ b/src/generated/app-server/ResourceTemplate.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "./serde_json/JsonValue"; + +/** + * A template description for resources available on the server. + */ +export type ResourceTemplate = { annotations?: JsonValue, uriTemplate: string, name: string, title?: string, description?: string, mimeType?: string, }; diff --git a/src/generated/app-server/ResponseItem.ts b/src/generated/app-server/ResponseItem.ts new file mode 100644 index 00000000..6fa9beee --- /dev/null +++ b/src/generated/app-server/ResponseItem.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ContentItem } from "./ContentItem"; +import type { FunctionCallOutputBody } from "./FunctionCallOutputBody"; +import type { LocalShellAction } from "./LocalShellAction"; +import type { LocalShellStatus } from "./LocalShellStatus"; +import type { MessagePhase } from "./MessagePhase"; +import type { ReasoningItemContent } from "./ReasoningItemContent"; +import type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSummary"; +import type { WebSearchAction } from "./WebSearchAction"; + +export type ResponseItem = { "type": "message", role: string, content: Array, phase?: MessagePhase, } | { "type": "reasoning", summary: Array, content?: Array, encrypted_content: string | null, } | { "type": "local_shell_call", +/** + * Set when using the Responses API. + */ +call_id: string | null, status: LocalShellStatus, action: LocalShellAction, } | { "type": "function_call", name: string, namespace?: string, arguments: string, call_id: string, } | { "type": "tool_search_call", call_id: string | null, status?: string, execution: string, arguments: unknown, } | { "type": "function_call_output", call_id: string, output: FunctionCallOutputBody, } | { "type": "custom_tool_call", status?: string, call_id: string, name: string, input: string, } | { "type": "custom_tool_call_output", call_id: string, name?: string, output: FunctionCallOutputBody, } | { "type": "tool_search_output", call_id: string | null, status: string, execution: string, tools: unknown[], } | { "type": "web_search_call", status?: string, action?: WebSearchAction, } | { "type": "image_generation_call", id: string, status: string, revised_prompt?: string, result: string, } | { "type": "compaction", encrypted_content: string, } | { "type": "context_compaction", encrypted_content?: string, } | { "type": "other" }; diff --git a/src/generated/app-server/ReviewDecision.ts b/src/generated/app-server/ReviewDecision.ts new file mode 100644 index 00000000..109f7292 --- /dev/null +++ b/src/generated/app-server/ReviewDecision.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExecPolicyAmendment } from "./ExecPolicyAmendment"; +import type { NetworkPolicyAmendment } from "./NetworkPolicyAmendment"; + +/** + * User's decision in response to an ExecApprovalRequest. + */ +export type ReviewDecision = "approved" | { "approved_execpolicy_amendment": { proposed_execpolicy_amendment: ExecPolicyAmendment, } } | "approved_for_session" | { "network_policy_amendment": { network_policy_amendment: NetworkPolicyAmendment, } } | "denied" | "timed_out" | "abort"; diff --git a/src/generated/app-server/ServerNotification.ts b/src/generated/app-server/ServerNotification.ts new file mode 100644 index 00000000..f4dd0e18 --- /dev/null +++ b/src/generated/app-server/ServerNotification.ts @@ -0,0 +1,72 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FuzzyFileSearchSessionCompletedNotification } from "./FuzzyFileSearchSessionCompletedNotification"; +import type { FuzzyFileSearchSessionUpdatedNotification } from "./FuzzyFileSearchSessionUpdatedNotification"; +import type { AccountLoginCompletedNotification } from "./v2/AccountLoginCompletedNotification"; +import type { AccountRateLimitsUpdatedNotification } from "./v2/AccountRateLimitsUpdatedNotification"; +import type { AccountUpdatedNotification } from "./v2/AccountUpdatedNotification"; +import type { AgentMessageDeltaNotification } from "./v2/AgentMessageDeltaNotification"; +import type { AppListUpdatedNotification } from "./v2/AppListUpdatedNotification"; +import type { CommandExecOutputDeltaNotification } from "./v2/CommandExecOutputDeltaNotification"; +import type { CommandExecutionOutputDeltaNotification } from "./v2/CommandExecutionOutputDeltaNotification"; +import type { ConfigWarningNotification } from "./v2/ConfigWarningNotification"; +import type { ContextCompactedNotification } from "./v2/ContextCompactedNotification"; +import type { DeprecationNoticeNotification } from "./v2/DeprecationNoticeNotification"; +import type { ErrorNotification } from "./v2/ErrorNotification"; +import type { ExternalAgentConfigImportCompletedNotification } from "./v2/ExternalAgentConfigImportCompletedNotification"; +import type { FileChangeOutputDeltaNotification } from "./v2/FileChangeOutputDeltaNotification"; +import type { FileChangePatchUpdatedNotification } from "./v2/FileChangePatchUpdatedNotification"; +import type { FsChangedNotification } from "./v2/FsChangedNotification"; +import type { GuardianWarningNotification } from "./v2/GuardianWarningNotification"; +import type { HookCompletedNotification } from "./v2/HookCompletedNotification"; +import type { HookStartedNotification } from "./v2/HookStartedNotification"; +import type { ItemCompletedNotification } from "./v2/ItemCompletedNotification"; +import type { ItemGuardianApprovalReviewCompletedNotification } from "./v2/ItemGuardianApprovalReviewCompletedNotification"; +import type { ItemGuardianApprovalReviewStartedNotification } from "./v2/ItemGuardianApprovalReviewStartedNotification"; +import type { ItemStartedNotification } from "./v2/ItemStartedNotification"; +import type { McpServerOauthLoginCompletedNotification } from "./v2/McpServerOauthLoginCompletedNotification"; +import type { McpServerStatusUpdatedNotification } from "./v2/McpServerStatusUpdatedNotification"; +import type { McpToolCallProgressNotification } from "./v2/McpToolCallProgressNotification"; +import type { ModelReroutedNotification } from "./v2/ModelReroutedNotification"; +import type { ModelVerificationNotification } from "./v2/ModelVerificationNotification"; +import type { PlanDeltaNotification } from "./v2/PlanDeltaNotification"; +import type { ProcessExitedNotification } from "./v2/ProcessExitedNotification"; +import type { ProcessOutputDeltaNotification } from "./v2/ProcessOutputDeltaNotification"; +import type { RawResponseItemCompletedNotification } from "./v2/RawResponseItemCompletedNotification"; +import type { ReasoningSummaryPartAddedNotification } from "./v2/ReasoningSummaryPartAddedNotification"; +import type { ReasoningSummaryTextDeltaNotification } from "./v2/ReasoningSummaryTextDeltaNotification"; +import type { ReasoningTextDeltaNotification } from "./v2/ReasoningTextDeltaNotification"; +import type { RemoteControlStatusChangedNotification } from "./v2/RemoteControlStatusChangedNotification"; +import type { ServerRequestResolvedNotification } from "./v2/ServerRequestResolvedNotification"; +import type { SkillsChangedNotification } from "./v2/SkillsChangedNotification"; +import type { TerminalInteractionNotification } from "./v2/TerminalInteractionNotification"; +import type { ThreadArchivedNotification } from "./v2/ThreadArchivedNotification"; +import type { ThreadClosedNotification } from "./v2/ThreadClosedNotification"; +import type { ThreadGoalClearedNotification } from "./v2/ThreadGoalClearedNotification"; +import type { ThreadGoalUpdatedNotification } from "./v2/ThreadGoalUpdatedNotification"; +import type { ThreadNameUpdatedNotification } from "./v2/ThreadNameUpdatedNotification"; +import type { ThreadRealtimeClosedNotification } from "./v2/ThreadRealtimeClosedNotification"; +import type { ThreadRealtimeErrorNotification } from "./v2/ThreadRealtimeErrorNotification"; +import type { ThreadRealtimeItemAddedNotification } from "./v2/ThreadRealtimeItemAddedNotification"; +import type { ThreadRealtimeOutputAudioDeltaNotification } from "./v2/ThreadRealtimeOutputAudioDeltaNotification"; +import type { ThreadRealtimeSdpNotification } from "./v2/ThreadRealtimeSdpNotification"; +import type { ThreadRealtimeStartedNotification } from "./v2/ThreadRealtimeStartedNotification"; +import type { ThreadRealtimeTranscriptDeltaNotification } from "./v2/ThreadRealtimeTranscriptDeltaNotification"; +import type { ThreadRealtimeTranscriptDoneNotification } from "./v2/ThreadRealtimeTranscriptDoneNotification"; +import type { ThreadStartedNotification } from "./v2/ThreadStartedNotification"; +import type { ThreadStatusChangedNotification } from "./v2/ThreadStatusChangedNotification"; +import type { ThreadTokenUsageUpdatedNotification } from "./v2/ThreadTokenUsageUpdatedNotification"; +import type { ThreadUnarchivedNotification } from "./v2/ThreadUnarchivedNotification"; +import type { TurnCompletedNotification } from "./v2/TurnCompletedNotification"; +import type { TurnDiffUpdatedNotification } from "./v2/TurnDiffUpdatedNotification"; +import type { TurnPlanUpdatedNotification } from "./v2/TurnPlanUpdatedNotification"; +import type { TurnStartedNotification } from "./v2/TurnStartedNotification"; +import type { WarningNotification } from "./v2/WarningNotification"; +import type { WindowsSandboxSetupCompletedNotification } from "./v2/WindowsSandboxSetupCompletedNotification"; +import type { WindowsWorldWritableWarningNotification } from "./v2/WindowsWorldWritableWarningNotification"; + +/** + * Notification sent from the server to the client. + */ +export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/status/changed", "params": ThreadStatusChangedNotification } | { "method": "thread/archived", "params": ThreadArchivedNotification } | { "method": "thread/unarchived", "params": ThreadUnarchivedNotification } | { "method": "thread/closed", "params": ThreadClosedNotification } | { "method": "skills/changed", "params": SkillsChangedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/goal/updated", "params": ThreadGoalUpdatedNotification } | { "method": "thread/goal/cleared", "params": ThreadGoalClearedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "hook/started", "params": HookStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "hook/completed", "params": HookCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/autoApprovalReview/started", "params": ItemGuardianApprovalReviewStartedNotification } | { "method": "item/autoApprovalReview/completed", "params": ItemGuardianApprovalReviewCompletedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "command/exec/outputDelta", "params": CommandExecOutputDeltaNotification } | { "method": "process/outputDelta", "params": ProcessOutputDeltaNotification } | { "method": "process/exited", "params": ProcessExitedNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "item/fileChange/patchUpdated", "params": FileChangePatchUpdatedNotification } | { "method": "serverRequest/resolved", "params": ServerRequestResolvedNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "mcpServer/startupStatus/updated", "params": McpServerStatusUpdatedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "app/list/updated", "params": AppListUpdatedNotification } | { "method": "remoteControl/status/changed", "params": RemoteControlStatusChangedNotification } | { "method": "externalAgentConfig/import/completed", "params": ExternalAgentConfigImportCompletedNotification } | { "method": "fs/changed", "params": FsChangedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "model/rerouted", "params": ModelReroutedNotification } | { "method": "model/verification", "params": ModelVerificationNotification } | { "method": "warning", "params": WarningNotification } | { "method": "guardianWarning", "params": GuardianWarningNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "fuzzyFileSearch/sessionUpdated", "params": FuzzyFileSearchSessionUpdatedNotification } | { "method": "fuzzyFileSearch/sessionCompleted", "params": FuzzyFileSearchSessionCompletedNotification } | { "method": "thread/realtime/started", "params": ThreadRealtimeStartedNotification } | { "method": "thread/realtime/itemAdded", "params": ThreadRealtimeItemAddedNotification } | { "method": "thread/realtime/transcript/delta", "params": ThreadRealtimeTranscriptDeltaNotification } | { "method": "thread/realtime/transcript/done", "params": ThreadRealtimeTranscriptDoneNotification } | { "method": "thread/realtime/outputAudio/delta", "params": ThreadRealtimeOutputAudioDeltaNotification } | { "method": "thread/realtime/sdp", "params": ThreadRealtimeSdpNotification } | { "method": "thread/realtime/error", "params": ThreadRealtimeErrorNotification } | { "method": "thread/realtime/closed", "params": ThreadRealtimeClosedNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "windowsSandbox/setupCompleted", "params": WindowsSandboxSetupCompletedNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification }; diff --git a/src/generated/app-server/ServerRequest.ts b/src/generated/app-server/ServerRequest.ts new file mode 100644 index 00000000..13d04b0b --- /dev/null +++ b/src/generated/app-server/ServerRequest.ts @@ -0,0 +1,18 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ApplyPatchApprovalParams } from "./ApplyPatchApprovalParams"; +import type { ExecCommandApprovalParams } from "./ExecCommandApprovalParams"; +import type { RequestId } from "./RequestId"; +import type { ChatgptAuthTokensRefreshParams } from "./v2/ChatgptAuthTokensRefreshParams"; +import type { CommandExecutionRequestApprovalParams } from "./v2/CommandExecutionRequestApprovalParams"; +import type { DynamicToolCallParams } from "./v2/DynamicToolCallParams"; +import type { FileChangeRequestApprovalParams } from "./v2/FileChangeRequestApprovalParams"; +import type { McpServerElicitationRequestParams } from "./v2/McpServerElicitationRequestParams"; +import type { PermissionsRequestApprovalParams } from "./v2/PermissionsRequestApprovalParams"; +import type { ToolRequestUserInputParams } from "./v2/ToolRequestUserInputParams"; + +/** + * Request initiated from the server and sent to the client. + */ +export type ServerRequest = { "method": "item/commandExecution/requestApproval", id: RequestId, params: CommandExecutionRequestApprovalParams, } | { "method": "item/fileChange/requestApproval", id: RequestId, params: FileChangeRequestApprovalParams, } | { "method": "item/tool/requestUserInput", id: RequestId, params: ToolRequestUserInputParams, } | { "method": "mcpServer/elicitation/request", id: RequestId, params: McpServerElicitationRequestParams, } | { "method": "item/permissions/requestApproval", id: RequestId, params: PermissionsRequestApprovalParams, } | { "method": "item/tool/call", id: RequestId, params: DynamicToolCallParams, } | { "method": "account/chatgptAuthTokens/refresh", id: RequestId, params: ChatgptAuthTokensRefreshParams, } | { "method": "applyPatchApproval", id: RequestId, params: ApplyPatchApprovalParams, } | { "method": "execCommandApproval", id: RequestId, params: ExecCommandApprovalParams, }; diff --git a/src/generated/app-server/SessionSource.ts b/src/generated/app-server/SessionSource.ts new file mode 100644 index 00000000..3317c228 --- /dev/null +++ b/src/generated/app-server/SessionSource.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { InternalSessionSource } from "./InternalSessionSource"; +import type { SubAgentSource } from "./SubAgentSource"; + +export type SessionSource = "cli" | "vscode" | "exec" | "mcp" | { "custom": string } | { "internal": InternalSessionSource } | { "subagent": SubAgentSource } | "unknown"; diff --git a/src/generated/app-server/Settings.ts b/src/generated/app-server/Settings.ts new file mode 100644 index 00000000..29bcadd5 --- /dev/null +++ b/src/generated/app-server/Settings.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ReasoningEffort } from "./ReasoningEffort"; + +/** + * Settings for a collaboration mode. + */ +export type Settings = { model: string, reasoning_effort: ReasoningEffort | null, developer_instructions: string | null, }; diff --git a/src/generated/app-server/SubAgentSource.ts b/src/generated/app-server/SubAgentSource.ts new file mode 100644 index 00000000..669e5802 --- /dev/null +++ b/src/generated/app-server/SubAgentSource.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AgentPath } from "./AgentPath"; +import type { ThreadId } from "./ThreadId"; + +export type SubAgentSource = "review" | "compact" | { "thread_spawn": { parent_thread_id: ThreadId, depth: number, agent_path: AgentPath | null, agent_nickname: string | null, agent_role: string | null, } } | "memory_consolidation" | { "other": string }; diff --git a/src/generated/app-server/ThreadId.ts b/src/generated/app-server/ThreadId.ts new file mode 100644 index 00000000..bfb3b4b4 --- /dev/null +++ b/src/generated/app-server/ThreadId.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadId = string; diff --git a/src/generated/app-server/ThreadMemoryMode.ts b/src/generated/app-server/ThreadMemoryMode.ts new file mode 100644 index 00000000..74a7e759 --- /dev/null +++ b/src/generated/app-server/ThreadMemoryMode.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadMemoryMode = "enabled" | "disabled"; diff --git a/src/generated/app-server/Tool.ts b/src/generated/app-server/Tool.ts new file mode 100644 index 00000000..b7959161 --- /dev/null +++ b/src/generated/app-server/Tool.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "./serde_json/JsonValue"; + +/** + * Definition for a tool the client can call. + */ +export type Tool = { name: string, title?: string, description?: string, inputSchema: JsonValue, outputSchema?: JsonValue, annotations?: JsonValue, icons?: Array, _meta?: JsonValue, }; diff --git a/src/generated/app-server/Verbosity.ts b/src/generated/app-server/Verbosity.ts new file mode 100644 index 00000000..8fd97b0b --- /dev/null +++ b/src/generated/app-server/Verbosity.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Controls output length/detail on GPT-5 models via the Responses API. + * Serialized with lowercase values to match the OpenAI API. + */ +export type Verbosity = "low" | "medium" | "high"; diff --git a/src/generated/app-server/WebSearchAction.ts b/src/generated/app-server/WebSearchAction.ts new file mode 100644 index 00000000..91cb99e9 --- /dev/null +++ b/src/generated/app-server/WebSearchAction.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WebSearchAction = { "type": "search", query?: string, queries?: Array, } | { "type": "open_page", url?: string, } | { "type": "find_in_page", url?: string, pattern?: string, } | { "type": "other" }; diff --git a/src/generated/app-server/WebSearchContextSize.ts b/src/generated/app-server/WebSearchContextSize.ts new file mode 100644 index 00000000..d6feedde --- /dev/null +++ b/src/generated/app-server/WebSearchContextSize.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WebSearchContextSize = "low" | "medium" | "high"; diff --git a/src/generated/app-server/WebSearchLocation.ts b/src/generated/app-server/WebSearchLocation.ts new file mode 100644 index 00000000..12319983 --- /dev/null +++ b/src/generated/app-server/WebSearchLocation.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WebSearchLocation = { country: string | null, region: string | null, city: string | null, timezone: string | null, }; diff --git a/src/generated/app-server/WebSearchMode.ts b/src/generated/app-server/WebSearchMode.ts new file mode 100644 index 00000000..695c13e3 --- /dev/null +++ b/src/generated/app-server/WebSearchMode.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WebSearchMode = "disabled" | "cached" | "live"; diff --git a/src/generated/app-server/WebSearchToolConfig.ts b/src/generated/app-server/WebSearchToolConfig.ts new file mode 100644 index 00000000..c14067ce --- /dev/null +++ b/src/generated/app-server/WebSearchToolConfig.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { WebSearchContextSize } from "./WebSearchContextSize"; +import type { WebSearchLocation } from "./WebSearchLocation"; + +export type WebSearchToolConfig = { context_size: WebSearchContextSize | null, allowed_domains: Array | null, location: WebSearchLocation | null, }; diff --git a/src/generated/app-server/index.ts b/src/generated/app-server/index.ts new file mode 100644 index 00000000..e679e822 --- /dev/null +++ b/src/generated/app-server/index.ts @@ -0,0 +1,85 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +export type { AbsolutePathBuf } from "./AbsolutePathBuf"; +export type { AgentPath } from "./AgentPath"; +export type { ApplyPatchApprovalParams } from "./ApplyPatchApprovalParams"; +export type { ApplyPatchApprovalResponse } from "./ApplyPatchApprovalResponse"; +export type { AuthMode } from "./AuthMode"; +export type { ClientInfo } from "./ClientInfo"; +export type { ClientNotification } from "./ClientNotification"; +export type { ClientRequest } from "./ClientRequest"; +export type { CollaborationMode } from "./CollaborationMode"; +export type { ContentItem } from "./ContentItem"; +export type { ConversationGitInfo } from "./ConversationGitInfo"; +export type { ConversationSummary } from "./ConversationSummary"; +export type { ExecCommandApprovalParams } from "./ExecCommandApprovalParams"; +export type { ExecCommandApprovalResponse } from "./ExecCommandApprovalResponse"; +export type { ExecPolicyAmendment } from "./ExecPolicyAmendment"; +export type { FileChange } from "./FileChange"; +export type { ForcedLoginMethod } from "./ForcedLoginMethod"; +export type { FunctionCallOutputBody } from "./FunctionCallOutputBody"; +export type { FunctionCallOutputContentItem } from "./FunctionCallOutputContentItem"; +export type { FuzzyFileSearchMatchType } from "./FuzzyFileSearchMatchType"; +export type { FuzzyFileSearchParams } from "./FuzzyFileSearchParams"; +export type { FuzzyFileSearchResponse } from "./FuzzyFileSearchResponse"; +export type { FuzzyFileSearchResult } from "./FuzzyFileSearchResult"; +export type { FuzzyFileSearchSessionCompletedNotification } from "./FuzzyFileSearchSessionCompletedNotification"; +export type { FuzzyFileSearchSessionStartParams } from "./FuzzyFileSearchSessionStartParams"; +export type { FuzzyFileSearchSessionStartResponse } from "./FuzzyFileSearchSessionStartResponse"; +export type { FuzzyFileSearchSessionStopParams } from "./FuzzyFileSearchSessionStopParams"; +export type { FuzzyFileSearchSessionStopResponse } from "./FuzzyFileSearchSessionStopResponse"; +export type { FuzzyFileSearchSessionUpdateParams } from "./FuzzyFileSearchSessionUpdateParams"; +export type { FuzzyFileSearchSessionUpdateResponse } from "./FuzzyFileSearchSessionUpdateResponse"; +export type { FuzzyFileSearchSessionUpdatedNotification } from "./FuzzyFileSearchSessionUpdatedNotification"; +export type { GetAuthStatusParams } from "./GetAuthStatusParams"; +export type { GetAuthStatusResponse } from "./GetAuthStatusResponse"; +export type { GetConversationSummaryParams } from "./GetConversationSummaryParams"; +export type { GetConversationSummaryResponse } from "./GetConversationSummaryResponse"; +export type { GitDiffToRemoteParams } from "./GitDiffToRemoteParams"; +export type { GitDiffToRemoteResponse } from "./GitDiffToRemoteResponse"; +export type { GitSha } from "./GitSha"; +export type { ImageDetail } from "./ImageDetail"; +export type { InitializeCapabilities } from "./InitializeCapabilities"; +export type { InitializeParams } from "./InitializeParams"; +export type { InitializeResponse } from "./InitializeResponse"; +export type { InputModality } from "./InputModality"; +export type { InternalSessionSource } from "./InternalSessionSource"; +export type { LocalShellAction } from "./LocalShellAction"; +export type { LocalShellExecAction } from "./LocalShellExecAction"; +export type { LocalShellStatus } from "./LocalShellStatus"; +export type { MessagePhase } from "./MessagePhase"; +export type { ModeKind } from "./ModeKind"; +export type { NetworkPolicyAmendment } from "./NetworkPolicyAmendment"; +export type { NetworkPolicyRuleAction } from "./NetworkPolicyRuleAction"; +export type { ParsedCommand } from "./ParsedCommand"; +export type { Personality } from "./Personality"; +export type { PlanType } from "./PlanType"; +export type { RealtimeConversationVersion } from "./RealtimeConversationVersion"; +export type { RealtimeOutputModality } from "./RealtimeOutputModality"; +export type { RealtimeVoice } from "./RealtimeVoice"; +export type { RealtimeVoicesList } from "./RealtimeVoicesList"; +export type { ReasoningEffort } from "./ReasoningEffort"; +export type { ReasoningItemContent } from "./ReasoningItemContent"; +export type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSummary"; +export type { ReasoningSummary } from "./ReasoningSummary"; +export type { RequestId } from "./RequestId"; +export type { Resource } from "./Resource"; +export type { ResourceContent } from "./ResourceContent"; +export type { ResourceTemplate } from "./ResourceTemplate"; +export type { ResponseItem } from "./ResponseItem"; +export type { ReviewDecision } from "./ReviewDecision"; +export type { ServerNotification } from "./ServerNotification"; +export type { ServerRequest } from "./ServerRequest"; +export type { SessionSource } from "./SessionSource"; +export type { Settings } from "./Settings"; +export type { SubAgentSource } from "./SubAgentSource"; +export type { ThreadId } from "./ThreadId"; +export type { ThreadMemoryMode } from "./ThreadMemoryMode"; +export type { Tool } from "./Tool"; +export type { Verbosity } from "./Verbosity"; +export type { WebSearchAction } from "./WebSearchAction"; +export type { WebSearchContextSize } from "./WebSearchContextSize"; +export type { WebSearchLocation } from "./WebSearchLocation"; +export type { WebSearchMode } from "./WebSearchMode"; +export type { WebSearchToolConfig } from "./WebSearchToolConfig"; +export * as v2 from "./v2"; diff --git a/src/generated/app-server/serde_json/JsonValue.ts b/src/generated/app-server/serde_json/JsonValue.ts new file mode 100644 index 00000000..75cf7389 --- /dev/null +++ b/src/generated/app-server/serde_json/JsonValue.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type JsonValue = number | string | boolean | Array | { [key in string]?: JsonValue } | null; diff --git a/src/generated/app-server/v2/Account.ts b/src/generated/app-server/v2/Account.ts new file mode 100644 index 00000000..4c3a58e8 --- /dev/null +++ b/src/generated/app-server/v2/Account.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PlanType } from "../PlanType"; + +export type Account = { "type": "apiKey", } | { "type": "chatgpt", email: string, planType: PlanType, } | { "type": "amazonBedrock", }; diff --git a/src/generated/app-server/v2/AccountLoginCompletedNotification.ts b/src/generated/app-server/v2/AccountLoginCompletedNotification.ts new file mode 100644 index 00000000..587237b2 --- /dev/null +++ b/src/generated/app-server/v2/AccountLoginCompletedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AccountLoginCompletedNotification = { loginId: string | null, success: boolean, error: string | null, }; diff --git a/src/generated/app-server/v2/AccountRateLimitsUpdatedNotification.ts b/src/generated/app-server/v2/AccountRateLimitsUpdatedNotification.ts new file mode 100644 index 00000000..96c735a2 --- /dev/null +++ b/src/generated/app-server/v2/AccountRateLimitsUpdatedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RateLimitSnapshot } from "./RateLimitSnapshot"; + +export type AccountRateLimitsUpdatedNotification = { rateLimits: RateLimitSnapshot, }; diff --git a/src/generated/app-server/v2/AccountUpdatedNotification.ts b/src/generated/app-server/v2/AccountUpdatedNotification.ts new file mode 100644 index 00000000..84bf626e --- /dev/null +++ b/src/generated/app-server/v2/AccountUpdatedNotification.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AuthMode } from "../AuthMode"; +import type { PlanType } from "../PlanType"; + +export type AccountUpdatedNotification = { authMode: AuthMode | null, planType: PlanType | null, }; diff --git a/src/generated/app-server/v2/ActivePermissionProfile.ts b/src/generated/app-server/v2/ActivePermissionProfile.ts new file mode 100644 index 00000000..cbc8c6ef --- /dev/null +++ b/src/generated/app-server/v2/ActivePermissionProfile.ts @@ -0,0 +1,21 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActivePermissionProfileModification } from "./ActivePermissionProfileModification"; + +export type ActivePermissionProfile = { +/** + * Identifier from `default_permissions` or the implicit built-in default, + * such as `:workspace` or a user-defined `[permissions.]` profile. + */ +id: string, +/** + * Parent profile identifier once permissions profiles support + * inheritance. This is currently always `null`. + */ +extends: string | null, +/** + * Bounded user-requested modifications applied on top of the named + * profile, if any. + */ +modifications: Array, }; diff --git a/src/generated/app-server/v2/ActivePermissionProfileModification.ts b/src/generated/app-server/v2/ActivePermissionProfileModification.ts new file mode 100644 index 00000000..1cbee686 --- /dev/null +++ b/src/generated/app-server/v2/ActivePermissionProfileModification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type ActivePermissionProfileModification = { "type": "additionalWritableRoot", path: AbsolutePathBuf, }; diff --git a/src/generated/app-server/v2/AddCreditsNudgeCreditType.ts b/src/generated/app-server/v2/AddCreditsNudgeCreditType.ts new file mode 100644 index 00000000..70498d6a --- /dev/null +++ b/src/generated/app-server/v2/AddCreditsNudgeCreditType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AddCreditsNudgeCreditType = "credits" | "usage_limit"; diff --git a/src/generated/app-server/v2/AddCreditsNudgeEmailStatus.ts b/src/generated/app-server/v2/AddCreditsNudgeEmailStatus.ts new file mode 100644 index 00000000..2b62da68 --- /dev/null +++ b/src/generated/app-server/v2/AddCreditsNudgeEmailStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AddCreditsNudgeEmailStatus = "sent" | "cooldown_active"; diff --git a/src/generated/app-server/v2/AdditionalFileSystemPermissions.ts b/src/generated/app-server/v2/AdditionalFileSystemPermissions.ts new file mode 100644 index 00000000..e29263b9 --- /dev/null +++ b/src/generated/app-server/v2/AdditionalFileSystemPermissions.ts @@ -0,0 +1,15 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { FileSystemSandboxEntry } from "./FileSystemSandboxEntry"; + +export type AdditionalFileSystemPermissions = { +/** + * This will be removed in favor of `entries`. + */ +read: Array | null, +/** + * This will be removed in favor of `entries`. + */ +write: Array | null, globScanMaxDepth?: number, entries?: Array, }; diff --git a/src/generated/app-server/v2/AdditionalNetworkPermissions.ts b/src/generated/app-server/v2/AdditionalNetworkPermissions.ts new file mode 100644 index 00000000..823de26c --- /dev/null +++ b/src/generated/app-server/v2/AdditionalNetworkPermissions.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AdditionalNetworkPermissions = { enabled: boolean | null, }; diff --git a/src/generated/app-server/v2/AdditionalPermissionProfile.ts b/src/generated/app-server/v2/AdditionalPermissionProfile.ts new file mode 100644 index 00000000..5120ec31 --- /dev/null +++ b/src/generated/app-server/v2/AdditionalPermissionProfile.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AdditionalFileSystemPermissions } from "./AdditionalFileSystemPermissions"; +import type { AdditionalNetworkPermissions } from "./AdditionalNetworkPermissions"; + +export type AdditionalPermissionProfile = { +/** + * Partial overlay used for per-command permission requests. + */ +network: AdditionalNetworkPermissions | null, fileSystem: AdditionalFileSystemPermissions | null, }; diff --git a/src/generated/app-server/v2/AgentMessageDeltaNotification.ts b/src/generated/app-server/v2/AgentMessageDeltaNotification.ts new file mode 100644 index 00000000..b47985e5 --- /dev/null +++ b/src/generated/app-server/v2/AgentMessageDeltaNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AgentMessageDeltaNotification = { threadId: string, turnId: string, itemId: string, delta: string, }; diff --git a/src/generated/app-server/v2/AnalyticsConfig.ts b/src/generated/app-server/v2/AnalyticsConfig.ts new file mode 100644 index 00000000..d095439a --- /dev/null +++ b/src/generated/app-server/v2/AnalyticsConfig.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; + +export type AnalyticsConfig = { enabled: boolean | null, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/src/generated/app-server/v2/AppBranding.ts b/src/generated/app-server/v2/AppBranding.ts new file mode 100644 index 00000000..873398db --- /dev/null +++ b/src/generated/app-server/v2/AppBranding.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - app metadata returned by app-list APIs. + */ +export type AppBranding = { category: string | null, developer: string | null, website: string | null, privacyPolicy: string | null, termsOfService: string | null, isDiscoverableApp: boolean, }; diff --git a/src/generated/app-server/v2/AppInfo.ts b/src/generated/app-server/v2/AppInfo.ts new file mode 100644 index 00000000..ef1f54aa --- /dev/null +++ b/src/generated/app-server/v2/AppInfo.ts @@ -0,0 +1,19 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AppBranding } from "./AppBranding"; +import type { AppMetadata } from "./AppMetadata"; + +/** + * EXPERIMENTAL - app metadata returned by app-list APIs. + */ +export type AppInfo = { id: string, name: string, description: string | null, logoUrl: string | null, logoUrlDark: string | null, distributionChannel: string | null, branding: AppBranding | null, appMetadata: AppMetadata | null, labels: { [key in string]?: string } | null, installUrl: string | null, isAccessible: boolean, +/** + * Whether this app is enabled in config.toml. + * Example: + * ```toml + * [apps.bad_app] + * enabled = false + * ``` + */ +isEnabled: boolean, pluginDisplayNames: Array, }; diff --git a/src/generated/app-server/v2/AppListUpdatedNotification.ts b/src/generated/app-server/v2/AppListUpdatedNotification.ts new file mode 100644 index 00000000..c6ad87f2 --- /dev/null +++ b/src/generated/app-server/v2/AppListUpdatedNotification.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AppInfo } from "./AppInfo"; + +/** + * EXPERIMENTAL - notification emitted when the app list changes. + */ +export type AppListUpdatedNotification = { data: Array, }; diff --git a/src/generated/app-server/v2/AppMetadata.ts b/src/generated/app-server/v2/AppMetadata.ts new file mode 100644 index 00000000..f1a5001e --- /dev/null +++ b/src/generated/app-server/v2/AppMetadata.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AppReview } from "./AppReview"; +import type { AppScreenshot } from "./AppScreenshot"; + +export type AppMetadata = { review: AppReview | null, categories: Array | null, subCategories: Array | null, seoDescription: string | null, screenshots: Array | null, developer: string | null, version: string | null, versionId: string | null, versionNotes: string | null, firstPartyType: string | null, firstPartyRequiresInstall: boolean | null, showInComposerWhenUnlinked: boolean | null, }; diff --git a/src/generated/app-server/v2/AppReview.ts b/src/generated/app-server/v2/AppReview.ts new file mode 100644 index 00000000..10fd95f0 --- /dev/null +++ b/src/generated/app-server/v2/AppReview.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AppReview = { status: string, }; diff --git a/src/generated/app-server/v2/AppScreenshot.ts b/src/generated/app-server/v2/AppScreenshot.ts new file mode 100644 index 00000000..0d264246 --- /dev/null +++ b/src/generated/app-server/v2/AppScreenshot.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AppScreenshot = { url: string | null, fileId: string | null, userPrompt: string, }; diff --git a/src/generated/app-server/v2/AppSummary.ts b/src/generated/app-server/v2/AppSummary.ts new file mode 100644 index 00000000..586c76f8 --- /dev/null +++ b/src/generated/app-server/v2/AppSummary.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - app metadata summary for plugin responses. + */ +export type AppSummary = { id: string, name: string, description: string | null, installUrl: string | null, needsAuth: boolean, }; diff --git a/src/generated/app-server/v2/AppToolApproval.ts b/src/generated/app-server/v2/AppToolApproval.ts new file mode 100644 index 00000000..e92cd8e2 --- /dev/null +++ b/src/generated/app-server/v2/AppToolApproval.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AppToolApproval = "auto" | "prompt" | "approve"; diff --git a/src/generated/app-server/v2/AppToolsConfig.ts b/src/generated/app-server/v2/AppToolsConfig.ts new file mode 100644 index 00000000..16a1c22c --- /dev/null +++ b/src/generated/app-server/v2/AppToolsConfig.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AppToolApproval } from "./AppToolApproval"; + +export type AppToolsConfig = { [key in string]?: { enabled: boolean | null, approval_mode: AppToolApproval | null, } }; diff --git a/src/generated/app-server/v2/ApprovalsReviewer.ts b/src/generated/app-server/v2/ApprovalsReviewer.ts new file mode 100644 index 00000000..1d932946 --- /dev/null +++ b/src/generated/app-server/v2/ApprovalsReviewer.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Configures who approval requests are routed to for review. Examples + * include sandbox escapes, blocked network access, MCP approval prompts, and + * ARC escalations. Defaults to `user`. `auto_review` uses a carefully + * prompted subagent to gather relevant context and apply a risk-based + * decision framework before approving or denying the request. + */ +export type ApprovalsReviewer = "user" | "auto_review" | "guardian_subagent"; diff --git a/src/generated/app-server/v2/AppsConfig.ts b/src/generated/app-server/v2/AppsConfig.ts new file mode 100644 index 00000000..b22997de --- /dev/null +++ b/src/generated/app-server/v2/AppsConfig.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AppToolApproval } from "./AppToolApproval"; +import type { AppToolsConfig } from "./AppToolsConfig"; +import type { AppsDefaultConfig } from "./AppsDefaultConfig"; + +export type AppsConfig = { _default: AppsDefaultConfig | null, } & ({ [key in string]?: { enabled: boolean, destructive_enabled: boolean | null, open_world_enabled: boolean | null, default_tools_approval_mode: AppToolApproval | null, default_tools_enabled: boolean | null, tools: AppToolsConfig | null, } }); diff --git a/src/generated/app-server/v2/AppsDefaultConfig.ts b/src/generated/app-server/v2/AppsDefaultConfig.ts new file mode 100644 index 00000000..e7338602 --- /dev/null +++ b/src/generated/app-server/v2/AppsDefaultConfig.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AppsDefaultConfig = { enabled: boolean, destructive_enabled: boolean, open_world_enabled: boolean, }; diff --git a/src/generated/app-server/v2/AppsListParams.ts b/src/generated/app-server/v2/AppsListParams.ts new file mode 100644 index 00000000..b9682956 --- /dev/null +++ b/src/generated/app-server/v2/AppsListParams.ts @@ -0,0 +1,24 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - list available apps/connectors. + */ +export type AppsListParams = { +/** + * Opaque pagination cursor returned by a previous call. + */ +cursor?: string | null, +/** + * Optional page size; defaults to a reasonable server-side value. + */ +limit?: number | null, +/** + * Optional thread id used to evaluate app feature gating from that thread's config. + */ +threadId?: string | null, +/** + * When true, bypass app caches and fetch the latest data from sources. + */ +forceRefetch?: boolean, }; diff --git a/src/generated/app-server/v2/AppsListResponse.ts b/src/generated/app-server/v2/AppsListResponse.ts new file mode 100644 index 00000000..dabeded1 --- /dev/null +++ b/src/generated/app-server/v2/AppsListResponse.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AppInfo } from "./AppInfo"; + +/** + * EXPERIMENTAL - app list response. + */ +export type AppsListResponse = { data: Array, +/** + * Opaque cursor to pass to the next call to continue after the last item. + * If None, there are no more items to return. + */ +nextCursor: string | null, }; diff --git a/src/generated/app-server/v2/AskForApproval.ts b/src/generated/app-server/v2/AskForApproval.ts new file mode 100644 index 00000000..8d41214e --- /dev/null +++ b/src/generated/app-server/v2/AskForApproval.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AskForApproval = "untrusted" | "on-failure" | "on-request" | { "granular": { sandbox_approval: boolean, rules: boolean, skill_approval: boolean, request_permissions: boolean, mcp_elicitations: boolean, } } | "never"; diff --git a/src/generated/app-server/v2/AutoReviewDecisionSource.ts b/src/generated/app-server/v2/AutoReviewDecisionSource.ts new file mode 100644 index 00000000..88069812 --- /dev/null +++ b/src/generated/app-server/v2/AutoReviewDecisionSource.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * [UNSTABLE] Source that produced a terminal approval auto-review decision. + */ +export type AutoReviewDecisionSource = "agent"; diff --git a/src/generated/app-server/v2/ByteRange.ts b/src/generated/app-server/v2/ByteRange.ts new file mode 100644 index 00000000..6cb81b87 --- /dev/null +++ b/src/generated/app-server/v2/ByteRange.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ByteRange = { start: number, end: number, }; diff --git a/src/generated/app-server/v2/CancelLoginAccountParams.ts b/src/generated/app-server/v2/CancelLoginAccountParams.ts new file mode 100644 index 00000000..8e2e90df --- /dev/null +++ b/src/generated/app-server/v2/CancelLoginAccountParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CancelLoginAccountParams = { loginId: string, }; diff --git a/src/generated/app-server/v2/CancelLoginAccountResponse.ts b/src/generated/app-server/v2/CancelLoginAccountResponse.ts new file mode 100644 index 00000000..2e7b3d03 --- /dev/null +++ b/src/generated/app-server/v2/CancelLoginAccountResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CancelLoginAccountStatus } from "./CancelLoginAccountStatus"; + +export type CancelLoginAccountResponse = { status: CancelLoginAccountStatus, }; diff --git a/src/generated/app-server/v2/CancelLoginAccountStatus.ts b/src/generated/app-server/v2/CancelLoginAccountStatus.ts new file mode 100644 index 00000000..bd851c6a --- /dev/null +++ b/src/generated/app-server/v2/CancelLoginAccountStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CancelLoginAccountStatus = "canceled" | "notFound"; diff --git a/src/generated/app-server/v2/ChatgptAuthTokensRefreshParams.ts b/src/generated/app-server/v2/ChatgptAuthTokensRefreshParams.ts new file mode 100644 index 00000000..d59cc30d --- /dev/null +++ b/src/generated/app-server/v2/ChatgptAuthTokensRefreshParams.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ChatgptAuthTokensRefreshReason } from "./ChatgptAuthTokensRefreshReason"; + +export type ChatgptAuthTokensRefreshParams = { reason: ChatgptAuthTokensRefreshReason, +/** + * Workspace/account identifier that Codex was previously using. + * + * Clients that manage multiple accounts/workspaces can use this as a hint + * to refresh the token for the correct workspace. + * + * This may be `null` when the prior auth state did not include a workspace + * identifier (`chatgpt_account_id`). + */ +previousAccountId?: string | null, }; diff --git a/src/generated/app-server/v2/ChatgptAuthTokensRefreshReason.ts b/src/generated/app-server/v2/ChatgptAuthTokensRefreshReason.ts new file mode 100644 index 00000000..ac4006ba --- /dev/null +++ b/src/generated/app-server/v2/ChatgptAuthTokensRefreshReason.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ChatgptAuthTokensRefreshReason = "unauthorized"; diff --git a/src/generated/app-server/v2/ChatgptAuthTokensRefreshResponse.ts b/src/generated/app-server/v2/ChatgptAuthTokensRefreshResponse.ts new file mode 100644 index 00000000..30bf03e8 --- /dev/null +++ b/src/generated/app-server/v2/ChatgptAuthTokensRefreshResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ChatgptAuthTokensRefreshResponse = { accessToken: string, chatgptAccountId: string, chatgptPlanType: string | null, }; diff --git a/src/generated/app-server/v2/CodexErrorInfo.ts b/src/generated/app-server/v2/CodexErrorInfo.ts new file mode 100644 index 00000000..6e975abf --- /dev/null +++ b/src/generated/app-server/v2/CodexErrorInfo.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { NonSteerableTurnKind } from "./NonSteerableTurnKind"; + +/** + * This translation layer make sure that we expose codex error code in camel case. + * + * When an upstream HTTP status is available (for example, from the Responses API or a provider), + * it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant. + */ +export type CodexErrorInfo = "contextWindowExceeded" | "usageLimitExceeded" | "serverOverloaded" | "cyberPolicy" | { "httpConnectionFailed": { httpStatusCode: number | null, } } | { "responseStreamConnectionFailed": { httpStatusCode: number | null, } } | "internalServerError" | "unauthorized" | "badRequest" | "threadRollbackFailed" | "sandboxError" | { "responseStreamDisconnected": { httpStatusCode: number | null, } } | { "responseTooManyFailedAttempts": { httpStatusCode: number | null, } } | { "activeTurnNotSteerable": { turnKind: NonSteerableTurnKind, } } | "other"; diff --git a/src/generated/app-server/v2/CollabAgentState.ts b/src/generated/app-server/v2/CollabAgentState.ts new file mode 100644 index 00000000..785dbf1f --- /dev/null +++ b/src/generated/app-server/v2/CollabAgentState.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CollabAgentStatus } from "./CollabAgentStatus"; + +export type CollabAgentState = { status: CollabAgentStatus, message: string | null, }; diff --git a/src/generated/app-server/v2/CollabAgentStatus.ts b/src/generated/app-server/v2/CollabAgentStatus.ts new file mode 100644 index 00000000..66d3119b --- /dev/null +++ b/src/generated/app-server/v2/CollabAgentStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CollabAgentStatus = "pendingInit" | "running" | "interrupted" | "completed" | "errored" | "shutdown" | "notFound"; diff --git a/src/generated/app-server/v2/CollabAgentTool.ts b/src/generated/app-server/v2/CollabAgentTool.ts new file mode 100644 index 00000000..3637853a --- /dev/null +++ b/src/generated/app-server/v2/CollabAgentTool.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CollabAgentTool = "spawnAgent" | "sendInput" | "resumeAgent" | "wait" | "closeAgent"; diff --git a/src/generated/app-server/v2/CollabAgentToolCallStatus.ts b/src/generated/app-server/v2/CollabAgentToolCallStatus.ts new file mode 100644 index 00000000..f21f7bd5 --- /dev/null +++ b/src/generated/app-server/v2/CollabAgentToolCallStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CollabAgentToolCallStatus = "inProgress" | "completed" | "failed"; diff --git a/src/generated/app-server/v2/CollaborationModeListParams.ts b/src/generated/app-server/v2/CollaborationModeListParams.ts new file mode 100644 index 00000000..37e8f792 --- /dev/null +++ b/src/generated/app-server/v2/CollaborationModeListParams.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - list collaboration mode presets. + */ +export type CollaborationModeListParams = Record; diff --git a/src/generated/app-server/v2/CollaborationModeListResponse.ts b/src/generated/app-server/v2/CollaborationModeListResponse.ts new file mode 100644 index 00000000..5da935b9 --- /dev/null +++ b/src/generated/app-server/v2/CollaborationModeListResponse.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CollaborationModeMask } from "./CollaborationModeMask"; + +/** + * EXPERIMENTAL - collaboration mode presets response. + */ +export type CollaborationModeListResponse = { data: Array, }; diff --git a/src/generated/app-server/v2/CollaborationModeMask.ts b/src/generated/app-server/v2/CollaborationModeMask.ts new file mode 100644 index 00000000..83adc644 --- /dev/null +++ b/src/generated/app-server/v2/CollaborationModeMask.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ModeKind } from "../ModeKind"; +import type { ReasoningEffort } from "../ReasoningEffort"; + +/** + * EXPERIMENTAL - collaboration mode preset metadata for clients. + */ +export type CollaborationModeMask = { name: string, mode: ModeKind | null, model: string | null, reasoning_effort: ReasoningEffort | null | null, }; diff --git a/src/generated/app-server/v2/CommandAction.ts b/src/generated/app-server/v2/CommandAction.ts new file mode 100644 index 00000000..a17fb06a --- /dev/null +++ b/src/generated/app-server/v2/CommandAction.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type CommandAction = { "type": "read", command: string, name: string, path: AbsolutePathBuf, } | { "type": "listFiles", command: string, path: string | null, } | { "type": "search", command: string, query: string | null, path: string | null, } | { "type": "unknown", command: string, }; diff --git a/src/generated/app-server/v2/CommandExecOutputDeltaNotification.ts b/src/generated/app-server/v2/CommandExecOutputDeltaNotification.ts new file mode 100644 index 00000000..a6c2ea45 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecOutputDeltaNotification.ts @@ -0,0 +1,30 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CommandExecOutputStream } from "./CommandExecOutputStream"; + +/** + * Base64-encoded output chunk emitted for a streaming `command/exec` request. + * + * These notifications are connection-scoped. If the originating connection + * closes, the server terminates the process. + */ +export type CommandExecOutputDeltaNotification = { +/** + * Client-supplied, connection-scoped `processId` from the original + * `command/exec` request. + */ +processId: string, +/** + * Output stream for this chunk. + */ +stream: CommandExecOutputStream, +/** + * Base64-encoded output bytes. + */ +deltaBase64: string, +/** + * `true` on the final streamed chunk for a stream when `outputBytesCap` + * truncated later output on that stream. + */ +capReached: boolean, }; diff --git a/src/generated/app-server/v2/CommandExecOutputStream.ts b/src/generated/app-server/v2/CommandExecOutputStream.ts new file mode 100644 index 00000000..a8c5b667 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecOutputStream.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Stream label for `command/exec/outputDelta` notifications. + */ +export type CommandExecOutputStream = "stdout" | "stderr"; diff --git a/src/generated/app-server/v2/CommandExecParams.ts b/src/generated/app-server/v2/CommandExecParams.ts new file mode 100644 index 00000000..659974fe --- /dev/null +++ b/src/generated/app-server/v2/CommandExecParams.ts @@ -0,0 +1,106 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CommandExecTerminalSize } from "./CommandExecTerminalSize"; +import type { PermissionProfile } from "./PermissionProfile"; +import type { SandboxPolicy } from "./SandboxPolicy"; + +/** + * Run a standalone command (argv vector) in the server sandbox without + * creating a thread or turn. + * + * The final `command/exec` response is deferred until the process exits and is + * sent only after all `command/exec/outputDelta` notifications for that + * connection have been emitted. + */ +export type CommandExecParams = { +/** + * Command argv vector. Empty arrays are rejected. + */ +command: Array, +/** + * Optional client-supplied, connection-scoped process id. + * + * Required for `tty`, `streamStdin`, `streamStdoutStderr`, and follow-up + * `command/exec/write`, `command/exec/resize`, and + * `command/exec/terminate` calls. When omitted, buffered execution gets an + * internal id that is not exposed to the client. + */ +processId?: string | null, +/** + * Enable PTY mode. + * + * This implies `streamStdin` and `streamStdoutStderr`. + */ +tty?: boolean, +/** + * Allow follow-up `command/exec/write` requests to write stdin bytes. + * + * Requires a client-supplied `processId`. + */ +streamStdin?: boolean, +/** + * Stream stdout/stderr via `command/exec/outputDelta` notifications. + * + * Streamed bytes are not duplicated into the final response and require a + * client-supplied `processId`. + */ +streamStdoutStderr?: boolean, +/** + * Optional per-stream stdout/stderr capture cap in bytes. + * + * When omitted, the server default applies. Cannot be combined with + * `disableOutputCap`. + */ +outputBytesCap?: number | null, +/** + * Disable stdout/stderr capture truncation for this request. + * + * Cannot be combined with `outputBytesCap`. + */ +disableOutputCap?: boolean, +/** + * Disable the timeout entirely for this request. + * + * Cannot be combined with `timeoutMs`. + */ +disableTimeout?: boolean, +/** + * Optional timeout in milliseconds. + * + * When omitted, the server default applies. Cannot be combined with + * `disableTimeout`. + */ +timeoutMs?: number | null, +/** + * Optional working directory. Defaults to the server cwd. + */ +cwd?: string | null, +/** + * Optional environment overrides merged into the server-computed + * environment. + * + * Matching names override inherited values. Set a key to `null` to unset + * an inherited variable. + */ +env?: { [key in string]?: string | null } | null, +/** + * Optional initial PTY size in character cells. Only valid when `tty` is + * true. + */ +size?: CommandExecTerminalSize | null, +/** + * Optional sandbox policy for this command. + * + * Uses the same shape as thread/turn execution sandbox configuration and + * defaults to the user's configured policy when omitted. Cannot be + * combined with `permissionProfile`. + */ +sandboxPolicy?: SandboxPolicy | null, +/** + * Optional full permissions profile for this command. + * + * Defaults to the user's configured permissions when omitted. Cannot be + * combined with `sandboxPolicy`. + */ +permissionProfile?: PermissionProfile | null, }; diff --git a/src/generated/app-server/v2/CommandExecResizeParams.ts b/src/generated/app-server/v2/CommandExecResizeParams.ts new file mode 100644 index 00000000..40a05dc7 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecResizeParams.ts @@ -0,0 +1,18 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CommandExecTerminalSize } from "./CommandExecTerminalSize"; + +/** + * Resize a running PTY-backed `command/exec` session. + */ +export type CommandExecResizeParams = { +/** + * Client-supplied, connection-scoped `processId` from the original + * `command/exec` request. + */ +processId: string, +/** + * New PTY size in character cells. + */ +size: CommandExecTerminalSize, }; diff --git a/src/generated/app-server/v2/CommandExecResizeResponse.ts b/src/generated/app-server/v2/CommandExecResizeResponse.ts new file mode 100644 index 00000000..7b7f2be7 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecResizeResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Empty success response for `command/exec/resize`. + */ +export type CommandExecResizeResponse = Record; diff --git a/src/generated/app-server/v2/CommandExecResponse.ts b/src/generated/app-server/v2/CommandExecResponse.ts new file mode 100644 index 00000000..25e01eb5 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecResponse.ts @@ -0,0 +1,24 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Final buffered result for `command/exec`. + */ +export type CommandExecResponse = { +/** + * Process exit code. + */ +exitCode: number, +/** + * Buffered stdout capture. + * + * Empty when stdout was streamed via `command/exec/outputDelta`. + */ +stdout: string, +/** + * Buffered stderr capture. + * + * Empty when stderr was streamed via `command/exec/outputDelta`. + */ +stderr: string, }; diff --git a/src/generated/app-server/v2/CommandExecTerminalSize.ts b/src/generated/app-server/v2/CommandExecTerminalSize.ts new file mode 100644 index 00000000..0bfacb62 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecTerminalSize.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * PTY size in character cells for `command/exec` PTY sessions. + */ +export type CommandExecTerminalSize = { +/** + * Terminal height in character cells. + */ +rows: number, +/** + * Terminal width in character cells. + */ +cols: number, }; diff --git a/src/generated/app-server/v2/CommandExecTerminateParams.ts b/src/generated/app-server/v2/CommandExecTerminateParams.ts new file mode 100644 index 00000000..cae97057 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecTerminateParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Terminate a running `command/exec` session. + */ +export type CommandExecTerminateParams = { +/** + * Client-supplied, connection-scoped `processId` from the original + * `command/exec` request. + */ +processId: string, }; diff --git a/src/generated/app-server/v2/CommandExecTerminateResponse.ts b/src/generated/app-server/v2/CommandExecTerminateResponse.ts new file mode 100644 index 00000000..dc6371fb --- /dev/null +++ b/src/generated/app-server/v2/CommandExecTerminateResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Empty success response for `command/exec/terminate`. + */ +export type CommandExecTerminateResponse = Record; diff --git a/src/generated/app-server/v2/CommandExecWriteParams.ts b/src/generated/app-server/v2/CommandExecWriteParams.ts new file mode 100644 index 00000000..2092c793 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecWriteParams.ts @@ -0,0 +1,22 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Write stdin bytes to a running `command/exec` session, close stdin, or + * both. + */ +export type CommandExecWriteParams = { +/** + * Client-supplied, connection-scoped `processId` from the original + * `command/exec` request. + */ +processId: string, +/** + * Optional base64-encoded stdin bytes to write. + */ +deltaBase64?: string | null, +/** + * Close stdin after writing `deltaBase64`, if present. + */ +closeStdin?: boolean, }; diff --git a/src/generated/app-server/v2/CommandExecWriteResponse.ts b/src/generated/app-server/v2/CommandExecWriteResponse.ts new file mode 100644 index 00000000..6dbbddf4 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecWriteResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Empty success response for `command/exec/write`. + */ +export type CommandExecWriteResponse = Record; diff --git a/src/generated/app-server/v2/CommandExecutionApprovalDecision.ts b/src/generated/app-server/v2/CommandExecutionApprovalDecision.ts new file mode 100644 index 00000000..c022030a --- /dev/null +++ b/src/generated/app-server/v2/CommandExecutionApprovalDecision.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExecPolicyAmendment } from "./ExecPolicyAmendment"; +import type { NetworkPolicyAmendment } from "./NetworkPolicyAmendment"; + +export type CommandExecutionApprovalDecision = "accept" | "acceptForSession" | { "acceptWithExecpolicyAmendment": { execpolicy_amendment: ExecPolicyAmendment, } } | { "applyNetworkPolicyAmendment": { network_policy_amendment: NetworkPolicyAmendment, } } | "decline" | "cancel"; diff --git a/src/generated/app-server/v2/CommandExecutionOutputDeltaNotification.ts b/src/generated/app-server/v2/CommandExecutionOutputDeltaNotification.ts new file mode 100644 index 00000000..90a4ae17 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecutionOutputDeltaNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CommandExecutionOutputDeltaNotification = { threadId: string, turnId: string, itemId: string, delta: string, }; diff --git a/src/generated/app-server/v2/CommandExecutionRequestApprovalParams.ts b/src/generated/app-server/v2/CommandExecutionRequestApprovalParams.ts new file mode 100644 index 00000000..824e9850 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecutionRequestApprovalParams.ts @@ -0,0 +1,62 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { AdditionalPermissionProfile } from "./AdditionalPermissionProfile"; +import type { CommandAction } from "./CommandAction"; +import type { CommandExecutionApprovalDecision } from "./CommandExecutionApprovalDecision"; +import type { ExecPolicyAmendment } from "./ExecPolicyAmendment"; +import type { NetworkApprovalContext } from "./NetworkApprovalContext"; +import type { NetworkPolicyAmendment } from "./NetworkPolicyAmendment"; + +export type CommandExecutionRequestApprovalParams = { threadId: string, turnId: string, itemId: string, +/** + * Unix timestamp (in milliseconds) when this approval request started. + */ +startedAtMs: number, +/** + * Unique identifier for this specific approval callback. + * + * For regular shell/unified_exec approvals, this is null. + * + * For zsh-exec-bridge subcommand approvals, multiple callbacks can belong to + * one parent `itemId`, so `approvalId` is a distinct opaque callback id + * (a UUID) used to disambiguate routing. + */ +approvalId?: string | null, +/** + * Optional explanatory reason (e.g. request for network access). + */ +reason?: string | null, +/** + * Optional context for a managed-network approval prompt. + */ +networkApprovalContext?: NetworkApprovalContext | null, +/** + * The command to be executed. + */ +command?: string | null, +/** + * The command's working directory. + */ +cwd?: AbsolutePathBuf | null, +/** + * Best-effort parsed command actions for friendly display. + */ +commandActions?: Array | null, +/** + * Optional additional permissions requested for this command. + */ +additionalPermissions?: AdditionalPermissionProfile | null, +/** + * Optional proposed execpolicy amendment to allow similar commands without prompting. + */ +proposedExecpolicyAmendment?: ExecPolicyAmendment | null, +/** + * Optional proposed network policy amendments (allow/deny host) for future requests. + */ +proposedNetworkPolicyAmendments?: Array | null, +/** + * Ordered list of decisions the client may present for this prompt. + */ +availableDecisions?: Array | null, }; diff --git a/src/generated/app-server/v2/CommandExecutionRequestApprovalResponse.ts b/src/generated/app-server/v2/CommandExecutionRequestApprovalResponse.ts new file mode 100644 index 00000000..33df2256 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecutionRequestApprovalResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CommandExecutionApprovalDecision } from "./CommandExecutionApprovalDecision"; + +export type CommandExecutionRequestApprovalResponse = { decision: CommandExecutionApprovalDecision, }; diff --git a/src/generated/app-server/v2/CommandExecutionSource.ts b/src/generated/app-server/v2/CommandExecutionSource.ts new file mode 100644 index 00000000..9432841f --- /dev/null +++ b/src/generated/app-server/v2/CommandExecutionSource.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CommandExecutionSource = "agent" | "userShell" | "unifiedExecStartup" | "unifiedExecInteraction"; diff --git a/src/generated/app-server/v2/CommandExecutionStatus.ts b/src/generated/app-server/v2/CommandExecutionStatus.ts new file mode 100644 index 00000000..c58b3cc7 --- /dev/null +++ b/src/generated/app-server/v2/CommandExecutionStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CommandExecutionStatus = "inProgress" | "completed" | "failed" | "declined"; diff --git a/src/generated/app-server/v2/CommandMigration.ts b/src/generated/app-server/v2/CommandMigration.ts new file mode 100644 index 00000000..fdf28f31 --- /dev/null +++ b/src/generated/app-server/v2/CommandMigration.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CommandMigration = { name: string, }; diff --git a/src/generated/app-server/v2/Config.ts b/src/generated/app-server/v2/Config.ts new file mode 100644 index 00000000..c3389b4f --- /dev/null +++ b/src/generated/app-server/v2/Config.ts @@ -0,0 +1,24 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ForcedLoginMethod } from "../ForcedLoginMethod"; +import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ReasoningSummary } from "../ReasoningSummary"; +import type { Verbosity } from "../Verbosity"; +import type { WebSearchMode } from "../WebSearchMode"; +import type { JsonValue } from "../serde_json/JsonValue"; +import type { AnalyticsConfig } from "./AnalyticsConfig"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AppsConfig } from "./AppsConfig"; +import type { AskForApproval } from "./AskForApproval"; +import type { ProfileV2 } from "./ProfileV2"; +import type { SandboxMode } from "./SandboxMode"; +import type { SandboxWorkspaceWrite } from "./SandboxWorkspaceWrite"; +import type { ToolsV2 } from "./ToolsV2"; + +export type Config = { model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, +/** + * [UNSTABLE] Optional default for where approval requests are routed for + * review. + */ +approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: string | null, analytics: AnalyticsConfig | null, apps: AppsConfig | null, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/src/generated/app-server/v2/ConfigBatchWriteParams.ts b/src/generated/app-server/v2/ConfigBatchWriteParams.ts new file mode 100644 index 00000000..352eac28 --- /dev/null +++ b/src/generated/app-server/v2/ConfigBatchWriteParams.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ConfigEdit } from "./ConfigEdit"; + +export type ConfigBatchWriteParams = { edits: Array, +/** + * Path to the config file to write; defaults to the user's `config.toml` when omitted. + */ +filePath?: string | null, expectedVersion?: string | null, +/** + * When true, hot-reload the updated user config into all loaded threads after writing. + */ +reloadUserConfig?: boolean, }; diff --git a/src/generated/app-server/v2/ConfigEdit.ts b/src/generated/app-server/v2/ConfigEdit.ts new file mode 100644 index 00000000..fee14aab --- /dev/null +++ b/src/generated/app-server/v2/ConfigEdit.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; +import type { MergeStrategy } from "./MergeStrategy"; + +export type ConfigEdit = { keyPath: string, value: JsonValue, mergeStrategy: MergeStrategy, }; diff --git a/src/generated/app-server/v2/ConfigLayer.ts b/src/generated/app-server/v2/ConfigLayer.ts new file mode 100644 index 00000000..6fe7c991 --- /dev/null +++ b/src/generated/app-server/v2/ConfigLayer.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; +import type { ConfigLayerSource } from "./ConfigLayerSource"; + +export type ConfigLayer = { name: ConfigLayerSource, version: string, config: JsonValue, disabledReason: string | null, }; diff --git a/src/generated/app-server/v2/ConfigLayerMetadata.ts b/src/generated/app-server/v2/ConfigLayerMetadata.ts new file mode 100644 index 00000000..fbb334e5 --- /dev/null +++ b/src/generated/app-server/v2/ConfigLayerMetadata.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ConfigLayerSource } from "./ConfigLayerSource"; + +export type ConfigLayerMetadata = { name: ConfigLayerSource, version: string, }; diff --git a/src/generated/app-server/v2/ConfigLayerSource.ts b/src/generated/app-server/v2/ConfigLayerSource.ts new file mode 100644 index 00000000..1fe19b67 --- /dev/null +++ b/src/generated/app-server/v2/ConfigLayerSource.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type ConfigLayerSource = { "type": "mdm", domain: string, key: string, } | { "type": "system", +/** + * This is the path to the system config.toml file, though it is not + * guaranteed to exist. + */ +file: AbsolutePathBuf, } | { "type": "user", +/** + * This is the path to the user's config.toml file, though it is not + * guaranteed to exist. + */ +file: AbsolutePathBuf, } | { "type": "project", dotCodexFolder: AbsolutePathBuf, } | { "type": "sessionFlags" } | { "type": "legacyManagedConfigTomlFromFile", file: AbsolutePathBuf, } | { "type": "legacyManagedConfigTomlFromMdm" }; diff --git a/src/generated/app-server/v2/ConfigReadParams.ts b/src/generated/app-server/v2/ConfigReadParams.ts new file mode 100644 index 00000000..1fd418d1 --- /dev/null +++ b/src/generated/app-server/v2/ConfigReadParams.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ConfigReadParams = { includeLayers: boolean, +/** + * Optional working directory to resolve project config layers. If specified, + * return the effective config as seen from that directory (i.e., including any + * project layers between `cwd` and the project/repo root). + */ +cwd?: string | null, }; diff --git a/src/generated/app-server/v2/ConfigReadResponse.ts b/src/generated/app-server/v2/ConfigReadResponse.ts new file mode 100644 index 00000000..6b9c6a5c --- /dev/null +++ b/src/generated/app-server/v2/ConfigReadResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Config } from "./Config"; +import type { ConfigLayer } from "./ConfigLayer"; +import type { ConfigLayerMetadata } from "./ConfigLayerMetadata"; + +export type ConfigReadResponse = { config: Config, origins: { [key in string]?: ConfigLayerMetadata }, layers: Array | null, }; diff --git a/src/generated/app-server/v2/ConfigRequirements.ts b/src/generated/app-server/v2/ConfigRequirements.ts new file mode 100644 index 00000000..4349cf88 --- /dev/null +++ b/src/generated/app-server/v2/ConfigRequirements.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { WebSearchMode } from "../WebSearchMode"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AskForApproval } from "./AskForApproval"; +import type { ManagedHooksRequirements } from "./ManagedHooksRequirements"; +import type { NetworkRequirements } from "./NetworkRequirements"; +import type { ResidencyRequirement } from "./ResidencyRequirement"; +import type { SandboxMode } from "./SandboxMode"; + +export type ConfigRequirements = { allowedApprovalPolicies: Array | null, allowedApprovalsReviewers: Array | null, allowedSandboxModes: Array | null, allowedWebSearchModes: Array | null, featureRequirements: { [key in string]?: boolean } | null, hooks: ManagedHooksRequirements | null, enforceResidency: ResidencyRequirement | null, network: NetworkRequirements | null, }; diff --git a/src/generated/app-server/v2/ConfigRequirementsReadResponse.ts b/src/generated/app-server/v2/ConfigRequirementsReadResponse.ts new file mode 100644 index 00000000..f2de11d9 --- /dev/null +++ b/src/generated/app-server/v2/ConfigRequirementsReadResponse.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ConfigRequirements } from "./ConfigRequirements"; + +export type ConfigRequirementsReadResponse = { +/** + * Null if no requirements are configured (e.g. no requirements.toml/MDM entries). + */ +requirements: ConfigRequirements | null, }; diff --git a/src/generated/app-server/v2/ConfigValueWriteParams.ts b/src/generated/app-server/v2/ConfigValueWriteParams.ts new file mode 100644 index 00000000..709173d7 --- /dev/null +++ b/src/generated/app-server/v2/ConfigValueWriteParams.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; +import type { MergeStrategy } from "./MergeStrategy"; + +export type ConfigValueWriteParams = { keyPath: string, value: JsonValue, mergeStrategy: MergeStrategy, +/** + * Path to the config file to write; defaults to the user's `config.toml` when omitted. + */ +filePath?: string | null, expectedVersion?: string | null, }; diff --git a/src/generated/app-server/v2/ConfigWarningNotification.ts b/src/generated/app-server/v2/ConfigWarningNotification.ts new file mode 100644 index 00000000..e0cdf392 --- /dev/null +++ b/src/generated/app-server/v2/ConfigWarningNotification.ts @@ -0,0 +1,22 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { TextRange } from "./TextRange"; + +export type ConfigWarningNotification = { +/** + * Concise summary of the warning. + */ +summary: string, +/** + * Optional extra guidance or error details. + */ +details: string | null, +/** + * Optional path to the config file that triggered the warning. + */ +path?: string, +/** + * Optional range for the error location inside the config file. + */ +range?: TextRange, }; diff --git a/src/generated/app-server/v2/ConfigWriteResponse.ts b/src/generated/app-server/v2/ConfigWriteResponse.ts new file mode 100644 index 00000000..55cdce37 --- /dev/null +++ b/src/generated/app-server/v2/ConfigWriteResponse.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { OverriddenMetadata } from "./OverriddenMetadata"; +import type { WriteStatus } from "./WriteStatus"; + +export type ConfigWriteResponse = { status: WriteStatus, version: string, +/** + * Canonical path to the config file that was written. + */ +filePath: AbsolutePathBuf, overriddenMetadata: OverriddenMetadata | null, }; diff --git a/src/generated/app-server/v2/ConfiguredHookHandler.ts b/src/generated/app-server/v2/ConfiguredHookHandler.ts new file mode 100644 index 00000000..a81ce61f --- /dev/null +++ b/src/generated/app-server/v2/ConfiguredHookHandler.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ConfiguredHookHandler = { "type": "command", command: string, timeoutSec: bigint | null, async: boolean, statusMessage: string | null, } | { "type": "prompt", } | { "type": "agent", }; diff --git a/src/generated/app-server/v2/ConfiguredHookMatcherGroup.ts b/src/generated/app-server/v2/ConfiguredHookMatcherGroup.ts new file mode 100644 index 00000000..2c00fc16 --- /dev/null +++ b/src/generated/app-server/v2/ConfiguredHookMatcherGroup.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ConfiguredHookHandler } from "./ConfiguredHookHandler"; + +export type ConfiguredHookMatcherGroup = { matcher: string | null, hooks: Array, }; diff --git a/src/generated/app-server/v2/ContextCompactedNotification.ts b/src/generated/app-server/v2/ContextCompactedNotification.ts new file mode 100644 index 00000000..6927609d --- /dev/null +++ b/src/generated/app-server/v2/ContextCompactedNotification.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Deprecated: Use `ContextCompaction` item type instead. + */ +export type ContextCompactedNotification = { threadId: string, turnId: string, }; diff --git a/src/generated/app-server/v2/CreditsSnapshot.ts b/src/generated/app-server/v2/CreditsSnapshot.ts new file mode 100644 index 00000000..94577df6 --- /dev/null +++ b/src/generated/app-server/v2/CreditsSnapshot.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CreditsSnapshot = { hasCredits: boolean, unlimited: boolean, balance: string | null, }; diff --git a/src/generated/app-server/v2/DeprecationNoticeNotification.ts b/src/generated/app-server/v2/DeprecationNoticeNotification.ts new file mode 100644 index 00000000..29b61171 --- /dev/null +++ b/src/generated/app-server/v2/DeprecationNoticeNotification.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type DeprecationNoticeNotification = { +/** + * Concise summary of what is deprecated. + */ +summary: string, +/** + * Optional extra guidance, such as migration steps or rationale. + */ +details: string | null, }; diff --git a/src/generated/app-server/v2/DeviceKeyAlgorithm.ts b/src/generated/app-server/v2/DeviceKeyAlgorithm.ts new file mode 100644 index 00000000..6809c41e --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeyAlgorithm.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Device-key algorithm reported at enrollment and signing boundaries. + */ +export type DeviceKeyAlgorithm = "ecdsa_p256_sha256"; diff --git a/src/generated/app-server/v2/DeviceKeyCreateParams.ts b/src/generated/app-server/v2/DeviceKeyCreateParams.ts new file mode 100644 index 00000000..7ffd9b5f --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeyCreateParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DeviceKeyProtectionPolicy } from "./DeviceKeyProtectionPolicy"; + +/** + * Create a controller-local device key with a random key id. + */ +export type DeviceKeyCreateParams = { +/** + * Defaults to `hardware_only` when omitted. + */ +protectionPolicy?: DeviceKeyProtectionPolicy | null, accountUserId: string, clientId: string, }; diff --git a/src/generated/app-server/v2/DeviceKeyCreateResponse.ts b/src/generated/app-server/v2/DeviceKeyCreateResponse.ts new file mode 100644 index 00000000..6ace3793 --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeyCreateResponse.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DeviceKeyAlgorithm } from "./DeviceKeyAlgorithm"; +import type { DeviceKeyProtectionClass } from "./DeviceKeyProtectionClass"; + +/** + * Device-key metadata and public key returned by create/public APIs. + */ +export type DeviceKeyCreateResponse = { keyId: string, +/** + * SubjectPublicKeyInfo DER encoded as base64. + */ +publicKeySpkiDerBase64: string, algorithm: DeviceKeyAlgorithm, protectionClass: DeviceKeyProtectionClass, }; diff --git a/src/generated/app-server/v2/DeviceKeyProtectionClass.ts b/src/generated/app-server/v2/DeviceKeyProtectionClass.ts new file mode 100644 index 00000000..ba7ff311 --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeyProtectionClass.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Platform protection class for a controller-local device key. + */ +export type DeviceKeyProtectionClass = "hardware_secure_enclave" | "hardware_tpm" | "os_protected_nonextractable"; diff --git a/src/generated/app-server/v2/DeviceKeyProtectionPolicy.ts b/src/generated/app-server/v2/DeviceKeyProtectionPolicy.ts new file mode 100644 index 00000000..66fceafb --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeyProtectionPolicy.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Protection policy for creating or loading a controller-local device key. + */ +export type DeviceKeyProtectionPolicy = "hardware_only" | "allow_os_protected_nonextractable"; diff --git a/src/generated/app-server/v2/DeviceKeyPublicParams.ts b/src/generated/app-server/v2/DeviceKeyPublicParams.ts new file mode 100644 index 00000000..5a5b7789 --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeyPublicParams.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Fetch a controller-local device key public key by id. + */ +export type DeviceKeyPublicParams = { keyId: string, }; diff --git a/src/generated/app-server/v2/DeviceKeyPublicResponse.ts b/src/generated/app-server/v2/DeviceKeyPublicResponse.ts new file mode 100644 index 00000000..9967c093 --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeyPublicResponse.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DeviceKeyAlgorithm } from "./DeviceKeyAlgorithm"; +import type { DeviceKeyProtectionClass } from "./DeviceKeyProtectionClass"; + +/** + * Device-key public metadata returned by `device/key/public`. + */ +export type DeviceKeyPublicResponse = { keyId: string, +/** + * SubjectPublicKeyInfo DER encoded as base64. + */ +publicKeySpkiDerBase64: string, algorithm: DeviceKeyAlgorithm, protectionClass: DeviceKeyProtectionClass, }; diff --git a/src/generated/app-server/v2/DeviceKeySignParams.ts b/src/generated/app-server/v2/DeviceKeySignParams.ts new file mode 100644 index 00000000..0886e45d --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeySignParams.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DeviceKeySignPayload } from "./DeviceKeySignPayload"; + +/** + * Sign an accepted structured payload with a controller-local device key. + */ +export type DeviceKeySignParams = { keyId: string, payload: DeviceKeySignPayload, }; diff --git a/src/generated/app-server/v2/DeviceKeySignPayload.ts b/src/generated/app-server/v2/DeviceKeySignPayload.ts new file mode 100644 index 00000000..85964454 --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeySignPayload.ts @@ -0,0 +1,54 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RemoteControlClientConnectionAudience } from "./RemoteControlClientConnectionAudience"; +import type { RemoteControlClientEnrollmentAudience } from "./RemoteControlClientEnrollmentAudience"; + +/** + * Structured payloads accepted by `device/key/sign`. + */ +export type DeviceKeySignPayload = { "type": "remoteControlClientConnection", nonce: string, audience: RemoteControlClientConnectionAudience, +/** + * Backend-issued websocket session id that this proof authorizes. + */ +sessionId: string, +/** + * Origin of the backend endpoint that issued the challenge and will verify this proof. + */ +targetOrigin: string, +/** + * Websocket route path that this proof authorizes. + */ +targetPath: string, accountUserId: string, clientId: string, +/** + * Remote-control token expiration as Unix seconds. + */ +tokenExpiresAt: number, +/** + * SHA-256 of the controller-scoped remote-control token, encoded as unpadded base64url. + */ +tokenSha256Base64url: string, +/** + * Must contain exactly `remote_control_controller_websocket`. + */ +scopes: Array, } | { "type": "remoteControlClientEnrollment", nonce: string, audience: RemoteControlClientEnrollmentAudience, +/** + * Backend-issued enrollment challenge id that this proof authorizes. + */ +challengeId: string, +/** + * Origin of the backend endpoint that issued the challenge and will verify this proof. + */ +targetOrigin: string, +/** + * HTTP route path that this proof authorizes. + */ +targetPath: string, accountUserId: string, clientId: string, +/** + * SHA-256 of the requested device identity operation, encoded as unpadded base64url. + */ +deviceIdentitySha256Base64url: string, +/** + * Enrollment challenge expiration as Unix seconds. + */ +challengeExpiresAt: number, }; diff --git a/src/generated/app-server/v2/DeviceKeySignResponse.ts b/src/generated/app-server/v2/DeviceKeySignResponse.ts new file mode 100644 index 00000000..cf77fae2 --- /dev/null +++ b/src/generated/app-server/v2/DeviceKeySignResponse.ts @@ -0,0 +1,18 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DeviceKeyAlgorithm } from "./DeviceKeyAlgorithm"; + +/** + * ASN.1 DER signature returned by `device/key/sign`. + */ +export type DeviceKeySignResponse = { +/** + * ECDSA signature DER encoded as base64. + */ +signatureDerBase64: string, +/** + * Exact bytes signed by the device key, encoded as base64. Verifiers must verify this byte + * string directly and must not reserialize `payload`. + */ +signedPayloadBase64: string, algorithm: DeviceKeyAlgorithm, }; diff --git a/src/generated/app-server/v2/DynamicToolCallOutputContentItem.ts b/src/generated/app-server/v2/DynamicToolCallOutputContentItem.ts new file mode 100644 index 00000000..8f432109 --- /dev/null +++ b/src/generated/app-server/v2/DynamicToolCallOutputContentItem.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type DynamicToolCallOutputContentItem = { "type": "inputText", text: string, } | { "type": "inputImage", imageUrl: string, }; diff --git a/src/generated/app-server/v2/DynamicToolCallParams.ts b/src/generated/app-server/v2/DynamicToolCallParams.ts new file mode 100644 index 00000000..0823ac66 --- /dev/null +++ b/src/generated/app-server/v2/DynamicToolCallParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; + +export type DynamicToolCallParams = { threadId: string, turnId: string, callId: string, namespace: string | null, tool: string, arguments: JsonValue, }; diff --git a/src/generated/app-server/v2/DynamicToolCallResponse.ts b/src/generated/app-server/v2/DynamicToolCallResponse.ts new file mode 100644 index 00000000..788e6242 --- /dev/null +++ b/src/generated/app-server/v2/DynamicToolCallResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DynamicToolCallOutputContentItem } from "./DynamicToolCallOutputContentItem"; + +export type DynamicToolCallResponse = { contentItems: Array, success: boolean, }; diff --git a/src/generated/app-server/v2/DynamicToolCallStatus.ts b/src/generated/app-server/v2/DynamicToolCallStatus.ts new file mode 100644 index 00000000..04f44ec0 --- /dev/null +++ b/src/generated/app-server/v2/DynamicToolCallStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type DynamicToolCallStatus = "inProgress" | "completed" | "failed"; diff --git a/src/generated/app-server/v2/DynamicToolSpec.ts b/src/generated/app-server/v2/DynamicToolSpec.ts new file mode 100644 index 00000000..db486bf9 --- /dev/null +++ b/src/generated/app-server/v2/DynamicToolSpec.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; + +export type DynamicToolSpec = { namespace?: string, name: string, description: string, inputSchema: JsonValue, deferLoading?: boolean, }; diff --git a/src/generated/app-server/v2/ErrorNotification.ts b/src/generated/app-server/v2/ErrorNotification.ts new file mode 100644 index 00000000..c3032883 --- /dev/null +++ b/src/generated/app-server/v2/ErrorNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { TurnError } from "./TurnError"; + +export type ErrorNotification = { error: TurnError, willRetry: boolean, threadId: string, turnId: string, }; diff --git a/src/generated/app-server/v2/ExecPolicyAmendment.ts b/src/generated/app-server/v2/ExecPolicyAmendment.ts new file mode 100644 index 00000000..e893dd44 --- /dev/null +++ b/src/generated/app-server/v2/ExecPolicyAmendment.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExecPolicyAmendment = Array; diff --git a/src/generated/app-server/v2/ExperimentalFeature.ts b/src/generated/app-server/v2/ExperimentalFeature.ts new file mode 100644 index 00000000..2baf7100 --- /dev/null +++ b/src/generated/app-server/v2/ExperimentalFeature.ts @@ -0,0 +1,37 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExperimentalFeatureStage } from "./ExperimentalFeatureStage"; + +export type ExperimentalFeature = { +/** + * Stable key used in config.toml and CLI flag toggles. + */ +name: string, +/** + * Lifecycle stage of this feature flag. + */ +stage: ExperimentalFeatureStage, +/** + * User-facing display name shown in the experimental features UI. + * Null when this feature is not in beta. + */ +displayName: string | null, +/** + * Short summary describing what the feature does. + * Null when this feature is not in beta. + */ +description: string | null, +/** + * Announcement copy shown to users when the feature is introduced. + * Null when this feature is not in beta. + */ +announcement: string | null, +/** + * Whether this feature is currently enabled in the loaded config. + */ +enabled: boolean, +/** + * Whether this feature is enabled by default. + */ +defaultEnabled: boolean, }; diff --git a/src/generated/app-server/v2/ExperimentalFeatureEnablementSetParams.ts b/src/generated/app-server/v2/ExperimentalFeatureEnablementSetParams.ts new file mode 100644 index 00000000..d96955bf --- /dev/null +++ b/src/generated/app-server/v2/ExperimentalFeatureEnablementSetParams.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExperimentalFeatureEnablementSetParams = { +/** + * Process-wide runtime feature enablement keyed by canonical feature name. + * + * Only named features are updated. Omitted features are left unchanged. + * Send an empty map for a no-op. + */ +enablement: { [key in string]?: boolean }, }; diff --git a/src/generated/app-server/v2/ExperimentalFeatureEnablementSetResponse.ts b/src/generated/app-server/v2/ExperimentalFeatureEnablementSetResponse.ts new file mode 100644 index 00000000..d0a8975b --- /dev/null +++ b/src/generated/app-server/v2/ExperimentalFeatureEnablementSetResponse.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExperimentalFeatureEnablementSetResponse = { +/** + * Feature enablement entries updated by this request. + */ +enablement: { [key in string]?: boolean }, }; diff --git a/src/generated/app-server/v2/ExperimentalFeatureListParams.ts b/src/generated/app-server/v2/ExperimentalFeatureListParams.ts new file mode 100644 index 00000000..0a1555d9 --- /dev/null +++ b/src/generated/app-server/v2/ExperimentalFeatureListParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExperimentalFeatureListParams = { +/** + * Opaque pagination cursor returned by a previous call. + */ +cursor?: string | null, +/** + * Optional page size; defaults to a reasonable server-side value. + */ +limit?: number | null, }; diff --git a/src/generated/app-server/v2/ExperimentalFeatureListResponse.ts b/src/generated/app-server/v2/ExperimentalFeatureListResponse.ts new file mode 100644 index 00000000..4d055fa8 --- /dev/null +++ b/src/generated/app-server/v2/ExperimentalFeatureListResponse.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExperimentalFeature } from "./ExperimentalFeature"; + +export type ExperimentalFeatureListResponse = { data: Array, +/** + * Opaque cursor to pass to the next call to continue after the last item. + * If None, there are no more items to return. + */ +nextCursor: string | null, }; diff --git a/src/generated/app-server/v2/ExperimentalFeatureStage.ts b/src/generated/app-server/v2/ExperimentalFeatureStage.ts new file mode 100644 index 00000000..dbd206e0 --- /dev/null +++ b/src/generated/app-server/v2/ExperimentalFeatureStage.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExperimentalFeatureStage = "beta" | "underDevelopment" | "stable" | "deprecated" | "removed"; diff --git a/src/generated/app-server/v2/ExternalAgentConfigDetectParams.ts b/src/generated/app-server/v2/ExternalAgentConfigDetectParams.ts new file mode 100644 index 00000000..163d9619 --- /dev/null +++ b/src/generated/app-server/v2/ExternalAgentConfigDetectParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExternalAgentConfigDetectParams = { +/** + * If true, include detection under the user's home (~/.claude, ~/.codex, etc.). + */ +includeHome?: boolean, +/** + * Zero or more working directories to include for repo-scoped detection. + */ +cwds?: Array | null, }; diff --git a/src/generated/app-server/v2/ExternalAgentConfigDetectResponse.ts b/src/generated/app-server/v2/ExternalAgentConfigDetectResponse.ts new file mode 100644 index 00000000..f220e3b6 --- /dev/null +++ b/src/generated/app-server/v2/ExternalAgentConfigDetectResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExternalAgentConfigMigrationItem } from "./ExternalAgentConfigMigrationItem"; + +export type ExternalAgentConfigDetectResponse = { items: Array, }; diff --git a/src/generated/app-server/v2/ExternalAgentConfigImportCompletedNotification.ts b/src/generated/app-server/v2/ExternalAgentConfigImportCompletedNotification.ts new file mode 100644 index 00000000..edb8f191 --- /dev/null +++ b/src/generated/app-server/v2/ExternalAgentConfigImportCompletedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExternalAgentConfigImportCompletedNotification = Record; diff --git a/src/generated/app-server/v2/ExternalAgentConfigImportParams.ts b/src/generated/app-server/v2/ExternalAgentConfigImportParams.ts new file mode 100644 index 00000000..7bc5d9d9 --- /dev/null +++ b/src/generated/app-server/v2/ExternalAgentConfigImportParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExternalAgentConfigMigrationItem } from "./ExternalAgentConfigMigrationItem"; + +export type ExternalAgentConfigImportParams = { migrationItems: Array, }; diff --git a/src/generated/app-server/v2/ExternalAgentConfigImportResponse.ts b/src/generated/app-server/v2/ExternalAgentConfigImportResponse.ts new file mode 100644 index 00000000..2ceddade --- /dev/null +++ b/src/generated/app-server/v2/ExternalAgentConfigImportResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExternalAgentConfigImportResponse = Record; diff --git a/src/generated/app-server/v2/ExternalAgentConfigMigrationItem.ts b/src/generated/app-server/v2/ExternalAgentConfigMigrationItem.ts new file mode 100644 index 00000000..c9921ccb --- /dev/null +++ b/src/generated/app-server/v2/ExternalAgentConfigMigrationItem.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType"; +import type { MigrationDetails } from "./MigrationDetails"; + +export type ExternalAgentConfigMigrationItem = { itemType: ExternalAgentConfigMigrationItemType, description: string, +/** + * Null or empty means home-scoped migration; non-empty means repo-scoped migration. + */ +cwd: string | null, details: MigrationDetails | null, }; diff --git a/src/generated/app-server/v2/ExternalAgentConfigMigrationItemType.ts b/src/generated/app-server/v2/ExternalAgentConfigMigrationItemType.ts new file mode 100644 index 00000000..d8576937 --- /dev/null +++ b/src/generated/app-server/v2/ExternalAgentConfigMigrationItemType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExternalAgentConfigMigrationItemType = "AGENTS_MD" | "CONFIG" | "SKILLS" | "PLUGINS" | "MCP_SERVER_CONFIG" | "SUBAGENTS" | "HOOKS" | "COMMANDS" | "SESSIONS"; diff --git a/src/generated/app-server/v2/FeedbackUploadParams.ts b/src/generated/app-server/v2/FeedbackUploadParams.ts new file mode 100644 index 00000000..86d9de2f --- /dev/null +++ b/src/generated/app-server/v2/FeedbackUploadParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FeedbackUploadParams = { classification: string, reason?: string | null, threadId?: string | null, includeLogs: boolean, extraLogFiles?: Array | null, tags?: { [key in string]?: string } | null, }; diff --git a/src/generated/app-server/v2/FeedbackUploadResponse.ts b/src/generated/app-server/v2/FeedbackUploadResponse.ts new file mode 100644 index 00000000..f0ad9784 --- /dev/null +++ b/src/generated/app-server/v2/FeedbackUploadResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FeedbackUploadResponse = { threadId: string, }; diff --git a/src/generated/app-server/v2/FileChangeApprovalDecision.ts b/src/generated/app-server/v2/FileChangeApprovalDecision.ts new file mode 100644 index 00000000..b74ba004 --- /dev/null +++ b/src/generated/app-server/v2/FileChangeApprovalDecision.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FileChangeApprovalDecision = "accept" | "acceptForSession" | "decline" | "cancel"; diff --git a/src/generated/app-server/v2/FileChangeOutputDeltaNotification.ts b/src/generated/app-server/v2/FileChangeOutputDeltaNotification.ts new file mode 100644 index 00000000..c11f626c --- /dev/null +++ b/src/generated/app-server/v2/FileChangeOutputDeltaNotification.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Deprecated legacy notification for `apply_patch` textual output. + * + * The server no longer emits this notification. + */ +export type FileChangeOutputDeltaNotification = { threadId: string, turnId: string, itemId: string, delta: string, }; diff --git a/src/generated/app-server/v2/FileChangePatchUpdatedNotification.ts b/src/generated/app-server/v2/FileChangePatchUpdatedNotification.ts new file mode 100644 index 00000000..4a4ed927 --- /dev/null +++ b/src/generated/app-server/v2/FileChangePatchUpdatedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FileUpdateChange } from "./FileUpdateChange"; + +export type FileChangePatchUpdatedNotification = { threadId: string, turnId: string, itemId: string, changes: Array, }; diff --git a/src/generated/app-server/v2/FileChangeRequestApprovalParams.ts b/src/generated/app-server/v2/FileChangeRequestApprovalParams.ts new file mode 100644 index 00000000..2db7be9e --- /dev/null +++ b/src/generated/app-server/v2/FileChangeRequestApprovalParams.ts @@ -0,0 +1,18 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FileChangeRequestApprovalParams = { threadId: string, turnId: string, itemId: string, +/** + * Unix timestamp (in milliseconds) when this approval request started. + */ +startedAtMs: number, +/** + * Optional explanatory reason (e.g. request for extra write access). + */ +reason?: string | null, +/** + * [UNSTABLE] When set, the agent is asking the user to allow writes under this root + * for the remainder of the session (unclear if this is honored today). + */ +grantRoot?: string | null, }; diff --git a/src/generated/app-server/v2/FileChangeRequestApprovalResponse.ts b/src/generated/app-server/v2/FileChangeRequestApprovalResponse.ts new file mode 100644 index 00000000..6f5de6e9 --- /dev/null +++ b/src/generated/app-server/v2/FileChangeRequestApprovalResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FileChangeApprovalDecision } from "./FileChangeApprovalDecision"; + +export type FileChangeRequestApprovalResponse = { decision: FileChangeApprovalDecision, }; diff --git a/src/generated/app-server/v2/FileSystemAccessMode.ts b/src/generated/app-server/v2/FileSystemAccessMode.ts new file mode 100644 index 00000000..b1d801fe --- /dev/null +++ b/src/generated/app-server/v2/FileSystemAccessMode.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FileSystemAccessMode = "read" | "write" | "none"; diff --git a/src/generated/app-server/v2/FileSystemPath.ts b/src/generated/app-server/v2/FileSystemPath.ts new file mode 100644 index 00000000..2efc7eab --- /dev/null +++ b/src/generated/app-server/v2/FileSystemPath.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { FileSystemSpecialPath } from "./FileSystemSpecialPath"; + +export type FileSystemPath = { "type": "path", path: AbsolutePathBuf, } | { "type": "glob_pattern", pattern: string, } | { "type": "special", value: FileSystemSpecialPath, }; diff --git a/src/generated/app-server/v2/FileSystemSandboxEntry.ts b/src/generated/app-server/v2/FileSystemSandboxEntry.ts new file mode 100644 index 00000000..f37cd0d6 --- /dev/null +++ b/src/generated/app-server/v2/FileSystemSandboxEntry.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FileSystemAccessMode } from "./FileSystemAccessMode"; +import type { FileSystemPath } from "./FileSystemPath"; + +export type FileSystemSandboxEntry = { path: FileSystemPath, access: FileSystemAccessMode, }; diff --git a/src/generated/app-server/v2/FileSystemSpecialPath.ts b/src/generated/app-server/v2/FileSystemSpecialPath.ts new file mode 100644 index 00000000..f4dc2b01 --- /dev/null +++ b/src/generated/app-server/v2/FileSystemSpecialPath.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type FileSystemSpecialPath = { "kind": "root" } | { "kind": "minimal" } | { "kind": "project_roots", subpath: string | null, } | { "kind": "tmpdir" } | { "kind": "slash_tmp" } | { "kind": "unknown", path: string, subpath: string | null, }; diff --git a/src/generated/app-server/v2/FileUpdateChange.ts b/src/generated/app-server/v2/FileUpdateChange.ts new file mode 100644 index 00000000..c724db2b --- /dev/null +++ b/src/generated/app-server/v2/FileUpdateChange.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PatchChangeKind } from "./PatchChangeKind"; + +export type FileUpdateChange = { path: string, kind: PatchChangeKind, diff: string, }; diff --git a/src/generated/app-server/v2/FsChangedNotification.ts b/src/generated/app-server/v2/FsChangedNotification.ts new file mode 100644 index 00000000..3f3be8ff --- /dev/null +++ b/src/generated/app-server/v2/FsChangedNotification.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Filesystem watch notification emitted for `fs/watch` subscribers. + */ +export type FsChangedNotification = { +/** + * Watch identifier previously provided to `fs/watch`. + */ +watchId: string, +/** + * File or directory paths associated with this event. + */ +changedPaths: Array, }; diff --git a/src/generated/app-server/v2/FsCopyParams.ts b/src/generated/app-server/v2/FsCopyParams.ts new file mode 100644 index 00000000..d19aca92 --- /dev/null +++ b/src/generated/app-server/v2/FsCopyParams.ts @@ -0,0 +1,21 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Copy a file or directory tree on the host filesystem. + */ +export type FsCopyParams = { +/** + * Absolute source path. + */ +sourcePath: AbsolutePathBuf, +/** + * Absolute destination path. + */ +destinationPath: AbsolutePathBuf, +/** + * Required for directory copies; ignored for file copies. + */ +recursive?: boolean, }; diff --git a/src/generated/app-server/v2/FsCopyResponse.ts b/src/generated/app-server/v2/FsCopyResponse.ts new file mode 100644 index 00000000..3e3061a8 --- /dev/null +++ b/src/generated/app-server/v2/FsCopyResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `fs/copy`. + */ +export type FsCopyResponse = Record; diff --git a/src/generated/app-server/v2/FsCreateDirectoryParams.ts b/src/generated/app-server/v2/FsCreateDirectoryParams.ts new file mode 100644 index 00000000..b648d350 --- /dev/null +++ b/src/generated/app-server/v2/FsCreateDirectoryParams.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Create a directory on the host filesystem. + */ +export type FsCreateDirectoryParams = { +/** + * Absolute directory path to create. + */ +path: AbsolutePathBuf, +/** + * Whether parent directories should also be created. Defaults to `true`. + */ +recursive?: boolean | null, }; diff --git a/src/generated/app-server/v2/FsCreateDirectoryResponse.ts b/src/generated/app-server/v2/FsCreateDirectoryResponse.ts new file mode 100644 index 00000000..5d251b71 --- /dev/null +++ b/src/generated/app-server/v2/FsCreateDirectoryResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `fs/createDirectory`. + */ +export type FsCreateDirectoryResponse = Record; diff --git a/src/generated/app-server/v2/FsGetMetadataParams.ts b/src/generated/app-server/v2/FsGetMetadataParams.ts new file mode 100644 index 00000000..4ea0445c --- /dev/null +++ b/src/generated/app-server/v2/FsGetMetadataParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Request metadata for an absolute path. + */ +export type FsGetMetadataParams = { +/** + * Absolute path to inspect. + */ +path: AbsolutePathBuf, }; diff --git a/src/generated/app-server/v2/FsGetMetadataResponse.ts b/src/generated/app-server/v2/FsGetMetadataResponse.ts new file mode 100644 index 00000000..a1a127e1 --- /dev/null +++ b/src/generated/app-server/v2/FsGetMetadataResponse.ts @@ -0,0 +1,28 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Metadata returned by `fs/getMetadata`. + */ +export type FsGetMetadataResponse = { +/** + * Whether the path resolves to a directory. + */ +isDirectory: boolean, +/** + * Whether the path resolves to a regular file. + */ +isFile: boolean, +/** + * Whether the path itself is a symbolic link. + */ +isSymlink: boolean, +/** + * File creation time in Unix milliseconds when available, otherwise `0`. + */ +createdAtMs: number, +/** + * File modification time in Unix milliseconds when available, otherwise `0`. + */ +modifiedAtMs: number, }; diff --git a/src/generated/app-server/v2/FsReadDirectoryEntry.ts b/src/generated/app-server/v2/FsReadDirectoryEntry.ts new file mode 100644 index 00000000..197673d2 --- /dev/null +++ b/src/generated/app-server/v2/FsReadDirectoryEntry.ts @@ -0,0 +1,20 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * A directory entry returned by `fs/readDirectory`. + */ +export type FsReadDirectoryEntry = { +/** + * Direct child entry name only, not an absolute or relative path. + */ +fileName: string, +/** + * Whether this entry resolves to a directory. + */ +isDirectory: boolean, +/** + * Whether this entry resolves to a regular file. + */ +isFile: boolean, }; diff --git a/src/generated/app-server/v2/FsReadDirectoryParams.ts b/src/generated/app-server/v2/FsReadDirectoryParams.ts new file mode 100644 index 00000000..94eaae43 --- /dev/null +++ b/src/generated/app-server/v2/FsReadDirectoryParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * List direct child names for a directory. + */ +export type FsReadDirectoryParams = { +/** + * Absolute directory path to read. + */ +path: AbsolutePathBuf, }; diff --git a/src/generated/app-server/v2/FsReadDirectoryResponse.ts b/src/generated/app-server/v2/FsReadDirectoryResponse.ts new file mode 100644 index 00000000..0ffb8acd --- /dev/null +++ b/src/generated/app-server/v2/FsReadDirectoryResponse.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FsReadDirectoryEntry } from "./FsReadDirectoryEntry"; + +/** + * Directory entries returned by `fs/readDirectory`. + */ +export type FsReadDirectoryResponse = { +/** + * Direct child entries in the requested directory. + */ +entries: Array, }; diff --git a/src/generated/app-server/v2/FsReadFileParams.ts b/src/generated/app-server/v2/FsReadFileParams.ts new file mode 100644 index 00000000..d5bf22e3 --- /dev/null +++ b/src/generated/app-server/v2/FsReadFileParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Read a file from the host filesystem. + */ +export type FsReadFileParams = { +/** + * Absolute path to read. + */ +path: AbsolutePathBuf, }; diff --git a/src/generated/app-server/v2/FsReadFileResponse.ts b/src/generated/app-server/v2/FsReadFileResponse.ts new file mode 100644 index 00000000..26b61269 --- /dev/null +++ b/src/generated/app-server/v2/FsReadFileResponse.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Base64-encoded file contents returned by `fs/readFile`. + */ +export type FsReadFileResponse = { +/** + * File contents encoded as base64. + */ +dataBase64: string, }; diff --git a/src/generated/app-server/v2/FsRemoveParams.ts b/src/generated/app-server/v2/FsRemoveParams.ts new file mode 100644 index 00000000..c95b860a --- /dev/null +++ b/src/generated/app-server/v2/FsRemoveParams.ts @@ -0,0 +1,21 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Remove a file or directory tree from the host filesystem. + */ +export type FsRemoveParams = { +/** + * Absolute path to remove. + */ +path: AbsolutePathBuf, +/** + * Whether directory removal should recurse. Defaults to `true`. + */ +recursive?: boolean | null, +/** + * Whether missing paths should be ignored. Defaults to `true`. + */ +force?: boolean | null, }; diff --git a/src/generated/app-server/v2/FsRemoveResponse.ts b/src/generated/app-server/v2/FsRemoveResponse.ts new file mode 100644 index 00000000..981c28fa --- /dev/null +++ b/src/generated/app-server/v2/FsRemoveResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `fs/remove`. + */ +export type FsRemoveResponse = Record; diff --git a/src/generated/app-server/v2/FsUnwatchParams.ts b/src/generated/app-server/v2/FsUnwatchParams.ts new file mode 100644 index 00000000..ff314814 --- /dev/null +++ b/src/generated/app-server/v2/FsUnwatchParams.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Stop filesystem watch notifications for a prior `fs/watch`. + */ +export type FsUnwatchParams = { +/** + * Watch identifier previously provided to `fs/watch`. + */ +watchId: string, }; diff --git a/src/generated/app-server/v2/FsUnwatchResponse.ts b/src/generated/app-server/v2/FsUnwatchResponse.ts new file mode 100644 index 00000000..02507d2c --- /dev/null +++ b/src/generated/app-server/v2/FsUnwatchResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `fs/unwatch`. + */ +export type FsUnwatchResponse = Record; diff --git a/src/generated/app-server/v2/FsWatchParams.ts b/src/generated/app-server/v2/FsWatchParams.ts new file mode 100644 index 00000000..b990b8e0 --- /dev/null +++ b/src/generated/app-server/v2/FsWatchParams.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Start filesystem watch notifications for an absolute path. + */ +export type FsWatchParams = { +/** + * Connection-scoped watch identifier used for `fs/unwatch` and `fs/changed`. + */ +watchId: string, +/** + * Absolute file or directory path to watch. + */ +path: AbsolutePathBuf, }; diff --git a/src/generated/app-server/v2/FsWatchResponse.ts b/src/generated/app-server/v2/FsWatchResponse.ts new file mode 100644 index 00000000..82e6c7e9 --- /dev/null +++ b/src/generated/app-server/v2/FsWatchResponse.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Successful response for `fs/watch`. + */ +export type FsWatchResponse = { +/** + * Canonicalized path associated with the watch. + */ +path: AbsolutePathBuf, }; diff --git a/src/generated/app-server/v2/FsWriteFileParams.ts b/src/generated/app-server/v2/FsWriteFileParams.ts new file mode 100644 index 00000000..1e8672b5 --- /dev/null +++ b/src/generated/app-server/v2/FsWriteFileParams.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Write a file on the host filesystem. + */ +export type FsWriteFileParams = { +/** + * Absolute path to write. + */ +path: AbsolutePathBuf, +/** + * File contents encoded as base64. + */ +dataBase64: string, }; diff --git a/src/generated/app-server/v2/FsWriteFileResponse.ts b/src/generated/app-server/v2/FsWriteFileResponse.ts new file mode 100644 index 00000000..ad0ce283 --- /dev/null +++ b/src/generated/app-server/v2/FsWriteFileResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `fs/writeFile`. + */ +export type FsWriteFileResponse = Record; diff --git a/src/generated/app-server/v2/GetAccountParams.ts b/src/generated/app-server/v2/GetAccountParams.ts new file mode 100644 index 00000000..a5c5c25f --- /dev/null +++ b/src/generated/app-server/v2/GetAccountParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type GetAccountParams = { +/** + * When `true`, requests a proactive token refresh before returning. + * + * In managed auth mode this triggers the normal refresh-token flow. In + * external auth mode this flag is ignored. Clients should refresh tokens + * themselves and call `account/login/start` with `chatgptAuthTokens`. + */ +refreshToken: boolean, }; diff --git a/src/generated/app-server/v2/GetAccountRateLimitsResponse.ts b/src/generated/app-server/v2/GetAccountRateLimitsResponse.ts new file mode 100644 index 00000000..02cc7779 --- /dev/null +++ b/src/generated/app-server/v2/GetAccountRateLimitsResponse.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RateLimitSnapshot } from "./RateLimitSnapshot"; + +export type GetAccountRateLimitsResponse = { +/** + * Backward-compatible single-bucket view; mirrors the historical payload. + */ +rateLimits: RateLimitSnapshot, +/** + * Multi-bucket view keyed by metered `limit_id` (for example, `codex`). + */ +rateLimitsByLimitId: { [key in string]?: RateLimitSnapshot } | null, }; diff --git a/src/generated/app-server/v2/GetAccountResponse.ts b/src/generated/app-server/v2/GetAccountResponse.ts new file mode 100644 index 00000000..83da4f4e --- /dev/null +++ b/src/generated/app-server/v2/GetAccountResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Account } from "./Account"; + +export type GetAccountResponse = { account: Account | null, requiresOpenaiAuth: boolean, }; diff --git a/src/generated/app-server/v2/GitInfo.ts b/src/generated/app-server/v2/GitInfo.ts new file mode 100644 index 00000000..9559272a --- /dev/null +++ b/src/generated/app-server/v2/GitInfo.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type GitInfo = { sha: string | null, branch: string | null, originUrl: string | null, }; diff --git a/src/generated/app-server/v2/GrantedPermissionProfile.ts b/src/generated/app-server/v2/GrantedPermissionProfile.ts new file mode 100644 index 00000000..3ae6c605 --- /dev/null +++ b/src/generated/app-server/v2/GrantedPermissionProfile.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AdditionalFileSystemPermissions } from "./AdditionalFileSystemPermissions"; +import type { AdditionalNetworkPermissions } from "./AdditionalNetworkPermissions"; + +export type GrantedPermissionProfile = { network?: AdditionalNetworkPermissions, fileSystem?: AdditionalFileSystemPermissions, }; diff --git a/src/generated/app-server/v2/GuardianApprovalReview.ts b/src/generated/app-server/v2/GuardianApprovalReview.ts new file mode 100644 index 00000000..11d797eb --- /dev/null +++ b/src/generated/app-server/v2/GuardianApprovalReview.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { GuardianApprovalReviewStatus } from "./GuardianApprovalReviewStatus"; +import type { GuardianRiskLevel } from "./GuardianRiskLevel"; +import type { GuardianUserAuthorization } from "./GuardianUserAuthorization"; + +/** + * [UNSTABLE] Temporary approval auto-review payload used by + * `item/autoApprovalReview/*` notifications. This shape is expected to change + * soon. + */ +export type GuardianApprovalReview = { status: GuardianApprovalReviewStatus, riskLevel: GuardianRiskLevel | null, userAuthorization: GuardianUserAuthorization | null, rationale: string | null, }; diff --git a/src/generated/app-server/v2/GuardianApprovalReviewAction.ts b/src/generated/app-server/v2/GuardianApprovalReviewAction.ts new file mode 100644 index 00000000..4f00e37d --- /dev/null +++ b/src/generated/app-server/v2/GuardianApprovalReviewAction.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { GuardianCommandSource } from "./GuardianCommandSource"; +import type { NetworkApprovalProtocol } from "./NetworkApprovalProtocol"; +import type { RequestPermissionProfile } from "./RequestPermissionProfile"; + +export type GuardianApprovalReviewAction = { "type": "command", source: GuardianCommandSource, command: string, cwd: AbsolutePathBuf, } | { "type": "execve", source: GuardianCommandSource, program: string, argv: Array, cwd: AbsolutePathBuf, } | { "type": "applyPatch", cwd: AbsolutePathBuf, files: Array, } | { "type": "networkAccess", target: string, host: string, protocol: NetworkApprovalProtocol, port: number, } | { "type": "mcpToolCall", server: string, toolName: string, connectorId: string | null, connectorName: string | null, toolTitle: string | null, } | { "type": "requestPermissions", reason: string | null, permissions: RequestPermissionProfile, }; diff --git a/src/generated/app-server/v2/GuardianApprovalReviewStatus.ts b/src/generated/app-server/v2/GuardianApprovalReviewStatus.ts new file mode 100644 index 00000000..ae892572 --- /dev/null +++ b/src/generated/app-server/v2/GuardianApprovalReviewStatus.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * [UNSTABLE] Lifecycle state for an approval auto-review. + */ +export type GuardianApprovalReviewStatus = "inProgress" | "approved" | "denied" | "timedOut" | "aborted"; diff --git a/src/generated/app-server/v2/GuardianCommandSource.ts b/src/generated/app-server/v2/GuardianCommandSource.ts new file mode 100644 index 00000000..b48e9b08 --- /dev/null +++ b/src/generated/app-server/v2/GuardianCommandSource.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type GuardianCommandSource = "shell" | "unifiedExec"; diff --git a/src/generated/app-server/v2/GuardianRiskLevel.ts b/src/generated/app-server/v2/GuardianRiskLevel.ts new file mode 100644 index 00000000..7734016a --- /dev/null +++ b/src/generated/app-server/v2/GuardianRiskLevel.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * [UNSTABLE] Risk level assigned by approval auto-review. + */ +export type GuardianRiskLevel = "low" | "medium" | "high" | "critical"; diff --git a/src/generated/app-server/v2/GuardianUserAuthorization.ts b/src/generated/app-server/v2/GuardianUserAuthorization.ts new file mode 100644 index 00000000..936611f7 --- /dev/null +++ b/src/generated/app-server/v2/GuardianUserAuthorization.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * [UNSTABLE] Authorization level assigned by approval auto-review. + */ +export type GuardianUserAuthorization = "unknown" | "low" | "medium" | "high"; diff --git a/src/generated/app-server/v2/GuardianWarningNotification.ts b/src/generated/app-server/v2/GuardianWarningNotification.ts new file mode 100644 index 00000000..1659f62f --- /dev/null +++ b/src/generated/app-server/v2/GuardianWarningNotification.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type GuardianWarningNotification = { +/** + * Thread target for the guardian warning. + */ +threadId: string, +/** + * Concise guardian warning message for the user. + */ +message: string, }; diff --git a/src/generated/app-server/v2/HookCompletedNotification.ts b/src/generated/app-server/v2/HookCompletedNotification.ts new file mode 100644 index 00000000..fe4dbfb5 --- /dev/null +++ b/src/generated/app-server/v2/HookCompletedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { HookRunSummary } from "./HookRunSummary"; + +export type HookCompletedNotification = { threadId: string, turnId: string | null, run: HookRunSummary, }; diff --git a/src/generated/app-server/v2/HookErrorInfo.ts b/src/generated/app-server/v2/HookErrorInfo.ts new file mode 100644 index 00000000..75c259b0 --- /dev/null +++ b/src/generated/app-server/v2/HookErrorInfo.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookErrorInfo = { path: string, message: string, }; diff --git a/src/generated/app-server/v2/HookEventName.ts b/src/generated/app-server/v2/HookEventName.ts new file mode 100644 index 00000000..91c2def7 --- /dev/null +++ b/src/generated/app-server/v2/HookEventName.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookEventName = "preToolUse" | "permissionRequest" | "postToolUse" | "preCompact" | "postCompact" | "sessionStart" | "userPromptSubmit" | "stop"; diff --git a/src/generated/app-server/v2/HookExecutionMode.ts b/src/generated/app-server/v2/HookExecutionMode.ts new file mode 100644 index 00000000..61f98564 --- /dev/null +++ b/src/generated/app-server/v2/HookExecutionMode.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookExecutionMode = "sync" | "async"; diff --git a/src/generated/app-server/v2/HookHandlerType.ts b/src/generated/app-server/v2/HookHandlerType.ts new file mode 100644 index 00000000..dc3f087b --- /dev/null +++ b/src/generated/app-server/v2/HookHandlerType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookHandlerType = "command" | "prompt" | "agent"; diff --git a/src/generated/app-server/v2/HookMetadata.ts b/src/generated/app-server/v2/HookMetadata.ts new file mode 100644 index 00000000..94e3c30c --- /dev/null +++ b/src/generated/app-server/v2/HookMetadata.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { HookEventName } from "./HookEventName"; +import type { HookHandlerType } from "./HookHandlerType"; +import type { HookSource } from "./HookSource"; +import type { HookTrustStatus } from "./HookTrustStatus"; + +export type HookMetadata = { key: string, eventName: HookEventName, handlerType: HookHandlerType, matcher: string | null, command: string | null, timeoutSec: bigint, statusMessage: string | null, sourcePath: AbsolutePathBuf, source: HookSource, pluginId: string | null, displayOrder: bigint, enabled: boolean, isManaged: boolean, currentHash: string, trustStatus: HookTrustStatus, }; diff --git a/src/generated/app-server/v2/HookMigration.ts b/src/generated/app-server/v2/HookMigration.ts new file mode 100644 index 00000000..92ec2d3d --- /dev/null +++ b/src/generated/app-server/v2/HookMigration.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookMigration = { name: string, }; diff --git a/src/generated/app-server/v2/HookOutputEntry.ts b/src/generated/app-server/v2/HookOutputEntry.ts new file mode 100644 index 00000000..834f0c4e --- /dev/null +++ b/src/generated/app-server/v2/HookOutputEntry.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { HookOutputEntryKind } from "./HookOutputEntryKind"; + +export type HookOutputEntry = { kind: HookOutputEntryKind, text: string, }; diff --git a/src/generated/app-server/v2/HookOutputEntryKind.ts b/src/generated/app-server/v2/HookOutputEntryKind.ts new file mode 100644 index 00000000..090dfe38 --- /dev/null +++ b/src/generated/app-server/v2/HookOutputEntryKind.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookOutputEntryKind = "warning" | "stop" | "feedback" | "context" | "error"; diff --git a/src/generated/app-server/v2/HookPromptFragment.ts b/src/generated/app-server/v2/HookPromptFragment.ts new file mode 100644 index 00000000..2c6b18ac --- /dev/null +++ b/src/generated/app-server/v2/HookPromptFragment.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookPromptFragment = { text: string, hookRunId: string, }; diff --git a/src/generated/app-server/v2/HookRunStatus.ts b/src/generated/app-server/v2/HookRunStatus.ts new file mode 100644 index 00000000..ffca7e0e --- /dev/null +++ b/src/generated/app-server/v2/HookRunStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookRunStatus = "running" | "completed" | "failed" | "blocked" | "stopped"; diff --git a/src/generated/app-server/v2/HookRunSummary.ts b/src/generated/app-server/v2/HookRunSummary.ts new file mode 100644 index 00000000..75ab780b --- /dev/null +++ b/src/generated/app-server/v2/HookRunSummary.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { HookEventName } from "./HookEventName"; +import type { HookExecutionMode } from "./HookExecutionMode"; +import type { HookHandlerType } from "./HookHandlerType"; +import type { HookOutputEntry } from "./HookOutputEntry"; +import type { HookRunStatus } from "./HookRunStatus"; +import type { HookScope } from "./HookScope"; +import type { HookSource } from "./HookSource"; + +export type HookRunSummary = { id: string, eventName: HookEventName, handlerType: HookHandlerType, executionMode: HookExecutionMode, scope: HookScope, sourcePath: AbsolutePathBuf, source: HookSource, displayOrder: bigint, status: HookRunStatus, statusMessage: string | null, startedAt: bigint, completedAt: bigint | null, durationMs: bigint | null, entries: Array, }; diff --git a/src/generated/app-server/v2/HookScope.ts b/src/generated/app-server/v2/HookScope.ts new file mode 100644 index 00000000..ff6f8bfe --- /dev/null +++ b/src/generated/app-server/v2/HookScope.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookScope = "thread" | "turn"; diff --git a/src/generated/app-server/v2/HookSource.ts b/src/generated/app-server/v2/HookSource.ts new file mode 100644 index 00000000..98bbe1e4 --- /dev/null +++ b/src/generated/app-server/v2/HookSource.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookSource = "system" | "user" | "project" | "mdm" | "sessionFlags" | "plugin" | "cloudRequirements" | "legacyManagedConfigFile" | "legacyManagedConfigMdm" | "unknown"; diff --git a/src/generated/app-server/v2/HookStartedNotification.ts b/src/generated/app-server/v2/HookStartedNotification.ts new file mode 100644 index 00000000..1f781ed6 --- /dev/null +++ b/src/generated/app-server/v2/HookStartedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { HookRunSummary } from "./HookRunSummary"; + +export type HookStartedNotification = { threadId: string, turnId: string | null, run: HookRunSummary, }; diff --git a/src/generated/app-server/v2/HookTrustStatus.ts b/src/generated/app-server/v2/HookTrustStatus.ts new file mode 100644 index 00000000..692fdc4c --- /dev/null +++ b/src/generated/app-server/v2/HookTrustStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HookTrustStatus = "managed" | "untrusted" | "trusted" | "modified"; diff --git a/src/generated/app-server/v2/HooksListEntry.ts b/src/generated/app-server/v2/HooksListEntry.ts new file mode 100644 index 00000000..256b29bb --- /dev/null +++ b/src/generated/app-server/v2/HooksListEntry.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { HookErrorInfo } from "./HookErrorInfo"; +import type { HookMetadata } from "./HookMetadata"; + +export type HooksListEntry = { cwd: string, hooks: Array, warnings: Array, errors: Array, }; diff --git a/src/generated/app-server/v2/HooksListParams.ts b/src/generated/app-server/v2/HooksListParams.ts new file mode 100644 index 00000000..db29387d --- /dev/null +++ b/src/generated/app-server/v2/HooksListParams.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type HooksListParams = { +/** + * When empty, defaults to the current session working directory. + */ +cwds?: Array, }; diff --git a/src/generated/app-server/v2/HooksListResponse.ts b/src/generated/app-server/v2/HooksListResponse.ts new file mode 100644 index 00000000..4c2dd1a8 --- /dev/null +++ b/src/generated/app-server/v2/HooksListResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { HooksListEntry } from "./HooksListEntry"; + +export type HooksListResponse = { data: Array, }; diff --git a/src/generated/app-server/v2/ItemCompletedNotification.ts b/src/generated/app-server/v2/ItemCompletedNotification.ts new file mode 100644 index 00000000..25ced4a0 --- /dev/null +++ b/src/generated/app-server/v2/ItemCompletedNotification.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadItem } from "./ThreadItem"; + +export type ItemCompletedNotification = { item: ThreadItem, threadId: string, turnId: string, +/** + * Unix timestamp (in milliseconds) when this item lifecycle completed. + */ +completedAtMs: number, }; diff --git a/src/generated/app-server/v2/ItemGuardianApprovalReviewCompletedNotification.ts b/src/generated/app-server/v2/ItemGuardianApprovalReviewCompletedNotification.ts new file mode 100644 index 00000000..32d12be6 --- /dev/null +++ b/src/generated/app-server/v2/ItemGuardianApprovalReviewCompletedNotification.ts @@ -0,0 +1,38 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AutoReviewDecisionSource } from "./AutoReviewDecisionSource"; +import type { GuardianApprovalReview } from "./GuardianApprovalReview"; +import type { GuardianApprovalReviewAction } from "./GuardianApprovalReviewAction"; + +/** + * [UNSTABLE] Temporary notification payload for approval auto-review. This + * shape is expected to change soon. + */ +export type ItemGuardianApprovalReviewCompletedNotification = { threadId: string, turnId: string, +/** + * Unix timestamp (in milliseconds) when this review started. + */ +startedAtMs: number, +/** + * Unix timestamp (in milliseconds) when this review completed. + */ +completedAtMs: number, +/** + * Stable identifier for this review. + */ +reviewId: string, +/** + * Identifier for the reviewed item or tool call when one exists. + * + * In most cases, one review maps to one target item. The exceptions are + * - execve reviews, where a single command may contain multiple execve + * calls to review (only possible when using the shell_zsh_fork feature) + * - network policy reviews, where there is no target item + * + * A network call is triggered by a CommandExecution item, so having a + * target_item_id set to the CommandExecution item would be misleading + * because the review is about the network call, not the command execution. + * Therefore, target_item_id is set to None for network policy reviews. + */ +targetItemId: string | null, decisionSource: AutoReviewDecisionSource, review: GuardianApprovalReview, action: GuardianApprovalReviewAction, }; diff --git a/src/generated/app-server/v2/ItemGuardianApprovalReviewStartedNotification.ts b/src/generated/app-server/v2/ItemGuardianApprovalReviewStartedNotification.ts new file mode 100644 index 00000000..92d34fde --- /dev/null +++ b/src/generated/app-server/v2/ItemGuardianApprovalReviewStartedNotification.ts @@ -0,0 +1,33 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { GuardianApprovalReview } from "./GuardianApprovalReview"; +import type { GuardianApprovalReviewAction } from "./GuardianApprovalReviewAction"; + +/** + * [UNSTABLE] Temporary notification payload for approval auto-review. This + * shape is expected to change soon. + */ +export type ItemGuardianApprovalReviewStartedNotification = { threadId: string, turnId: string, +/** + * Unix timestamp (in milliseconds) when this review started. + */ +startedAtMs: number, +/** + * Stable identifier for this review. + */ +reviewId: string, +/** + * Identifier for the reviewed item or tool call when one exists. + * + * In most cases, one review maps to one target item. The exceptions are + * - execve reviews, where a single command may contain multiple execve + * calls to review (only possible when using the shell_zsh_fork feature) + * - network policy reviews, where there is no target item + * + * A network call is triggered by a CommandExecution item, so having a + * target_item_id set to the CommandExecution item would be misleading + * because the review is about the network call, not the command execution. + * Therefore, target_item_id is set to None for network policy reviews. + */ +targetItemId: string | null, review: GuardianApprovalReview, action: GuardianApprovalReviewAction, }; diff --git a/src/generated/app-server/v2/ItemStartedNotification.ts b/src/generated/app-server/v2/ItemStartedNotification.ts new file mode 100644 index 00000000..9ec8af09 --- /dev/null +++ b/src/generated/app-server/v2/ItemStartedNotification.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadItem } from "./ThreadItem"; + +export type ItemStartedNotification = { item: ThreadItem, threadId: string, turnId: string, +/** + * Unix timestamp (in milliseconds) when this item lifecycle started. + */ +startedAtMs: number, }; diff --git a/src/generated/app-server/v2/ListMcpServerStatusParams.ts b/src/generated/app-server/v2/ListMcpServerStatusParams.ts new file mode 100644 index 00000000..fa00b8ea --- /dev/null +++ b/src/generated/app-server/v2/ListMcpServerStatusParams.ts @@ -0,0 +1,19 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpServerStatusDetail } from "./McpServerStatusDetail"; + +export type ListMcpServerStatusParams = { +/** + * Opaque pagination cursor returned by a previous call. + */ +cursor?: string | null, +/** + * Optional page size; defaults to a server-defined value. + */ +limit?: number | null, +/** + * Controls how much MCP inventory data to fetch for each server. + * Defaults to `Full` when omitted. + */ +detail?: McpServerStatusDetail | null, }; diff --git a/src/generated/app-server/v2/ListMcpServerStatusResponse.ts b/src/generated/app-server/v2/ListMcpServerStatusResponse.ts new file mode 100644 index 00000000..18696ed8 --- /dev/null +++ b/src/generated/app-server/v2/ListMcpServerStatusResponse.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpServerStatus } from "./McpServerStatus"; + +export type ListMcpServerStatusResponse = { data: Array, +/** + * Opaque cursor to pass to the next call to continue after the last item. + * If None, there are no more items to return. + */ +nextCursor: string | null, }; diff --git a/src/generated/app-server/v2/LoginAccountParams.ts b/src/generated/app-server/v2/LoginAccountParams.ts new file mode 100644 index 00000000..e6f1e2ed --- /dev/null +++ b/src/generated/app-server/v2/LoginAccountParams.ts @@ -0,0 +1,21 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type LoginAccountParams = { "type": "apiKey", apiKey: string, } | { "type": "chatgpt", codexStreamlinedLogin?: boolean, } | { "type": "chatgptDeviceCode" } | { "type": "chatgptAuthTokens", +/** + * Access token (JWT) supplied by the client. + * This token is used for backend API requests and email extraction. + */ +accessToken: string, +/** + * Workspace/account identifier supplied by the client. + */ +chatgptAccountId: string, +/** + * Optional plan type supplied by the client. + * + * When `null`, Codex attempts to derive the plan type from access-token + * claims. If unavailable, the plan defaults to `unknown`. + */ +chatgptPlanType?: string | null, }; diff --git a/src/generated/app-server/v2/LoginAccountResponse.ts b/src/generated/app-server/v2/LoginAccountResponse.ts new file mode 100644 index 00000000..34bccd65 --- /dev/null +++ b/src/generated/app-server/v2/LoginAccountResponse.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type LoginAccountResponse = { "type": "apiKey", } | { "type": "chatgpt", loginId: string, +/** + * URL the client should open in a browser to initiate the OAuth flow. + */ +authUrl: string, } | { "type": "chatgptDeviceCode", loginId: string, +/** + * URL the client should open in a browser to complete device code authorization. + */ +verificationUrl: string, +/** + * One-time code the user must enter after signing in. + */ +userCode: string, } | { "type": "chatgptAuthTokens", }; diff --git a/src/generated/app-server/v2/LogoutAccountResponse.ts b/src/generated/app-server/v2/LogoutAccountResponse.ts new file mode 100644 index 00000000..ec85cf0f --- /dev/null +++ b/src/generated/app-server/v2/LogoutAccountResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type LogoutAccountResponse = Record; diff --git a/src/generated/app-server/v2/ManagedHooksRequirements.ts b/src/generated/app-server/v2/ManagedHooksRequirements.ts new file mode 100644 index 00000000..cde0e4a5 --- /dev/null +++ b/src/generated/app-server/v2/ManagedHooksRequirements.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ConfiguredHookMatcherGroup } from "./ConfiguredHookMatcherGroup"; + +export type ManagedHooksRequirements = { managedDir: string | null, windowsManagedDir: string | null, PreToolUse: Array, PermissionRequest: Array, PostToolUse: Array, PreCompact: Array, PostCompact: Array, SessionStart: Array, UserPromptSubmit: Array, Stop: Array, }; diff --git a/src/generated/app-server/v2/MarketplaceAddParams.ts b/src/generated/app-server/v2/MarketplaceAddParams.ts new file mode 100644 index 00000000..23d16048 --- /dev/null +++ b/src/generated/app-server/v2/MarketplaceAddParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MarketplaceAddParams = { source: string, refName?: string | null, sparsePaths?: Array | null, }; diff --git a/src/generated/app-server/v2/MarketplaceAddResponse.ts b/src/generated/app-server/v2/MarketplaceAddResponse.ts new file mode 100644 index 00000000..8657d44c --- /dev/null +++ b/src/generated/app-server/v2/MarketplaceAddResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type MarketplaceAddResponse = { marketplaceName: string, installedRoot: AbsolutePathBuf, alreadyAdded: boolean, }; diff --git a/src/generated/app-server/v2/MarketplaceInterface.ts b/src/generated/app-server/v2/MarketplaceInterface.ts new file mode 100644 index 00000000..f82dc179 --- /dev/null +++ b/src/generated/app-server/v2/MarketplaceInterface.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MarketplaceInterface = { displayName: string | null, }; diff --git a/src/generated/app-server/v2/MarketplaceLoadErrorInfo.ts b/src/generated/app-server/v2/MarketplaceLoadErrorInfo.ts new file mode 100644 index 00000000..3e60e214 --- /dev/null +++ b/src/generated/app-server/v2/MarketplaceLoadErrorInfo.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type MarketplaceLoadErrorInfo = { marketplacePath: AbsolutePathBuf, message: string, }; diff --git a/src/generated/app-server/v2/MarketplaceRemoveParams.ts b/src/generated/app-server/v2/MarketplaceRemoveParams.ts new file mode 100644 index 00000000..086dd52a --- /dev/null +++ b/src/generated/app-server/v2/MarketplaceRemoveParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MarketplaceRemoveParams = { marketplaceName: string, }; diff --git a/src/generated/app-server/v2/MarketplaceRemoveResponse.ts b/src/generated/app-server/v2/MarketplaceRemoveResponse.ts new file mode 100644 index 00000000..68a04ecd --- /dev/null +++ b/src/generated/app-server/v2/MarketplaceRemoveResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type MarketplaceRemoveResponse = { marketplaceName: string, installedRoot: AbsolutePathBuf | null, }; diff --git a/src/generated/app-server/v2/MarketplaceUpgradeErrorInfo.ts b/src/generated/app-server/v2/MarketplaceUpgradeErrorInfo.ts new file mode 100644 index 00000000..d54f8f59 --- /dev/null +++ b/src/generated/app-server/v2/MarketplaceUpgradeErrorInfo.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MarketplaceUpgradeErrorInfo = { marketplaceName: string, message: string, }; diff --git a/src/generated/app-server/v2/MarketplaceUpgradeParams.ts b/src/generated/app-server/v2/MarketplaceUpgradeParams.ts new file mode 100644 index 00000000..6d2e5f50 --- /dev/null +++ b/src/generated/app-server/v2/MarketplaceUpgradeParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MarketplaceUpgradeParams = { marketplaceName?: string | null, }; diff --git a/src/generated/app-server/v2/MarketplaceUpgradeResponse.ts b/src/generated/app-server/v2/MarketplaceUpgradeResponse.ts new file mode 100644 index 00000000..456fbdcc --- /dev/null +++ b/src/generated/app-server/v2/MarketplaceUpgradeResponse.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { MarketplaceUpgradeErrorInfo } from "./MarketplaceUpgradeErrorInfo"; + +export type MarketplaceUpgradeResponse = { selectedMarketplaces: Array, upgradedRoots: Array, errors: Array, }; diff --git a/src/generated/app-server/v2/McpAuthStatus.ts b/src/generated/app-server/v2/McpAuthStatus.ts new file mode 100644 index 00000000..6903a123 --- /dev/null +++ b/src/generated/app-server/v2/McpAuthStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpAuthStatus = "unsupported" | "notLoggedIn" | "bearerToken" | "oAuth"; diff --git a/src/generated/app-server/v2/McpElicitationArrayType.ts b/src/generated/app-server/v2/McpElicitationArrayType.ts new file mode 100644 index 00000000..066b44ea --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationArrayType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpElicitationArrayType = "array"; diff --git a/src/generated/app-server/v2/McpElicitationBooleanSchema.ts b/src/generated/app-server/v2/McpElicitationBooleanSchema.ts new file mode 100644 index 00000000..ae0f4a49 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationBooleanSchema.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationBooleanType } from "./McpElicitationBooleanType"; + +export type McpElicitationBooleanSchema = { type: McpElicitationBooleanType, title?: string, description?: string, default?: boolean, }; diff --git a/src/generated/app-server/v2/McpElicitationBooleanType.ts b/src/generated/app-server/v2/McpElicitationBooleanType.ts new file mode 100644 index 00000000..f2b9ed48 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationBooleanType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpElicitationBooleanType = "boolean"; diff --git a/src/generated/app-server/v2/McpElicitationConstOption.ts b/src/generated/app-server/v2/McpElicitationConstOption.ts new file mode 100644 index 00000000..2031655d --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationConstOption.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpElicitationConstOption = { const: string, title: string, }; diff --git a/src/generated/app-server/v2/McpElicitationEnumSchema.ts b/src/generated/app-server/v2/McpElicitationEnumSchema.ts new file mode 100644 index 00000000..e9155db4 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationEnumSchema.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationLegacyTitledEnumSchema } from "./McpElicitationLegacyTitledEnumSchema"; +import type { McpElicitationMultiSelectEnumSchema } from "./McpElicitationMultiSelectEnumSchema"; +import type { McpElicitationSingleSelectEnumSchema } from "./McpElicitationSingleSelectEnumSchema"; + +export type McpElicitationEnumSchema = McpElicitationSingleSelectEnumSchema | McpElicitationMultiSelectEnumSchema | McpElicitationLegacyTitledEnumSchema; diff --git a/src/generated/app-server/v2/McpElicitationLegacyTitledEnumSchema.ts b/src/generated/app-server/v2/McpElicitationLegacyTitledEnumSchema.ts new file mode 100644 index 00000000..8dcec317 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationLegacyTitledEnumSchema.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationStringType } from "./McpElicitationStringType"; + +export type McpElicitationLegacyTitledEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, enum: Array, enumNames?: Array, default?: string, }; diff --git a/src/generated/app-server/v2/McpElicitationMultiSelectEnumSchema.ts b/src/generated/app-server/v2/McpElicitationMultiSelectEnumSchema.ts new file mode 100644 index 00000000..48eb25e1 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationMultiSelectEnumSchema.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationTitledMultiSelectEnumSchema } from "./McpElicitationTitledMultiSelectEnumSchema"; +import type { McpElicitationUntitledMultiSelectEnumSchema } from "./McpElicitationUntitledMultiSelectEnumSchema"; + +export type McpElicitationMultiSelectEnumSchema = McpElicitationUntitledMultiSelectEnumSchema | McpElicitationTitledMultiSelectEnumSchema; diff --git a/src/generated/app-server/v2/McpElicitationNumberSchema.ts b/src/generated/app-server/v2/McpElicitationNumberSchema.ts new file mode 100644 index 00000000..6628db92 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationNumberSchema.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationNumberType } from "./McpElicitationNumberType"; + +export type McpElicitationNumberSchema = { type: McpElicitationNumberType, title?: string, description?: string, minimum?: number, maximum?: number, default?: number, }; diff --git a/src/generated/app-server/v2/McpElicitationNumberType.ts b/src/generated/app-server/v2/McpElicitationNumberType.ts new file mode 100644 index 00000000..96a9ded7 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationNumberType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpElicitationNumberType = "number" | "integer"; diff --git a/src/generated/app-server/v2/McpElicitationObjectType.ts b/src/generated/app-server/v2/McpElicitationObjectType.ts new file mode 100644 index 00000000..2449a0c1 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationObjectType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpElicitationObjectType = "object"; diff --git a/src/generated/app-server/v2/McpElicitationPrimitiveSchema.ts b/src/generated/app-server/v2/McpElicitationPrimitiveSchema.ts new file mode 100644 index 00000000..2828ae58 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationPrimitiveSchema.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationBooleanSchema } from "./McpElicitationBooleanSchema"; +import type { McpElicitationEnumSchema } from "./McpElicitationEnumSchema"; +import type { McpElicitationNumberSchema } from "./McpElicitationNumberSchema"; +import type { McpElicitationStringSchema } from "./McpElicitationStringSchema"; + +export type McpElicitationPrimitiveSchema = McpElicitationEnumSchema | McpElicitationStringSchema | McpElicitationNumberSchema | McpElicitationBooleanSchema; diff --git a/src/generated/app-server/v2/McpElicitationSchema.ts b/src/generated/app-server/v2/McpElicitationSchema.ts new file mode 100644 index 00000000..1afa5333 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationSchema.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationObjectType } from "./McpElicitationObjectType"; +import type { McpElicitationPrimitiveSchema } from "./McpElicitationPrimitiveSchema"; + +/** + * Typed form schema for MCP `elicitation/create` requests. + * + * This matches the `requestedSchema` shape from the MCP 2025-11-25 + * `ElicitRequestFormParams` schema. + */ +export type McpElicitationSchema = { $schema?: string, type: McpElicitationObjectType, properties: { [key in string]?: McpElicitationPrimitiveSchema }, required?: Array, }; diff --git a/src/generated/app-server/v2/McpElicitationSingleSelectEnumSchema.ts b/src/generated/app-server/v2/McpElicitationSingleSelectEnumSchema.ts new file mode 100644 index 00000000..2ba7dadb --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationSingleSelectEnumSchema.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationTitledSingleSelectEnumSchema } from "./McpElicitationTitledSingleSelectEnumSchema"; +import type { McpElicitationUntitledSingleSelectEnumSchema } from "./McpElicitationUntitledSingleSelectEnumSchema"; + +export type McpElicitationSingleSelectEnumSchema = McpElicitationUntitledSingleSelectEnumSchema | McpElicitationTitledSingleSelectEnumSchema; diff --git a/src/generated/app-server/v2/McpElicitationStringFormat.ts b/src/generated/app-server/v2/McpElicitationStringFormat.ts new file mode 100644 index 00000000..9891d4c7 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationStringFormat.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpElicitationStringFormat = "email" | "uri" | "date" | "date-time"; diff --git a/src/generated/app-server/v2/McpElicitationStringSchema.ts b/src/generated/app-server/v2/McpElicitationStringSchema.ts new file mode 100644 index 00000000..c2ca1eb8 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationStringSchema.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationStringFormat } from "./McpElicitationStringFormat"; +import type { McpElicitationStringType } from "./McpElicitationStringType"; + +export type McpElicitationStringSchema = { type: McpElicitationStringType, title?: string, description?: string, minLength?: number, maxLength?: number, format?: McpElicitationStringFormat, default?: string, }; diff --git a/src/generated/app-server/v2/McpElicitationStringType.ts b/src/generated/app-server/v2/McpElicitationStringType.ts new file mode 100644 index 00000000..bf2ddfab --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationStringType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpElicitationStringType = "string"; diff --git a/src/generated/app-server/v2/McpElicitationTitledEnumItems.ts b/src/generated/app-server/v2/McpElicitationTitledEnumItems.ts new file mode 100644 index 00000000..44ff2ef2 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationTitledEnumItems.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationConstOption } from "./McpElicitationConstOption"; + +export type McpElicitationTitledEnumItems = { anyOf: Array, }; diff --git a/src/generated/app-server/v2/McpElicitationTitledMultiSelectEnumSchema.ts b/src/generated/app-server/v2/McpElicitationTitledMultiSelectEnumSchema.ts new file mode 100644 index 00000000..75274d34 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationTitledMultiSelectEnumSchema.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationArrayType } from "./McpElicitationArrayType"; +import type { McpElicitationTitledEnumItems } from "./McpElicitationTitledEnumItems"; + +export type McpElicitationTitledMultiSelectEnumSchema = { type: McpElicitationArrayType, title?: string, description?: string, minItems?: bigint, maxItems?: bigint, items: McpElicitationTitledEnumItems, default?: Array, }; diff --git a/src/generated/app-server/v2/McpElicitationTitledSingleSelectEnumSchema.ts b/src/generated/app-server/v2/McpElicitationTitledSingleSelectEnumSchema.ts new file mode 100644 index 00000000..47b73191 --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationTitledSingleSelectEnumSchema.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationConstOption } from "./McpElicitationConstOption"; +import type { McpElicitationStringType } from "./McpElicitationStringType"; + +export type McpElicitationTitledSingleSelectEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, oneOf: Array, default?: string, }; diff --git a/src/generated/app-server/v2/McpElicitationUntitledEnumItems.ts b/src/generated/app-server/v2/McpElicitationUntitledEnumItems.ts new file mode 100644 index 00000000..f790881f --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationUntitledEnumItems.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationStringType } from "./McpElicitationStringType"; + +export type McpElicitationUntitledEnumItems = { type: McpElicitationStringType, enum: Array, }; diff --git a/src/generated/app-server/v2/McpElicitationUntitledMultiSelectEnumSchema.ts b/src/generated/app-server/v2/McpElicitationUntitledMultiSelectEnumSchema.ts new file mode 100644 index 00000000..5acf9fee --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationUntitledMultiSelectEnumSchema.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationArrayType } from "./McpElicitationArrayType"; +import type { McpElicitationUntitledEnumItems } from "./McpElicitationUntitledEnumItems"; + +export type McpElicitationUntitledMultiSelectEnumSchema = { type: McpElicitationArrayType, title?: string, description?: string, minItems?: bigint, maxItems?: bigint, items: McpElicitationUntitledEnumItems, default?: Array, }; diff --git a/src/generated/app-server/v2/McpElicitationUntitledSingleSelectEnumSchema.ts b/src/generated/app-server/v2/McpElicitationUntitledSingleSelectEnumSchema.ts new file mode 100644 index 00000000..49be545d --- /dev/null +++ b/src/generated/app-server/v2/McpElicitationUntitledSingleSelectEnumSchema.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpElicitationStringType } from "./McpElicitationStringType"; + +export type McpElicitationUntitledSingleSelectEnumSchema = { type: McpElicitationStringType, title?: string, description?: string, enum: Array, default?: string, }; diff --git a/src/generated/app-server/v2/McpResourceReadParams.ts b/src/generated/app-server/v2/McpResourceReadParams.ts new file mode 100644 index 00000000..c48795f2 --- /dev/null +++ b/src/generated/app-server/v2/McpResourceReadParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpResourceReadParams = { threadId?: string | null, server: string, uri: string, }; diff --git a/src/generated/app-server/v2/McpResourceReadResponse.ts b/src/generated/app-server/v2/McpResourceReadResponse.ts new file mode 100644 index 00000000..2af1dbcd --- /dev/null +++ b/src/generated/app-server/v2/McpResourceReadResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ResourceContent } from "../ResourceContent"; + +export type McpResourceReadResponse = { contents: Array, }; diff --git a/src/generated/app-server/v2/McpServerElicitationAction.ts b/src/generated/app-server/v2/McpServerElicitationAction.ts new file mode 100644 index 00000000..7be134c0 --- /dev/null +++ b/src/generated/app-server/v2/McpServerElicitationAction.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpServerElicitationAction = "accept" | "decline" | "cancel"; diff --git a/src/generated/app-server/v2/McpServerElicitationRequestParams.ts b/src/generated/app-server/v2/McpServerElicitationRequestParams.ts new file mode 100644 index 00000000..90d60f77 --- /dev/null +++ b/src/generated/app-server/v2/McpServerElicitationRequestParams.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; +import type { McpElicitationSchema } from "./McpElicitationSchema"; + +export type McpServerElicitationRequestParams = { threadId: string, +/** + * Active Codex turn when this elicitation was observed, if app-server could correlate one. + * + * This is nullable because MCP models elicitation as a standalone server-to-client request + * identified by the MCP server request id. It may be triggered during a turn, but turn + * context is app-server correlation rather than part of the protocol identity of the + * elicitation itself. + */ +turnId: string | null, serverName: string, } & ({ "mode": "form", _meta: JsonValue | null, message: string, requestedSchema: McpElicitationSchema, } | { "mode": "url", _meta: JsonValue | null, message: string, url: string, elicitationId: string, }); diff --git a/src/generated/app-server/v2/McpServerElicitationRequestResponse.ts b/src/generated/app-server/v2/McpServerElicitationRequestResponse.ts new file mode 100644 index 00000000..a3d14574 --- /dev/null +++ b/src/generated/app-server/v2/McpServerElicitationRequestResponse.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; +import type { McpServerElicitationAction } from "./McpServerElicitationAction"; + +export type McpServerElicitationRequestResponse = { action: McpServerElicitationAction, +/** + * Structured user input for accepted elicitations, mirroring RMCP `CreateElicitationResult`. + * + * This is nullable because decline/cancel responses have no content. + */ +content: JsonValue | null, +/** + * Optional client metadata for form-mode action handling. + */ +_meta: JsonValue | null, }; diff --git a/src/generated/app-server/v2/McpServerMigration.ts b/src/generated/app-server/v2/McpServerMigration.ts new file mode 100644 index 00000000..03c12510 --- /dev/null +++ b/src/generated/app-server/v2/McpServerMigration.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpServerMigration = { name: string, }; diff --git a/src/generated/app-server/v2/McpServerOauthLoginCompletedNotification.ts b/src/generated/app-server/v2/McpServerOauthLoginCompletedNotification.ts new file mode 100644 index 00000000..592860ae --- /dev/null +++ b/src/generated/app-server/v2/McpServerOauthLoginCompletedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpServerOauthLoginCompletedNotification = { name: string, success: boolean, error?: string, }; diff --git a/src/generated/app-server/v2/McpServerOauthLoginParams.ts b/src/generated/app-server/v2/McpServerOauthLoginParams.ts new file mode 100644 index 00000000..a61c3046 --- /dev/null +++ b/src/generated/app-server/v2/McpServerOauthLoginParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpServerOauthLoginParams = { name: string, scopes?: Array | null, timeoutSecs?: bigint | null, }; diff --git a/src/generated/app-server/v2/McpServerOauthLoginResponse.ts b/src/generated/app-server/v2/McpServerOauthLoginResponse.ts new file mode 100644 index 00000000..59335747 --- /dev/null +++ b/src/generated/app-server/v2/McpServerOauthLoginResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpServerOauthLoginResponse = { authorizationUrl: string, }; diff --git a/src/generated/app-server/v2/McpServerRefreshResponse.ts b/src/generated/app-server/v2/McpServerRefreshResponse.ts new file mode 100644 index 00000000..48a25d2f --- /dev/null +++ b/src/generated/app-server/v2/McpServerRefreshResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpServerRefreshResponse = Record; diff --git a/src/generated/app-server/v2/McpServerStartupState.ts b/src/generated/app-server/v2/McpServerStartupState.ts new file mode 100644 index 00000000..c62babca --- /dev/null +++ b/src/generated/app-server/v2/McpServerStartupState.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpServerStartupState = "starting" | "ready" | "failed" | "cancelled"; diff --git a/src/generated/app-server/v2/McpServerStatus.ts b/src/generated/app-server/v2/McpServerStatus.ts new file mode 100644 index 00000000..430494e2 --- /dev/null +++ b/src/generated/app-server/v2/McpServerStatus.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Resource } from "../Resource"; +import type { ResourceTemplate } from "../ResourceTemplate"; +import type { Tool } from "../Tool"; +import type { McpAuthStatus } from "./McpAuthStatus"; + +export type McpServerStatus = { name: string, tools: { [key in string]?: Tool }, resources: Array, resourceTemplates: Array, authStatus: McpAuthStatus, }; diff --git a/src/generated/app-server/v2/McpServerStatusDetail.ts b/src/generated/app-server/v2/McpServerStatusDetail.ts new file mode 100644 index 00000000..ab97cc2f --- /dev/null +++ b/src/generated/app-server/v2/McpServerStatusDetail.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpServerStatusDetail = "full" | "toolsAndAuthOnly"; diff --git a/src/generated/app-server/v2/McpServerStatusUpdatedNotification.ts b/src/generated/app-server/v2/McpServerStatusUpdatedNotification.ts new file mode 100644 index 00000000..42f5881c --- /dev/null +++ b/src/generated/app-server/v2/McpServerStatusUpdatedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { McpServerStartupState } from "./McpServerStartupState"; + +export type McpServerStatusUpdatedNotification = { name: string, status: McpServerStartupState, error: string | null, }; diff --git a/src/generated/app-server/v2/McpServerToolCallParams.ts b/src/generated/app-server/v2/McpServerToolCallParams.ts new file mode 100644 index 00000000..046a3fdc --- /dev/null +++ b/src/generated/app-server/v2/McpServerToolCallParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; + +export type McpServerToolCallParams = { threadId: string, server: string, tool: string, arguments?: JsonValue, _meta?: JsonValue, }; diff --git a/src/generated/app-server/v2/McpServerToolCallResponse.ts b/src/generated/app-server/v2/McpServerToolCallResponse.ts new file mode 100644 index 00000000..fe14692a --- /dev/null +++ b/src/generated/app-server/v2/McpServerToolCallResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; + +export type McpServerToolCallResponse = { content: Array, structuredContent?: JsonValue, isError?: boolean, _meta?: JsonValue, }; diff --git a/src/generated/app-server/v2/McpToolCallError.ts b/src/generated/app-server/v2/McpToolCallError.ts new file mode 100644 index 00000000..5e4ae839 --- /dev/null +++ b/src/generated/app-server/v2/McpToolCallError.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpToolCallError = { message: string, }; diff --git a/src/generated/app-server/v2/McpToolCallProgressNotification.ts b/src/generated/app-server/v2/McpToolCallProgressNotification.ts new file mode 100644 index 00000000..c255de27 --- /dev/null +++ b/src/generated/app-server/v2/McpToolCallProgressNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpToolCallProgressNotification = { threadId: string, turnId: string, itemId: string, message: string, }; diff --git a/src/generated/app-server/v2/McpToolCallResult.ts b/src/generated/app-server/v2/McpToolCallResult.ts new file mode 100644 index 00000000..916a5f5b --- /dev/null +++ b/src/generated/app-server/v2/McpToolCallResult.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; + +export type McpToolCallResult = { content: Array, structuredContent: JsonValue | null, _meta: JsonValue | null, }; diff --git a/src/generated/app-server/v2/McpToolCallStatus.ts b/src/generated/app-server/v2/McpToolCallStatus.ts new file mode 100644 index 00000000..f46bca07 --- /dev/null +++ b/src/generated/app-server/v2/McpToolCallStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type McpToolCallStatus = "inProgress" | "completed" | "failed"; diff --git a/src/generated/app-server/v2/MemoryCitation.ts b/src/generated/app-server/v2/MemoryCitation.ts new file mode 100644 index 00000000..7657e29f --- /dev/null +++ b/src/generated/app-server/v2/MemoryCitation.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { MemoryCitationEntry } from "./MemoryCitationEntry"; + +export type MemoryCitation = { entries: Array, threadIds: Array, }; diff --git a/src/generated/app-server/v2/MemoryCitationEntry.ts b/src/generated/app-server/v2/MemoryCitationEntry.ts new file mode 100644 index 00000000..9b9ce172 --- /dev/null +++ b/src/generated/app-server/v2/MemoryCitationEntry.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MemoryCitationEntry = { path: string, lineStart: number, lineEnd: number, note: string, }; diff --git a/src/generated/app-server/v2/MemoryResetResponse.ts b/src/generated/app-server/v2/MemoryResetResponse.ts new file mode 100644 index 00000000..d9507945 --- /dev/null +++ b/src/generated/app-server/v2/MemoryResetResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MemoryResetResponse = Record; diff --git a/src/generated/app-server/v2/MergeStrategy.ts b/src/generated/app-server/v2/MergeStrategy.ts new file mode 100644 index 00000000..098677f2 --- /dev/null +++ b/src/generated/app-server/v2/MergeStrategy.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MergeStrategy = "replace" | "upsert"; diff --git a/src/generated/app-server/v2/MigrationDetails.ts b/src/generated/app-server/v2/MigrationDetails.ts new file mode 100644 index 00000000..4fe87eab --- /dev/null +++ b/src/generated/app-server/v2/MigrationDetails.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CommandMigration } from "./CommandMigration"; +import type { HookMigration } from "./HookMigration"; +import type { McpServerMigration } from "./McpServerMigration"; +import type { PluginsMigration } from "./PluginsMigration"; +import type { SessionMigration } from "./SessionMigration"; +import type { SubagentMigration } from "./SubagentMigration"; + +export type MigrationDetails = { plugins: Array, sessions: Array, mcpServers: Array, hooks: Array, subagents: Array, commands: Array, }; diff --git a/src/generated/app-server/v2/MockExperimentalMethodParams.ts b/src/generated/app-server/v2/MockExperimentalMethodParams.ts new file mode 100644 index 00000000..fe4577fa --- /dev/null +++ b/src/generated/app-server/v2/MockExperimentalMethodParams.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MockExperimentalMethodParams = { +/** + * Test-only payload field. + */ +value?: string | null, }; diff --git a/src/generated/app-server/v2/MockExperimentalMethodResponse.ts b/src/generated/app-server/v2/MockExperimentalMethodResponse.ts new file mode 100644 index 00000000..41085475 --- /dev/null +++ b/src/generated/app-server/v2/MockExperimentalMethodResponse.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MockExperimentalMethodResponse = { +/** + * Echoes the input `value`. + */ +echoed: string | null, }; diff --git a/src/generated/app-server/v2/Model.ts b/src/generated/app-server/v2/Model.ts new file mode 100644 index 00000000..2354ffbf --- /dev/null +++ b/src/generated/app-server/v2/Model.ts @@ -0,0 +1,15 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { InputModality } from "../InputModality"; +import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ModelAvailabilityNux } from "./ModelAvailabilityNux"; +import type { ModelServiceTier } from "./ModelServiceTier"; +import type { ModelUpgradeInfo } from "./ModelUpgradeInfo"; +import type { ReasoningEffortOption } from "./ReasoningEffortOption"; + +export type Model = { id: string, model: string, upgrade: string | null, upgradeInfo: ModelUpgradeInfo | null, availabilityNux: ModelAvailabilityNux | null, displayName: string, description: string, hidden: boolean, supportedReasoningEfforts: Array, defaultReasoningEffort: ReasoningEffort, inputModalities: Array, supportsPersonality: boolean, +/** + * Deprecated: use `serviceTiers` instead. + */ +additionalSpeedTiers: Array, serviceTiers: Array, isDefault: boolean, }; diff --git a/src/generated/app-server/v2/ModelAvailabilityNux.ts b/src/generated/app-server/v2/ModelAvailabilityNux.ts new file mode 100644 index 00000000..7254aaec --- /dev/null +++ b/src/generated/app-server/v2/ModelAvailabilityNux.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ModelAvailabilityNux = { message: string, }; diff --git a/src/generated/app-server/v2/ModelListParams.ts b/src/generated/app-server/v2/ModelListParams.ts new file mode 100644 index 00000000..dae406dd --- /dev/null +++ b/src/generated/app-server/v2/ModelListParams.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ModelListParams = { +/** + * Opaque pagination cursor returned by a previous call. + */ +cursor?: string | null, +/** + * Optional page size; defaults to a reasonable server-side value. + */ +limit?: number | null, +/** + * When true, include models that are hidden from the default picker list. + */ +includeHidden?: boolean | null, }; diff --git a/src/generated/app-server/v2/ModelListResponse.ts b/src/generated/app-server/v2/ModelListResponse.ts new file mode 100644 index 00000000..b664b6c0 --- /dev/null +++ b/src/generated/app-server/v2/ModelListResponse.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Model } from "./Model"; + +export type ModelListResponse = { data: Array, +/** + * Opaque cursor to pass to the next call to continue after the last item. + * If None, there are no more items to return. + */ +nextCursor: string | null, }; diff --git a/src/generated/app-server/v2/ModelProviderCapabilitiesReadParams.ts b/src/generated/app-server/v2/ModelProviderCapabilitiesReadParams.ts new file mode 100644 index 00000000..00cbe470 --- /dev/null +++ b/src/generated/app-server/v2/ModelProviderCapabilitiesReadParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ModelProviderCapabilitiesReadParams = Record; diff --git a/src/generated/app-server/v2/ModelProviderCapabilitiesReadResponse.ts b/src/generated/app-server/v2/ModelProviderCapabilitiesReadResponse.ts new file mode 100644 index 00000000..043fc304 --- /dev/null +++ b/src/generated/app-server/v2/ModelProviderCapabilitiesReadResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ModelProviderCapabilitiesReadResponse = { namespaceTools: boolean, imageGeneration: boolean, webSearch: boolean, }; diff --git a/src/generated/app-server/v2/ModelRerouteReason.ts b/src/generated/app-server/v2/ModelRerouteReason.ts new file mode 100644 index 00000000..e780e7f9 --- /dev/null +++ b/src/generated/app-server/v2/ModelRerouteReason.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ModelRerouteReason = "highRiskCyberActivity"; diff --git a/src/generated/app-server/v2/ModelReroutedNotification.ts b/src/generated/app-server/v2/ModelReroutedNotification.ts new file mode 100644 index 00000000..9b6b2e52 --- /dev/null +++ b/src/generated/app-server/v2/ModelReroutedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ModelRerouteReason } from "./ModelRerouteReason"; + +export type ModelReroutedNotification = { threadId: string, turnId: string, fromModel: string, toModel: string, reason: ModelRerouteReason, }; diff --git a/src/generated/app-server/v2/ModelServiceTier.ts b/src/generated/app-server/v2/ModelServiceTier.ts new file mode 100644 index 00000000..09693d07 --- /dev/null +++ b/src/generated/app-server/v2/ModelServiceTier.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ModelServiceTier = { id: string, name: string, description: string, }; diff --git a/src/generated/app-server/v2/ModelUpgradeInfo.ts b/src/generated/app-server/v2/ModelUpgradeInfo.ts new file mode 100644 index 00000000..82d73e9d --- /dev/null +++ b/src/generated/app-server/v2/ModelUpgradeInfo.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ModelUpgradeInfo = { model: string, upgradeCopy: string | null, modelLink: string | null, migrationMarkdown: string | null, }; diff --git a/src/generated/app-server/v2/ModelVerification.ts b/src/generated/app-server/v2/ModelVerification.ts new file mode 100644 index 00000000..00538c09 --- /dev/null +++ b/src/generated/app-server/v2/ModelVerification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ModelVerification = "trustedAccessForCyber"; diff --git a/src/generated/app-server/v2/ModelVerificationNotification.ts b/src/generated/app-server/v2/ModelVerificationNotification.ts new file mode 100644 index 00000000..3af484d0 --- /dev/null +++ b/src/generated/app-server/v2/ModelVerificationNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ModelVerification } from "./ModelVerification"; + +export type ModelVerificationNotification = { threadId: string, turnId: string, verifications: Array, }; diff --git a/src/generated/app-server/v2/NetworkAccess.ts b/src/generated/app-server/v2/NetworkAccess.ts new file mode 100644 index 00000000..7b697b23 --- /dev/null +++ b/src/generated/app-server/v2/NetworkAccess.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NetworkAccess = "restricted" | "enabled"; diff --git a/src/generated/app-server/v2/NetworkApprovalContext.ts b/src/generated/app-server/v2/NetworkApprovalContext.ts new file mode 100644 index 00000000..b4b78e47 --- /dev/null +++ b/src/generated/app-server/v2/NetworkApprovalContext.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { NetworkApprovalProtocol } from "./NetworkApprovalProtocol"; + +export type NetworkApprovalContext = { host: string, protocol: NetworkApprovalProtocol, }; diff --git a/src/generated/app-server/v2/NetworkApprovalProtocol.ts b/src/generated/app-server/v2/NetworkApprovalProtocol.ts new file mode 100644 index 00000000..9dd4066f --- /dev/null +++ b/src/generated/app-server/v2/NetworkApprovalProtocol.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NetworkApprovalProtocol = "http" | "https" | "socks5Tcp" | "socks5Udp"; diff --git a/src/generated/app-server/v2/NetworkDomainPermission.ts b/src/generated/app-server/v2/NetworkDomainPermission.ts new file mode 100644 index 00000000..2ea44392 --- /dev/null +++ b/src/generated/app-server/v2/NetworkDomainPermission.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NetworkDomainPermission = "allow" | "deny"; diff --git a/src/generated/app-server/v2/NetworkPolicyAmendment.ts b/src/generated/app-server/v2/NetworkPolicyAmendment.ts new file mode 100644 index 00000000..4e5092e4 --- /dev/null +++ b/src/generated/app-server/v2/NetworkPolicyAmendment.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { NetworkPolicyRuleAction } from "./NetworkPolicyRuleAction"; + +export type NetworkPolicyAmendment = { host: string, action: NetworkPolicyRuleAction, }; diff --git a/src/generated/app-server/v2/NetworkPolicyRuleAction.ts b/src/generated/app-server/v2/NetworkPolicyRuleAction.ts new file mode 100644 index 00000000..55ec7003 --- /dev/null +++ b/src/generated/app-server/v2/NetworkPolicyRuleAction.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NetworkPolicyRuleAction = "allow" | "deny"; diff --git a/src/generated/app-server/v2/NetworkRequirements.ts b/src/generated/app-server/v2/NetworkRequirements.ts new file mode 100644 index 00000000..04e07ef1 --- /dev/null +++ b/src/generated/app-server/v2/NetworkRequirements.ts @@ -0,0 +1,32 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { NetworkDomainPermission } from "./NetworkDomainPermission"; +import type { NetworkUnixSocketPermission } from "./NetworkUnixSocketPermission"; + +export type NetworkRequirements = { enabled: boolean | null, httpPort: number | null, socksPort: number | null, allowUpstreamProxy: boolean | null, dangerouslyAllowNonLoopbackProxy: boolean | null, dangerouslyAllowAllUnixSockets: boolean | null, +/** + * Canonical network permission map for `experimental_network`. + */ +domains: { [key in string]?: NetworkDomainPermission } | null, +/** + * When true, only managed allowlist entries are respected while managed + * network enforcement is active. + */ +managedAllowedDomainsOnly: boolean | null, +/** + * Legacy compatibility view derived from `domains`. + */ +allowedDomains: Array | null, +/** + * Legacy compatibility view derived from `domains`. + */ +deniedDomains: Array | null, +/** + * Canonical unix socket permission map for `experimental_network`. + */ +unixSockets: { [key in string]?: NetworkUnixSocketPermission } | null, +/** + * Legacy compatibility view derived from `unix_sockets`. + */ +allowUnixSockets: Array | null, allowLocalBinding: boolean | null, }; diff --git a/src/generated/app-server/v2/NetworkUnixSocketPermission.ts b/src/generated/app-server/v2/NetworkUnixSocketPermission.ts new file mode 100644 index 00000000..466c6e5f --- /dev/null +++ b/src/generated/app-server/v2/NetworkUnixSocketPermission.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NetworkUnixSocketPermission = "allow" | "none"; diff --git a/src/generated/app-server/v2/NonSteerableTurnKind.ts b/src/generated/app-server/v2/NonSteerableTurnKind.ts new file mode 100644 index 00000000..2624df2b --- /dev/null +++ b/src/generated/app-server/v2/NonSteerableTurnKind.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NonSteerableTurnKind = "review" | "compact"; diff --git a/src/generated/app-server/v2/OverriddenMetadata.ts b/src/generated/app-server/v2/OverriddenMetadata.ts new file mode 100644 index 00000000..0f6396bb --- /dev/null +++ b/src/generated/app-server/v2/OverriddenMetadata.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; +import type { ConfigLayerMetadata } from "./ConfigLayerMetadata"; + +export type OverriddenMetadata = { message: string, overridingLayer: ConfigLayerMetadata, effectiveValue: JsonValue, }; diff --git a/src/generated/app-server/v2/PatchApplyStatus.ts b/src/generated/app-server/v2/PatchApplyStatus.ts new file mode 100644 index 00000000..620be789 --- /dev/null +++ b/src/generated/app-server/v2/PatchApplyStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PatchApplyStatus = "inProgress" | "completed" | "failed" | "declined"; diff --git a/src/generated/app-server/v2/PatchChangeKind.ts b/src/generated/app-server/v2/PatchChangeKind.ts new file mode 100644 index 00000000..23dda6cb --- /dev/null +++ b/src/generated/app-server/v2/PatchChangeKind.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PatchChangeKind = { "type": "add" } | { "type": "delete" } | { "type": "update", move_path: string | null, }; diff --git a/src/generated/app-server/v2/PermissionGrantScope.ts b/src/generated/app-server/v2/PermissionGrantScope.ts new file mode 100644 index 00000000..8ca127eb --- /dev/null +++ b/src/generated/app-server/v2/PermissionGrantScope.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PermissionGrantScope = "turn" | "session"; diff --git a/src/generated/app-server/v2/PermissionProfile.ts b/src/generated/app-server/v2/PermissionProfile.ts new file mode 100644 index 00000000..7642c276 --- /dev/null +++ b/src/generated/app-server/v2/PermissionProfile.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PermissionProfileFileSystemPermissions } from "./PermissionProfileFileSystemPermissions"; +import type { PermissionProfileNetworkPermissions } from "./PermissionProfileNetworkPermissions"; + +export type PermissionProfile = { "type": "managed", network: PermissionProfileNetworkPermissions, fileSystem: PermissionProfileFileSystemPermissions, } | { "type": "disabled" } | { "type": "external", network: PermissionProfileNetworkPermissions, }; diff --git a/src/generated/app-server/v2/PermissionProfileFileSystemPermissions.ts b/src/generated/app-server/v2/PermissionProfileFileSystemPermissions.ts new file mode 100644 index 00000000..29aeceb4 --- /dev/null +++ b/src/generated/app-server/v2/PermissionProfileFileSystemPermissions.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FileSystemSandboxEntry } from "./FileSystemSandboxEntry"; + +export type PermissionProfileFileSystemPermissions = { "type": "restricted", entries: Array, globScanMaxDepth?: number, } | { "type": "unrestricted" }; diff --git a/src/generated/app-server/v2/PermissionProfileModificationParams.ts b/src/generated/app-server/v2/PermissionProfileModificationParams.ts new file mode 100644 index 00000000..c619edce --- /dev/null +++ b/src/generated/app-server/v2/PermissionProfileModificationParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type PermissionProfileModificationParams = { "type": "additionalWritableRoot", path: AbsolutePathBuf, }; diff --git a/src/generated/app-server/v2/PermissionProfileNetworkPermissions.ts b/src/generated/app-server/v2/PermissionProfileNetworkPermissions.ts new file mode 100644 index 00000000..0b25a769 --- /dev/null +++ b/src/generated/app-server/v2/PermissionProfileNetworkPermissions.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PermissionProfileNetworkPermissions = { enabled: boolean, }; diff --git a/src/generated/app-server/v2/PermissionProfileSelectionParams.ts b/src/generated/app-server/v2/PermissionProfileSelectionParams.ts new file mode 100644 index 00000000..a415bd00 --- /dev/null +++ b/src/generated/app-server/v2/PermissionProfileSelectionParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PermissionProfileModificationParams } from "./PermissionProfileModificationParams"; + +export type PermissionProfileSelectionParams = { "type": "profile", id: string, modifications?: Array | null, }; diff --git a/src/generated/app-server/v2/PermissionsRequestApprovalParams.ts b/src/generated/app-server/v2/PermissionsRequestApprovalParams.ts new file mode 100644 index 00000000..509f6092 --- /dev/null +++ b/src/generated/app-server/v2/PermissionsRequestApprovalParams.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { RequestPermissionProfile } from "./RequestPermissionProfile"; + +export type PermissionsRequestApprovalParams = { threadId: string, turnId: string, itemId: string, +/** + * Unix timestamp (in milliseconds) when this approval request started. + */ +startedAtMs: number, cwd: AbsolutePathBuf, reason: string | null, permissions: RequestPermissionProfile, }; diff --git a/src/generated/app-server/v2/PermissionsRequestApprovalResponse.ts b/src/generated/app-server/v2/PermissionsRequestApprovalResponse.ts new file mode 100644 index 00000000..f42b3956 --- /dev/null +++ b/src/generated/app-server/v2/PermissionsRequestApprovalResponse.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { GrantedPermissionProfile } from "./GrantedPermissionProfile"; +import type { PermissionGrantScope } from "./PermissionGrantScope"; + +export type PermissionsRequestApprovalResponse = { permissions: GrantedPermissionProfile, scope: PermissionGrantScope, +/** + * Review every subsequent command in this turn before normal sandboxed execution. + */ +strictAutoReview?: boolean, }; diff --git a/src/generated/app-server/v2/PlanDeltaNotification.ts b/src/generated/app-server/v2/PlanDeltaNotification.ts new file mode 100644 index 00000000..5ab35966 --- /dev/null +++ b/src/generated/app-server/v2/PlanDeltaNotification.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - proposed plan streaming deltas for plan items. Clients should + * not assume concatenated deltas match the completed plan item content. + */ +export type PlanDeltaNotification = { threadId: string, turnId: string, itemId: string, delta: string, }; diff --git a/src/generated/app-server/v2/PluginAuthPolicy.ts b/src/generated/app-server/v2/PluginAuthPolicy.ts new file mode 100644 index 00000000..5b90e9c3 --- /dev/null +++ b/src/generated/app-server/v2/PluginAuthPolicy.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginAuthPolicy = "ON_INSTALL" | "ON_USE"; diff --git a/src/generated/app-server/v2/PluginAvailability.ts b/src/generated/app-server/v2/PluginAvailability.ts new file mode 100644 index 00000000..bec0b88c --- /dev/null +++ b/src/generated/app-server/v2/PluginAvailability.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginAvailability = "AVAILABLE" | "DISABLED_BY_ADMIN"; diff --git a/src/generated/app-server/v2/PluginDetail.ts b/src/generated/app-server/v2/PluginDetail.ts new file mode 100644 index 00000000..64836c87 --- /dev/null +++ b/src/generated/app-server/v2/PluginDetail.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { AppSummary } from "./AppSummary"; +import type { PluginHookSummary } from "./PluginHookSummary"; +import type { PluginSummary } from "./PluginSummary"; +import type { SkillSummary } from "./SkillSummary"; + +export type PluginDetail = { marketplaceName: string, marketplacePath: AbsolutePathBuf | null, summary: PluginSummary, description: string | null, skills: Array, hooks: Array, apps: Array, mcpServers: Array, }; diff --git a/src/generated/app-server/v2/PluginHookSummary.ts b/src/generated/app-server/v2/PluginHookSummary.ts new file mode 100644 index 00000000..48046bbd --- /dev/null +++ b/src/generated/app-server/v2/PluginHookSummary.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { HookEventName } from "./HookEventName"; + +export type PluginHookSummary = { key: string, eventName: HookEventName, }; diff --git a/src/generated/app-server/v2/PluginInstallParams.ts b/src/generated/app-server/v2/PluginInstallParams.ts new file mode 100644 index 00000000..257dc47a --- /dev/null +++ b/src/generated/app-server/v2/PluginInstallParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type PluginInstallParams = { marketplacePath?: AbsolutePathBuf | null, remoteMarketplaceName?: string | null, pluginName: string, }; diff --git a/src/generated/app-server/v2/PluginInstallPolicy.ts b/src/generated/app-server/v2/PluginInstallPolicy.ts new file mode 100644 index 00000000..d624f38e --- /dev/null +++ b/src/generated/app-server/v2/PluginInstallPolicy.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginInstallPolicy = "NOT_AVAILABLE" | "AVAILABLE" | "INSTALLED_BY_DEFAULT"; diff --git a/src/generated/app-server/v2/PluginInstallResponse.ts b/src/generated/app-server/v2/PluginInstallResponse.ts new file mode 100644 index 00000000..b88119d4 --- /dev/null +++ b/src/generated/app-server/v2/PluginInstallResponse.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AppSummary } from "./AppSummary"; +import type { PluginAuthPolicy } from "./PluginAuthPolicy"; + +export type PluginInstallResponse = { authPolicy: PluginAuthPolicy, appsNeedingAuth: Array, }; diff --git a/src/generated/app-server/v2/PluginInterface.ts b/src/generated/app-server/v2/PluginInterface.ts new file mode 100644 index 00000000..4e97ee66 --- /dev/null +++ b/src/generated/app-server/v2/PluginInterface.ts @@ -0,0 +1,35 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type PluginInterface = { displayName: string | null, shortDescription: string | null, longDescription: string | null, developerName: string | null, category: string | null, capabilities: Array, websiteUrl: string | null, privacyPolicyUrl: string | null, termsOfServiceUrl: string | null, +/** + * Starter prompts for the plugin. Capped at 3 entries with a maximum of + * 128 characters per entry. + */ +defaultPrompt: Array | null, brandColor: string | null, +/** + * Local composer icon path, resolved from the installed plugin package. + */ +composerIcon: AbsolutePathBuf | null, +/** + * Remote composer icon URL from the plugin catalog. + */ +composerIconUrl: string | null, +/** + * Local logo path, resolved from the installed plugin package. + */ +logo: AbsolutePathBuf | null, +/** + * Remote logo URL from the plugin catalog. + */ +logoUrl: string | null, +/** + * Local screenshot paths, resolved from the installed plugin package. + */ +screenshots: Array, +/** + * Remote screenshot URLs from the plugin catalog. + */ +screenshotUrls: Array, }; diff --git a/src/generated/app-server/v2/PluginListMarketplaceKind.ts b/src/generated/app-server/v2/PluginListMarketplaceKind.ts new file mode 100644 index 00000000..6ff6161f --- /dev/null +++ b/src/generated/app-server/v2/PluginListMarketplaceKind.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginListMarketplaceKind = "local" | "workspace-directory" | "shared-with-me"; diff --git a/src/generated/app-server/v2/PluginListParams.ts b/src/generated/app-server/v2/PluginListParams.ts new file mode 100644 index 00000000..6dd86b8a --- /dev/null +++ b/src/generated/app-server/v2/PluginListParams.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { PluginListMarketplaceKind } from "./PluginListMarketplaceKind"; + +export type PluginListParams = { +/** + * Optional working directories used to discover repo marketplaces. When omitted, + * only home-scoped marketplaces and the official curated marketplace are considered. + */ +cwds?: Array | null, +/** + * Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus + * the default remote catalog when enabled by feature flag. + */ +marketplaceKinds?: Array | null, }; diff --git a/src/generated/app-server/v2/PluginListResponse.ts b/src/generated/app-server/v2/PluginListResponse.ts new file mode 100644 index 00000000..d50200c9 --- /dev/null +++ b/src/generated/app-server/v2/PluginListResponse.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { MarketplaceLoadErrorInfo } from "./MarketplaceLoadErrorInfo"; +import type { PluginMarketplaceEntry } from "./PluginMarketplaceEntry"; + +export type PluginListResponse = { marketplaces: Array, marketplaceLoadErrors: Array, featuredPluginIds: Array, }; diff --git a/src/generated/app-server/v2/PluginMarketplaceEntry.ts b/src/generated/app-server/v2/PluginMarketplaceEntry.ts new file mode 100644 index 00000000..f9dcee27 --- /dev/null +++ b/src/generated/app-server/v2/PluginMarketplaceEntry.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { MarketplaceInterface } from "./MarketplaceInterface"; +import type { PluginSummary } from "./PluginSummary"; + +export type PluginMarketplaceEntry = { name: string, +/** + * Local marketplace file path when the marketplace is backed by a local file. + * Remote-only catalog marketplaces do not have a local path. + */ +path: AbsolutePathBuf | null, interface: MarketplaceInterface | null, plugins: Array, }; diff --git a/src/generated/app-server/v2/PluginReadParams.ts b/src/generated/app-server/v2/PluginReadParams.ts new file mode 100644 index 00000000..8c4394f0 --- /dev/null +++ b/src/generated/app-server/v2/PluginReadParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type PluginReadParams = { marketplacePath?: AbsolutePathBuf | null, remoteMarketplaceName?: string | null, pluginName: string, }; diff --git a/src/generated/app-server/v2/PluginReadResponse.ts b/src/generated/app-server/v2/PluginReadResponse.ts new file mode 100644 index 00000000..841b916e --- /dev/null +++ b/src/generated/app-server/v2/PluginReadResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginDetail } from "./PluginDetail"; + +export type PluginReadResponse = { plugin: PluginDetail, }; diff --git a/src/generated/app-server/v2/PluginShareContext.ts b/src/generated/app-server/v2/PluginShareContext.ts new file mode 100644 index 00000000..f1c5c958 --- /dev/null +++ b/src/generated/app-server/v2/PluginShareContext.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginSharePrincipal } from "./PluginSharePrincipal"; + +export type PluginShareContext = { remotePluginId: string, shareUrl: string | null, creatorAccountUserId: string | null, creatorName: string | null, shareTargets: Array | null, }; diff --git a/src/generated/app-server/v2/PluginShareDeleteParams.ts b/src/generated/app-server/v2/PluginShareDeleteParams.ts new file mode 100644 index 00000000..b0adaf2d --- /dev/null +++ b/src/generated/app-server/v2/PluginShareDeleteParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginShareDeleteParams = { remotePluginId: string, }; diff --git a/src/generated/app-server/v2/PluginShareDeleteResponse.ts b/src/generated/app-server/v2/PluginShareDeleteResponse.ts new file mode 100644 index 00000000..23102683 --- /dev/null +++ b/src/generated/app-server/v2/PluginShareDeleteResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginShareDeleteResponse = Record; diff --git a/src/generated/app-server/v2/PluginShareDiscoverability.ts b/src/generated/app-server/v2/PluginShareDiscoverability.ts new file mode 100644 index 00000000..8c224216 --- /dev/null +++ b/src/generated/app-server/v2/PluginShareDiscoverability.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginShareDiscoverability = "LISTED" | "UNLISTED" | "PRIVATE"; diff --git a/src/generated/app-server/v2/PluginShareListItem.ts b/src/generated/app-server/v2/PluginShareListItem.ts new file mode 100644 index 00000000..b63738aa --- /dev/null +++ b/src/generated/app-server/v2/PluginShareListItem.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { PluginSummary } from "./PluginSummary"; + +export type PluginShareListItem = { plugin: PluginSummary, shareUrl: string, localPluginPath: AbsolutePathBuf | null, }; diff --git a/src/generated/app-server/v2/PluginShareListParams.ts b/src/generated/app-server/v2/PluginShareListParams.ts new file mode 100644 index 00000000..167ace7a --- /dev/null +++ b/src/generated/app-server/v2/PluginShareListParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginShareListParams = Record; diff --git a/src/generated/app-server/v2/PluginShareListResponse.ts b/src/generated/app-server/v2/PluginShareListResponse.ts new file mode 100644 index 00000000..50b324f5 --- /dev/null +++ b/src/generated/app-server/v2/PluginShareListResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginShareListItem } from "./PluginShareListItem"; + +export type PluginShareListResponse = { data: Array, }; diff --git a/src/generated/app-server/v2/PluginSharePrincipal.ts b/src/generated/app-server/v2/PluginSharePrincipal.ts new file mode 100644 index 00000000..9e0ecc48 --- /dev/null +++ b/src/generated/app-server/v2/PluginSharePrincipal.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginSharePrincipalType } from "./PluginSharePrincipalType"; + +export type PluginSharePrincipal = { principalType: PluginSharePrincipalType, principalId: string, name: string, }; diff --git a/src/generated/app-server/v2/PluginSharePrincipalType.ts b/src/generated/app-server/v2/PluginSharePrincipalType.ts new file mode 100644 index 00000000..e54c129c --- /dev/null +++ b/src/generated/app-server/v2/PluginSharePrincipalType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginSharePrincipalType = "user" | "group" | "workspace"; diff --git a/src/generated/app-server/v2/PluginShareSaveParams.ts b/src/generated/app-server/v2/PluginShareSaveParams.ts new file mode 100644 index 00000000..c8df0d6c --- /dev/null +++ b/src/generated/app-server/v2/PluginShareSaveParams.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { PluginShareDiscoverability } from "./PluginShareDiscoverability"; +import type { PluginShareTarget } from "./PluginShareTarget"; + +export type PluginShareSaveParams = { pluginPath: AbsolutePathBuf, remotePluginId?: string | null, discoverability?: PluginShareDiscoverability | null, shareTargets?: Array | null, }; diff --git a/src/generated/app-server/v2/PluginShareSaveResponse.ts b/src/generated/app-server/v2/PluginShareSaveResponse.ts new file mode 100644 index 00000000..b53ace0e --- /dev/null +++ b/src/generated/app-server/v2/PluginShareSaveResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginShareSaveResponse = { remotePluginId: string, shareUrl: string, }; diff --git a/src/generated/app-server/v2/PluginShareTarget.ts b/src/generated/app-server/v2/PluginShareTarget.ts new file mode 100644 index 00000000..fd196908 --- /dev/null +++ b/src/generated/app-server/v2/PluginShareTarget.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginSharePrincipalType } from "./PluginSharePrincipalType"; + +export type PluginShareTarget = { principalType: PluginSharePrincipalType, principalId: string, }; diff --git a/src/generated/app-server/v2/PluginShareUpdateDiscoverability.ts b/src/generated/app-server/v2/PluginShareUpdateDiscoverability.ts new file mode 100644 index 00000000..fd601987 --- /dev/null +++ b/src/generated/app-server/v2/PluginShareUpdateDiscoverability.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginShareUpdateDiscoverability = "UNLISTED" | "PRIVATE"; diff --git a/src/generated/app-server/v2/PluginShareUpdateTargetsParams.ts b/src/generated/app-server/v2/PluginShareUpdateTargetsParams.ts new file mode 100644 index 00000000..eecd4be8 --- /dev/null +++ b/src/generated/app-server/v2/PluginShareUpdateTargetsParams.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginShareTarget } from "./PluginShareTarget"; +import type { PluginShareUpdateDiscoverability } from "./PluginShareUpdateDiscoverability"; + +export type PluginShareUpdateTargetsParams = { remotePluginId: string, discoverability: PluginShareUpdateDiscoverability, shareTargets: Array, }; diff --git a/src/generated/app-server/v2/PluginShareUpdateTargetsResponse.ts b/src/generated/app-server/v2/PluginShareUpdateTargetsResponse.ts new file mode 100644 index 00000000..0ce72246 --- /dev/null +++ b/src/generated/app-server/v2/PluginShareUpdateTargetsResponse.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginShareDiscoverability } from "./PluginShareDiscoverability"; +import type { PluginSharePrincipal } from "./PluginSharePrincipal"; + +export type PluginShareUpdateTargetsResponse = { principals: Array, discoverability: PluginShareDiscoverability, }; diff --git a/src/generated/app-server/v2/PluginSkillReadParams.ts b/src/generated/app-server/v2/PluginSkillReadParams.ts new file mode 100644 index 00000000..54a63599 --- /dev/null +++ b/src/generated/app-server/v2/PluginSkillReadParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginSkillReadParams = { remoteMarketplaceName: string, remotePluginId: string, skillName: string, }; diff --git a/src/generated/app-server/v2/PluginSkillReadResponse.ts b/src/generated/app-server/v2/PluginSkillReadResponse.ts new file mode 100644 index 00000000..0ae37982 --- /dev/null +++ b/src/generated/app-server/v2/PluginSkillReadResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginSkillReadResponse = { contents: string | null, }; diff --git a/src/generated/app-server/v2/PluginSource.ts b/src/generated/app-server/v2/PluginSource.ts new file mode 100644 index 00000000..f6e86719 --- /dev/null +++ b/src/generated/app-server/v2/PluginSource.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type PluginSource = { "type": "local", path: AbsolutePathBuf, } | { "type": "git", url: string, path: string | null, refName: string | null, sha: string | null, } | { "type": "remote" }; diff --git a/src/generated/app-server/v2/PluginSummary.ts b/src/generated/app-server/v2/PluginSummary.ts new file mode 100644 index 00000000..d855f3d3 --- /dev/null +++ b/src/generated/app-server/v2/PluginSummary.ts @@ -0,0 +1,19 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginAuthPolicy } from "./PluginAuthPolicy"; +import type { PluginAvailability } from "./PluginAvailability"; +import type { PluginInstallPolicy } from "./PluginInstallPolicy"; +import type { PluginInterface } from "./PluginInterface"; +import type { PluginShareContext } from "./PluginShareContext"; +import type { PluginSource } from "./PluginSource"; + +export type PluginSummary = { id: string, name: string, +/** + * Remote sharing context associated with this plugin when available. + */ +shareContext: PluginShareContext | null, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy, authPolicy: PluginAuthPolicy, +/** + * Availability state for installing and using the plugin. + */ +availability: PluginAvailability, interface: PluginInterface | null, keywords: Array, }; diff --git a/src/generated/app-server/v2/PluginUninstallParams.ts b/src/generated/app-server/v2/PluginUninstallParams.ts new file mode 100644 index 00000000..e7f52c0e --- /dev/null +++ b/src/generated/app-server/v2/PluginUninstallParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginUninstallParams = { pluginId: string, }; diff --git a/src/generated/app-server/v2/PluginUninstallResponse.ts b/src/generated/app-server/v2/PluginUninstallResponse.ts new file mode 100644 index 00000000..5d02c2f7 --- /dev/null +++ b/src/generated/app-server/v2/PluginUninstallResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginUninstallResponse = Record; diff --git a/src/generated/app-server/v2/PluginsMigration.ts b/src/generated/app-server/v2/PluginsMigration.ts new file mode 100644 index 00000000..0dce06d9 --- /dev/null +++ b/src/generated/app-server/v2/PluginsMigration.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginsMigration = { marketplaceName: string, pluginNames: Array, }; diff --git a/src/generated/app-server/v2/ProcessExitedNotification.ts b/src/generated/app-server/v2/ProcessExitedNotification.ts new file mode 100644 index 00000000..0d826334 --- /dev/null +++ b/src/generated/app-server/v2/ProcessExitedNotification.ts @@ -0,0 +1,42 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Final process exit notification for `process/spawn`. + */ +export type ProcessExitedNotification = { +/** + * Client-supplied, connection-scoped `processHandle` from `process/spawn`. + */ +processHandle: string, +/** + * Process exit code. + */ +exitCode: number, +/** + * Buffered stdout capture. + * + * Empty when stdout was streamed via `process/outputDelta`. + */ +stdout: string, +/** + * Whether stdout reached `outputBytesCap`. + * + * In streaming mode, stdout is empty and cap state is also reported on the + * final stdout `process/outputDelta` notification. + */ +stdoutCapReached: boolean, +/** + * Buffered stderr capture. + * + * Empty when stderr was streamed via `process/outputDelta`. + */ +stderr: string, +/** + * Whether stderr reached `outputBytesCap`. + * + * In streaming mode, stderr is empty and cap state is also reported on the + * final stderr `process/outputDelta` notification. + */ +stderrCapReached: boolean, }; diff --git a/src/generated/app-server/v2/ProcessKillParams.ts b/src/generated/app-server/v2/ProcessKillParams.ts new file mode 100644 index 00000000..c222d6b7 --- /dev/null +++ b/src/generated/app-server/v2/ProcessKillParams.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Terminate a running `process/spawn` session. + */ +export type ProcessKillParams = { +/** + * Client-supplied, connection-scoped `processHandle` from `process/spawn`. + */ +processHandle: string, }; diff --git a/src/generated/app-server/v2/ProcessKillResponse.ts b/src/generated/app-server/v2/ProcessKillResponse.ts new file mode 100644 index 00000000..d1bd8242 --- /dev/null +++ b/src/generated/app-server/v2/ProcessKillResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Empty success response for `process/kill`. + */ +export type ProcessKillResponse = Record; diff --git a/src/generated/app-server/v2/ProcessOutputDeltaNotification.ts b/src/generated/app-server/v2/ProcessOutputDeltaNotification.ts new file mode 100644 index 00000000..46369e39 --- /dev/null +++ b/src/generated/app-server/v2/ProcessOutputDeltaNotification.ts @@ -0,0 +1,26 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ProcessOutputStream } from "./ProcessOutputStream"; + +/** + * Base64-encoded output chunk emitted for a streaming `process/spawn` request. + */ +export type ProcessOutputDeltaNotification = { +/** + * Client-supplied, connection-scoped `processHandle` from `process/spawn`. + */ +processHandle: string, +/** + * Output stream this chunk belongs to. + */ +stream: ProcessOutputStream, +/** + * Base64-encoded output bytes. + */ +deltaBase64: string, +/** + * True on the final streamed chunk for this stream when output was + * truncated by `outputBytesCap`. + */ +capReached: boolean, }; diff --git a/src/generated/app-server/v2/ProcessOutputStream.ts b/src/generated/app-server/v2/ProcessOutputStream.ts new file mode 100644 index 00000000..1bb550d9 --- /dev/null +++ b/src/generated/app-server/v2/ProcessOutputStream.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Stream label for `process/outputDelta` notifications. + */ +export type ProcessOutputStream = "stdout" | "stderr"; diff --git a/src/generated/app-server/v2/ProcessResizePtyParams.ts b/src/generated/app-server/v2/ProcessResizePtyParams.ts new file mode 100644 index 00000000..f789eae6 --- /dev/null +++ b/src/generated/app-server/v2/ProcessResizePtyParams.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ProcessTerminalSize } from "./ProcessTerminalSize"; + +/** + * Resize a running PTY-backed `process/spawn` session. + */ +export type ProcessResizePtyParams = { +/** + * Client-supplied, connection-scoped `processHandle` from `process/spawn`. + */ +processHandle: string, +/** + * New PTY size in character cells. + */ +size: ProcessTerminalSize, }; diff --git a/src/generated/app-server/v2/ProcessResizePtyResponse.ts b/src/generated/app-server/v2/ProcessResizePtyResponse.ts new file mode 100644 index 00000000..5d063553 --- /dev/null +++ b/src/generated/app-server/v2/ProcessResizePtyResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Empty success response for `process/resizePty`. + */ +export type ProcessResizePtyResponse = Record; diff --git a/src/generated/app-server/v2/ProcessSpawnParams.ts b/src/generated/app-server/v2/ProcessSpawnParams.ts new file mode 100644 index 00000000..fb09eb58 --- /dev/null +++ b/src/generated/app-server/v2/ProcessSpawnParams.ts @@ -0,0 +1,73 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { ProcessTerminalSize } from "./ProcessTerminalSize"; + +/** + * Spawn a standalone process (argv vector) without a Codex sandbox on the host + * where the app server is running. + * + * `process/spawn` returns after the process has started and the connection-scoped + * `processHandle` has been registered. Process output and exit are reported via + * `process/outputDelta` and `process/exited` notifications. + */ +export type ProcessSpawnParams = { +/** + * Command argv vector. Empty arrays are rejected. + */ +command: Array, +/** + * Client-supplied, connection-scoped process handle. + * + * Duplicate active handles are rejected on the same connection. The same + * handle can be reused after the prior process exits. + */ +processHandle: string, +/** + * Absolute working directory for the process. + */ +cwd: AbsolutePathBuf, +/** + * Enable PTY mode. + * + * This implies `streamStdin` and `streamStdoutStderr`. + */ +tty?: boolean, +/** + * Allow follow-up `process/writeStdin` requests to write stdin bytes. + */ +streamStdin?: boolean, +/** + * Stream stdout/stderr via `process/outputDelta` notifications. + * + * Streamed bytes are not duplicated into the `process/exited` notification. + */ +streamStdoutStderr?: boolean, +/** + * Optional per-stream stdout/stderr capture cap in bytes. + * + * When omitted, the server default applies. Set to `null` to disable the + * cap. + */ +outputBytesCap?: number | null, +/** + * Optional timeout in milliseconds. + * + * When omitted, the server default applies. Set to `null` to disable the + * timeout. + */ +timeoutMs?: number | null, +/** + * Optional environment overrides merged into the app-server process + * environment. + * + * Matching names override inherited values. Set a key to `null` to unset + * an inherited variable. + */ +env?: { [key in string]?: string | null } | null, +/** + * Optional initial PTY size in character cells. Only valid when `tty` is + * true. + */ +size?: ProcessTerminalSize | null, }; diff --git a/src/generated/app-server/v2/ProcessSpawnResponse.ts b/src/generated/app-server/v2/ProcessSpawnResponse.ts new file mode 100644 index 00000000..57b52227 --- /dev/null +++ b/src/generated/app-server/v2/ProcessSpawnResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `process/spawn`. + */ +export type ProcessSpawnResponse = Record; diff --git a/src/generated/app-server/v2/ProcessTerminalSize.ts b/src/generated/app-server/v2/ProcessTerminalSize.ts new file mode 100644 index 00000000..1c4b4670 --- /dev/null +++ b/src/generated/app-server/v2/ProcessTerminalSize.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * PTY size in character cells for `process/spawn` PTY sessions. + */ +export type ProcessTerminalSize = { +/** + * Terminal height in character cells. + */ +rows: number, +/** + * Terminal width in character cells. + */ +cols: number, }; diff --git a/src/generated/app-server/v2/ProcessWriteStdinParams.ts b/src/generated/app-server/v2/ProcessWriteStdinParams.ts new file mode 100644 index 00000000..d27e74a2 --- /dev/null +++ b/src/generated/app-server/v2/ProcessWriteStdinParams.ts @@ -0,0 +1,21 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Write stdin bytes to a running `process/spawn` session, close stdin, or + * both. + */ +export type ProcessWriteStdinParams = { +/** + * Client-supplied, connection-scoped `processHandle` from `process/spawn`. + */ +processHandle: string, +/** + * Optional base64-encoded stdin bytes to write. + */ +deltaBase64?: string | null, +/** + * Close stdin after writing `deltaBase64`, if present. + */ +closeStdin?: boolean, }; diff --git a/src/generated/app-server/v2/ProcessWriteStdinResponse.ts b/src/generated/app-server/v2/ProcessWriteStdinResponse.ts new file mode 100644 index 00000000..29ba8115 --- /dev/null +++ b/src/generated/app-server/v2/ProcessWriteStdinResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Empty success response for `process/writeStdin`. + */ +export type ProcessWriteStdinResponse = Record; diff --git a/src/generated/app-server/v2/ProfileV2.ts b/src/generated/app-server/v2/ProfileV2.ts new file mode 100644 index 00000000..5b667914 --- /dev/null +++ b/src/generated/app-server/v2/ProfileV2.ts @@ -0,0 +1,19 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ReasoningSummary } from "../ReasoningSummary"; +import type { Verbosity } from "../Verbosity"; +import type { WebSearchMode } from "../WebSearchMode"; +import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AskForApproval } from "./AskForApproval"; +import type { ToolsV2 } from "./ToolsV2"; + +export type ProfileV2 = { model: string | null, model_provider: string | null, approval_policy: AskForApproval | null, +/** + * [UNSTABLE] Optional profile-level override for where approval requests + * are routed for review. If omitted, the enclosing config default is + * used. + */ +approvals_reviewer: ApprovalsReviewer | null, service_tier: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, chatgpt_base_url: string | null, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/src/generated/app-server/v2/RateLimitReachedType.ts b/src/generated/app-server/v2/RateLimitReachedType.ts new file mode 100644 index 00000000..78f106c9 --- /dev/null +++ b/src/generated/app-server/v2/RateLimitReachedType.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RateLimitReachedType = "rate_limit_reached" | "workspace_owner_credits_depleted" | "workspace_member_credits_depleted" | "workspace_owner_usage_limit_reached" | "workspace_member_usage_limit_reached"; diff --git a/src/generated/app-server/v2/RateLimitSnapshot.ts b/src/generated/app-server/v2/RateLimitSnapshot.ts new file mode 100644 index 00000000..dc8417a3 --- /dev/null +++ b/src/generated/app-server/v2/RateLimitSnapshot.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PlanType } from "../PlanType"; +import type { CreditsSnapshot } from "./CreditsSnapshot"; +import type { RateLimitReachedType } from "./RateLimitReachedType"; +import type { RateLimitWindow } from "./RateLimitWindow"; + +export type RateLimitSnapshot = { limitId: string | null, limitName: string | null, primary: RateLimitWindow | null, secondary: RateLimitWindow | null, credits: CreditsSnapshot | null, planType: PlanType | null, rateLimitReachedType: RateLimitReachedType | null, }; diff --git a/src/generated/app-server/v2/RateLimitWindow.ts b/src/generated/app-server/v2/RateLimitWindow.ts new file mode 100644 index 00000000..5031f8d9 --- /dev/null +++ b/src/generated/app-server/v2/RateLimitWindow.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RateLimitWindow = { usedPercent: number, windowDurationMins: number | null, resetsAt: number | null, }; diff --git a/src/generated/app-server/v2/RawResponseItemCompletedNotification.ts b/src/generated/app-server/v2/RawResponseItemCompletedNotification.ts new file mode 100644 index 00000000..430c3a06 --- /dev/null +++ b/src/generated/app-server/v2/RawResponseItemCompletedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ResponseItem } from "../ResponseItem"; + +export type RawResponseItemCompletedNotification = { threadId: string, turnId: string, item: ResponseItem, }; diff --git a/src/generated/app-server/v2/ReasoningEffortOption.ts b/src/generated/app-server/v2/ReasoningEffortOption.ts new file mode 100644 index 00000000..ec18adfe --- /dev/null +++ b/src/generated/app-server/v2/ReasoningEffortOption.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ReasoningEffort } from "../ReasoningEffort"; + +export type ReasoningEffortOption = { reasoningEffort: ReasoningEffort, description: string, }; diff --git a/src/generated/app-server/v2/ReasoningSummaryPartAddedNotification.ts b/src/generated/app-server/v2/ReasoningSummaryPartAddedNotification.ts new file mode 100644 index 00000000..35858125 --- /dev/null +++ b/src/generated/app-server/v2/ReasoningSummaryPartAddedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ReasoningSummaryPartAddedNotification = { threadId: string, turnId: string, itemId: string, summaryIndex: number, }; diff --git a/src/generated/app-server/v2/ReasoningSummaryTextDeltaNotification.ts b/src/generated/app-server/v2/ReasoningSummaryTextDeltaNotification.ts new file mode 100644 index 00000000..aa932fa5 --- /dev/null +++ b/src/generated/app-server/v2/ReasoningSummaryTextDeltaNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ReasoningSummaryTextDeltaNotification = { threadId: string, turnId: string, itemId: string, delta: string, summaryIndex: number, }; diff --git a/src/generated/app-server/v2/ReasoningTextDeltaNotification.ts b/src/generated/app-server/v2/ReasoningTextDeltaNotification.ts new file mode 100644 index 00000000..86584ba3 --- /dev/null +++ b/src/generated/app-server/v2/ReasoningTextDeltaNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ReasoningTextDeltaNotification = { threadId: string, turnId: string, itemId: string, delta: string, contentIndex: number, }; diff --git a/src/generated/app-server/v2/RemoteControlClientConnectionAudience.ts b/src/generated/app-server/v2/RemoteControlClientConnectionAudience.ts new file mode 100644 index 00000000..e4d41ff4 --- /dev/null +++ b/src/generated/app-server/v2/RemoteControlClientConnectionAudience.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Audience for a remote-control client connection device-key proof. + */ +export type RemoteControlClientConnectionAudience = "remote_control_client_websocket"; diff --git a/src/generated/app-server/v2/RemoteControlClientEnrollmentAudience.ts b/src/generated/app-server/v2/RemoteControlClientEnrollmentAudience.ts new file mode 100644 index 00000000..b65fb3d1 --- /dev/null +++ b/src/generated/app-server/v2/RemoteControlClientEnrollmentAudience.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Audience for a remote-control client enrollment device-key proof. + */ +export type RemoteControlClientEnrollmentAudience = "remote_control_client_enrollment"; diff --git a/src/generated/app-server/v2/RemoteControlConnectionStatus.ts b/src/generated/app-server/v2/RemoteControlConnectionStatus.ts new file mode 100644 index 00000000..3e6197f5 --- /dev/null +++ b/src/generated/app-server/v2/RemoteControlConnectionStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RemoteControlConnectionStatus = "disabled" | "connecting" | "connected" | "errored"; diff --git a/src/generated/app-server/v2/RemoteControlStatusChangedNotification.ts b/src/generated/app-server/v2/RemoteControlStatusChangedNotification.ts new file mode 100644 index 00000000..16a91385 --- /dev/null +++ b/src/generated/app-server/v2/RemoteControlStatusChangedNotification.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RemoteControlConnectionStatus } from "./RemoteControlConnectionStatus"; + +/** + * Current remote-control connection status and environment id exposed to clients. + */ +export type RemoteControlStatusChangedNotification = { status: RemoteControlConnectionStatus, environmentId: string | null, }; diff --git a/src/generated/app-server/v2/RequestPermissionProfile.ts b/src/generated/app-server/v2/RequestPermissionProfile.ts new file mode 100644 index 00000000..2bf8d8df --- /dev/null +++ b/src/generated/app-server/v2/RequestPermissionProfile.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AdditionalFileSystemPermissions } from "./AdditionalFileSystemPermissions"; +import type { AdditionalNetworkPermissions } from "./AdditionalNetworkPermissions"; + +export type RequestPermissionProfile = { network: AdditionalNetworkPermissions | null, fileSystem: AdditionalFileSystemPermissions | null, }; diff --git a/src/generated/app-server/v2/ResidencyRequirement.ts b/src/generated/app-server/v2/ResidencyRequirement.ts new file mode 100644 index 00000000..1699c84e --- /dev/null +++ b/src/generated/app-server/v2/ResidencyRequirement.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ResidencyRequirement = "us"; diff --git a/src/generated/app-server/v2/ReviewDelivery.ts b/src/generated/app-server/v2/ReviewDelivery.ts new file mode 100644 index 00000000..8fbccd10 --- /dev/null +++ b/src/generated/app-server/v2/ReviewDelivery.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ReviewDelivery = "inline" | "detached"; diff --git a/src/generated/app-server/v2/ReviewStartParams.ts b/src/generated/app-server/v2/ReviewStartParams.ts new file mode 100644 index 00000000..9833e08a --- /dev/null +++ b/src/generated/app-server/v2/ReviewStartParams.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ReviewDelivery } from "./ReviewDelivery"; +import type { ReviewTarget } from "./ReviewTarget"; + +export type ReviewStartParams = { threadId: string, target: ReviewTarget, +/** + * Where to run the review: inline (default) on the current thread or + * detached on a new thread (returned in `reviewThreadId`). + */ +delivery?: ReviewDelivery | null, }; diff --git a/src/generated/app-server/v2/ReviewStartResponse.ts b/src/generated/app-server/v2/ReviewStartResponse.ts new file mode 100644 index 00000000..6d6c2bb7 --- /dev/null +++ b/src/generated/app-server/v2/ReviewStartResponse.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Turn } from "./Turn"; + +export type ReviewStartResponse = { turn: Turn, +/** + * Identifies the thread where the review runs. + * + * For inline reviews, this is the original thread id. + * For detached reviews, this is the id of the new review thread. + */ +reviewThreadId: string, }; diff --git a/src/generated/app-server/v2/ReviewTarget.ts b/src/generated/app-server/v2/ReviewTarget.ts new file mode 100644 index 00000000..a69b68c0 --- /dev/null +++ b/src/generated/app-server/v2/ReviewTarget.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ReviewTarget = { "type": "uncommittedChanges" } | { "type": "baseBranch", branch: string, } | { "type": "commit", sha: string, +/** + * Optional human-readable label (e.g., commit subject) for UIs. + */ +title: string | null, } | { "type": "custom", instructions: string, }; diff --git a/src/generated/app-server/v2/SandboxMode.ts b/src/generated/app-server/v2/SandboxMode.ts new file mode 100644 index 00000000..b8cf4326 --- /dev/null +++ b/src/generated/app-server/v2/SandboxMode.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SandboxMode = "read-only" | "workspace-write" | "danger-full-access"; diff --git a/src/generated/app-server/v2/SandboxPolicy.ts b/src/generated/app-server/v2/SandboxPolicy.ts new file mode 100644 index 00000000..5575701f --- /dev/null +++ b/src/generated/app-server/v2/SandboxPolicy.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { NetworkAccess } from "./NetworkAccess"; + +export type SandboxPolicy = { "type": "dangerFullAccess" } | { "type": "readOnly", networkAccess: boolean, } | { "type": "externalSandbox", networkAccess: NetworkAccess, } | { "type": "workspaceWrite", writableRoots: Array, networkAccess: boolean, excludeTmpdirEnvVar: boolean, excludeSlashTmp: boolean, }; diff --git a/src/generated/app-server/v2/SandboxWorkspaceWrite.ts b/src/generated/app-server/v2/SandboxWorkspaceWrite.ts new file mode 100644 index 00000000..cd19d83f --- /dev/null +++ b/src/generated/app-server/v2/SandboxWorkspaceWrite.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SandboxWorkspaceWrite = { writable_roots: Array, network_access: boolean, exclude_tmpdir_env_var: boolean, exclude_slash_tmp: boolean, }; diff --git a/src/generated/app-server/v2/SendAddCreditsNudgeEmailParams.ts b/src/generated/app-server/v2/SendAddCreditsNudgeEmailParams.ts new file mode 100644 index 00000000..383ad4aa --- /dev/null +++ b/src/generated/app-server/v2/SendAddCreditsNudgeEmailParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AddCreditsNudgeCreditType } from "./AddCreditsNudgeCreditType"; + +export type SendAddCreditsNudgeEmailParams = { creditType: AddCreditsNudgeCreditType, }; diff --git a/src/generated/app-server/v2/SendAddCreditsNudgeEmailResponse.ts b/src/generated/app-server/v2/SendAddCreditsNudgeEmailResponse.ts new file mode 100644 index 00000000..71dcb190 --- /dev/null +++ b/src/generated/app-server/v2/SendAddCreditsNudgeEmailResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AddCreditsNudgeEmailStatus } from "./AddCreditsNudgeEmailStatus"; + +export type SendAddCreditsNudgeEmailResponse = { status: AddCreditsNudgeEmailStatus, }; diff --git a/src/generated/app-server/v2/ServerRequestResolvedNotification.ts b/src/generated/app-server/v2/ServerRequestResolvedNotification.ts new file mode 100644 index 00000000..56c53cc4 --- /dev/null +++ b/src/generated/app-server/v2/ServerRequestResolvedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RequestId } from "../RequestId"; + +export type ServerRequestResolvedNotification = { threadId: string, requestId: RequestId, }; diff --git a/src/generated/app-server/v2/SessionMigration.ts b/src/generated/app-server/v2/SessionMigration.ts new file mode 100644 index 00000000..526af4dd --- /dev/null +++ b/src/generated/app-server/v2/SessionMigration.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SessionMigration = { path: string, cwd: string, title: string | null, }; diff --git a/src/generated/app-server/v2/SessionSource.ts b/src/generated/app-server/v2/SessionSource.ts new file mode 100644 index 00000000..852e6ded --- /dev/null +++ b/src/generated/app-server/v2/SessionSource.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SubAgentSource } from "../SubAgentSource"; + +export type SessionSource = "cli" | "vscode" | "exec" | "appServer" | { "custom": string } | { "subAgent": SubAgentSource } | "unknown"; diff --git a/src/generated/app-server/v2/SkillDependencies.ts b/src/generated/app-server/v2/SkillDependencies.ts new file mode 100644 index 00000000..e2dd4f42 --- /dev/null +++ b/src/generated/app-server/v2/SkillDependencies.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SkillToolDependency } from "./SkillToolDependency"; + +export type SkillDependencies = { tools: Array, }; diff --git a/src/generated/app-server/v2/SkillErrorInfo.ts b/src/generated/app-server/v2/SkillErrorInfo.ts new file mode 100644 index 00000000..6eaf035d --- /dev/null +++ b/src/generated/app-server/v2/SkillErrorInfo.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SkillErrorInfo = { path: string, message: string, }; diff --git a/src/generated/app-server/v2/SkillInterface.ts b/src/generated/app-server/v2/SkillInterface.ts new file mode 100644 index 00000000..2361afcf --- /dev/null +++ b/src/generated/app-server/v2/SkillInterface.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type SkillInterface = { displayName?: string, shortDescription?: string, iconSmall?: AbsolutePathBuf, iconLarge?: AbsolutePathBuf, brandColor?: string, defaultPrompt?: string, }; diff --git a/src/generated/app-server/v2/SkillMetadata.ts b/src/generated/app-server/v2/SkillMetadata.ts new file mode 100644 index 00000000..e43484d1 --- /dev/null +++ b/src/generated/app-server/v2/SkillMetadata.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { SkillDependencies } from "./SkillDependencies"; +import type { SkillInterface } from "./SkillInterface"; +import type { SkillScope } from "./SkillScope"; + +export type SkillMetadata = { name: string, description: string, +/** + * Legacy short_description from SKILL.md. Prefer SKILL.json interface.short_description. + */ +shortDescription?: string, interface?: SkillInterface, dependencies?: SkillDependencies, path: AbsolutePathBuf, scope: SkillScope, enabled: boolean, }; diff --git a/src/generated/app-server/v2/SkillScope.ts b/src/generated/app-server/v2/SkillScope.ts new file mode 100644 index 00000000..997006f5 --- /dev/null +++ b/src/generated/app-server/v2/SkillScope.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SkillScope = "user" | "repo" | "system" | "admin"; diff --git a/src/generated/app-server/v2/SkillSummary.ts b/src/generated/app-server/v2/SkillSummary.ts new file mode 100644 index 00000000..4999a072 --- /dev/null +++ b/src/generated/app-server/v2/SkillSummary.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { SkillInterface } from "./SkillInterface"; + +export type SkillSummary = { name: string, description: string, shortDescription: string | null, interface: SkillInterface | null, path: AbsolutePathBuf | null, enabled: boolean, }; diff --git a/src/generated/app-server/v2/SkillToolDependency.ts b/src/generated/app-server/v2/SkillToolDependency.ts new file mode 100644 index 00000000..a5da45e1 --- /dev/null +++ b/src/generated/app-server/v2/SkillToolDependency.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SkillToolDependency = { type: string, value: string, description?: string, transport?: string, command?: string, url?: string, }; diff --git a/src/generated/app-server/v2/SkillsChangedNotification.ts b/src/generated/app-server/v2/SkillsChangedNotification.ts new file mode 100644 index 00000000..23ed93a5 --- /dev/null +++ b/src/generated/app-server/v2/SkillsChangedNotification.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Notification emitted when watched local skill files change. + * + * Treat this as an invalidation signal and re-run `skills/list` with the + * client's current parameters when refreshed skill metadata is needed. + */ +export type SkillsChangedNotification = Record; diff --git a/src/generated/app-server/v2/SkillsConfigWriteParams.ts b/src/generated/app-server/v2/SkillsConfigWriteParams.ts new file mode 100644 index 00000000..39192e07 --- /dev/null +++ b/src/generated/app-server/v2/SkillsConfigWriteParams.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type SkillsConfigWriteParams = { +/** + * Path-based selector. + */ +path?: AbsolutePathBuf | null, +/** + * Name-based selector. + */ +name?: string | null, enabled: boolean, }; diff --git a/src/generated/app-server/v2/SkillsConfigWriteResponse.ts b/src/generated/app-server/v2/SkillsConfigWriteResponse.ts new file mode 100644 index 00000000..c0e8ef7c --- /dev/null +++ b/src/generated/app-server/v2/SkillsConfigWriteResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SkillsConfigWriteResponse = { effectiveEnabled: boolean, }; diff --git a/src/generated/app-server/v2/SkillsListEntry.ts b/src/generated/app-server/v2/SkillsListEntry.ts new file mode 100644 index 00000000..3f46c98a --- /dev/null +++ b/src/generated/app-server/v2/SkillsListEntry.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SkillErrorInfo } from "./SkillErrorInfo"; +import type { SkillMetadata } from "./SkillMetadata"; + +export type SkillsListEntry = { cwd: string, skills: Array, errors: Array, }; diff --git a/src/generated/app-server/v2/SkillsListExtraRootsForCwd.ts b/src/generated/app-server/v2/SkillsListExtraRootsForCwd.ts new file mode 100644 index 00000000..c18cd4ba --- /dev/null +++ b/src/generated/app-server/v2/SkillsListExtraRootsForCwd.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SkillsListExtraRootsForCwd = { cwd: string, extraUserRoots: Array, }; diff --git a/src/generated/app-server/v2/SkillsListParams.ts b/src/generated/app-server/v2/SkillsListParams.ts new file mode 100644 index 00000000..4adeb38b --- /dev/null +++ b/src/generated/app-server/v2/SkillsListParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SkillsListParams = { +/** + * When empty, defaults to the current session working directory. + */ +cwds?: Array, +/** + * When true, bypass the skills cache and re-scan skills from disk. + */ +forceReload?: boolean, }; diff --git a/src/generated/app-server/v2/SkillsListResponse.ts b/src/generated/app-server/v2/SkillsListResponse.ts new file mode 100644 index 00000000..a27c288a --- /dev/null +++ b/src/generated/app-server/v2/SkillsListResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SkillsListEntry } from "./SkillsListEntry"; + +export type SkillsListResponse = { data: Array, }; diff --git a/src/generated/app-server/v2/SortDirection.ts b/src/generated/app-server/v2/SortDirection.ts new file mode 100644 index 00000000..d8597a46 --- /dev/null +++ b/src/generated/app-server/v2/SortDirection.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SortDirection = "asc" | "desc"; diff --git a/src/generated/app-server/v2/SubagentMigration.ts b/src/generated/app-server/v2/SubagentMigration.ts new file mode 100644 index 00000000..aaf6cf0d --- /dev/null +++ b/src/generated/app-server/v2/SubagentMigration.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SubagentMigration = { name: string, }; diff --git a/src/generated/app-server/v2/TerminalInteractionNotification.ts b/src/generated/app-server/v2/TerminalInteractionNotification.ts new file mode 100644 index 00000000..1631f861 --- /dev/null +++ b/src/generated/app-server/v2/TerminalInteractionNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TerminalInteractionNotification = { threadId: string, turnId: string, itemId: string, processId: string, stdin: string, }; diff --git a/src/generated/app-server/v2/TextElement.ts b/src/generated/app-server/v2/TextElement.ts new file mode 100644 index 00000000..535e0a1d --- /dev/null +++ b/src/generated/app-server/v2/TextElement.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ByteRange } from "./ByteRange"; + +export type TextElement = { +/** + * Byte range in the parent `text` buffer that this element occupies. + */ +byteRange: ByteRange, +/** + * Optional human-readable placeholder for the element, displayed in the UI. + */ +placeholder: string | null, }; diff --git a/src/generated/app-server/v2/TextPosition.ts b/src/generated/app-server/v2/TextPosition.ts new file mode 100644 index 00000000..0e6eeb13 --- /dev/null +++ b/src/generated/app-server/v2/TextPosition.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TextPosition = { +/** + * 1-based line number. + */ +line: number, +/** + * 1-based column number (in Unicode scalar values). + */ +column: number, }; diff --git a/src/generated/app-server/v2/TextRange.ts b/src/generated/app-server/v2/TextRange.ts new file mode 100644 index 00000000..48b68398 --- /dev/null +++ b/src/generated/app-server/v2/TextRange.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { TextPosition } from "./TextPosition"; + +export type TextRange = { start: TextPosition, end: TextPosition, }; diff --git a/src/generated/app-server/v2/Thread.ts b/src/generated/app-server/v2/Thread.ts new file mode 100644 index 00000000..d917094e --- /dev/null +++ b/src/generated/app-server/v2/Thread.ts @@ -0,0 +1,86 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { GitInfo } from "./GitInfo"; +import type { SessionSource } from "./SessionSource"; +import type { ThreadSource } from "./ThreadSource"; +import type { ThreadStatus } from "./ThreadStatus"; +import type { Turn } from "./Turn"; + +export type Thread = { id: string, +/** + * Session id shared by threads that belong to the same session tree. + */ +sessionId: string, +/** + * Source thread id when this thread was created by forking another thread. + */ +forkedFromId: string | null, +/** + * Usually the first user message in the thread, if available. + */ +preview: string, +/** + * Whether the thread is ephemeral and should not be materialized on disk. + */ +ephemeral: boolean, +/** + * Model provider used for this thread (for example, 'openai'). + */ +modelProvider: string, +/** + * Unix timestamp (in seconds) when the thread was created. + */ +createdAt: number, +/** + * Unix timestamp (in seconds) when the thread was last updated. + */ +updatedAt: number, +/** + * Current runtime status for the thread. + */ +status: ThreadStatus, +/** + * [UNSTABLE] Path to the thread on disk. + */ +path: string | null, +/** + * Working directory captured for the thread. + */ +cwd: AbsolutePathBuf, +/** + * Version of the CLI that created the thread. + */ +cliVersion: string, +/** + * Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.). + */ +source: SessionSource, +/** + * Optional analytics source classification for this thread. + */ +threadSource: ThreadSource | null, +/** + * Optional random unique nickname assigned to an AgentControl-spawned sub-agent. + */ +agentNickname: string | null, +/** + * Optional role (agent_role) assigned to an AgentControl-spawned sub-agent. + */ +agentRole: string | null, +/** + * Optional Git metadata captured when the thread was created. + */ +gitInfo: GitInfo | null, +/** + * Optional user-facing thread title. + */ +name: string | null, +/** + * Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` + * (when `includeTurns` is true) responses. + * For all other responses and notifications returning a Thread, + * the turns field will be an empty list. + */ +turns: Array, }; diff --git a/src/generated/app-server/v2/ThreadActiveFlag.ts b/src/generated/app-server/v2/ThreadActiveFlag.ts new file mode 100644 index 00000000..73c875a0 --- /dev/null +++ b/src/generated/app-server/v2/ThreadActiveFlag.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadActiveFlag = "waitingOnApproval" | "waitingOnUserInput"; diff --git a/src/generated/app-server/v2/ThreadApproveGuardianDeniedActionParams.ts b/src/generated/app-server/v2/ThreadApproveGuardianDeniedActionParams.ts new file mode 100644 index 00000000..7d1ab0d8 --- /dev/null +++ b/src/generated/app-server/v2/ThreadApproveGuardianDeniedActionParams.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; + +export type ThreadApproveGuardianDeniedActionParams = { threadId: string, +/** + * Serialized `codex_protocol::protocol::GuardianAssessmentEvent`. + */ +event: JsonValue, }; diff --git a/src/generated/app-server/v2/ThreadApproveGuardianDeniedActionResponse.ts b/src/generated/app-server/v2/ThreadApproveGuardianDeniedActionResponse.ts new file mode 100644 index 00000000..856bb28c --- /dev/null +++ b/src/generated/app-server/v2/ThreadApproveGuardianDeniedActionResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadApproveGuardianDeniedActionResponse = Record; diff --git a/src/generated/app-server/v2/ThreadArchiveParams.ts b/src/generated/app-server/v2/ThreadArchiveParams.ts new file mode 100644 index 00000000..ad4071cb --- /dev/null +++ b/src/generated/app-server/v2/ThreadArchiveParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadArchiveParams = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadArchiveResponse.ts b/src/generated/app-server/v2/ThreadArchiveResponse.ts new file mode 100644 index 00000000..b5954268 --- /dev/null +++ b/src/generated/app-server/v2/ThreadArchiveResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadArchiveResponse = Record; diff --git a/src/generated/app-server/v2/ThreadArchivedNotification.ts b/src/generated/app-server/v2/ThreadArchivedNotification.ts new file mode 100644 index 00000000..cca18907 --- /dev/null +++ b/src/generated/app-server/v2/ThreadArchivedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadArchivedNotification = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadBackgroundTerminalsCleanParams.ts b/src/generated/app-server/v2/ThreadBackgroundTerminalsCleanParams.ts new file mode 100644 index 00000000..750eee87 --- /dev/null +++ b/src/generated/app-server/v2/ThreadBackgroundTerminalsCleanParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadBackgroundTerminalsCleanParams = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadBackgroundTerminalsCleanResponse.ts b/src/generated/app-server/v2/ThreadBackgroundTerminalsCleanResponse.ts new file mode 100644 index 00000000..f531fe0e --- /dev/null +++ b/src/generated/app-server/v2/ThreadBackgroundTerminalsCleanResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadBackgroundTerminalsCleanResponse = Record; diff --git a/src/generated/app-server/v2/ThreadClosedNotification.ts b/src/generated/app-server/v2/ThreadClosedNotification.ts new file mode 100644 index 00000000..ed5bf546 --- /dev/null +++ b/src/generated/app-server/v2/ThreadClosedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadClosedNotification = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadCompactStartParams.ts b/src/generated/app-server/v2/ThreadCompactStartParams.ts new file mode 100644 index 00000000..a60b2c28 --- /dev/null +++ b/src/generated/app-server/v2/ThreadCompactStartParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadCompactStartParams = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadCompactStartResponse.ts b/src/generated/app-server/v2/ThreadCompactStartResponse.ts new file mode 100644 index 00000000..3794feb2 --- /dev/null +++ b/src/generated/app-server/v2/ThreadCompactStartResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadCompactStartResponse = Record; diff --git a/src/generated/app-server/v2/ThreadDecrementElicitationParams.ts b/src/generated/app-server/v2/ThreadDecrementElicitationParams.ts new file mode 100644 index 00000000..08156500 --- /dev/null +++ b/src/generated/app-server/v2/ThreadDecrementElicitationParams.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Parameters for `thread/decrement_elicitation`. + */ +export type ThreadDecrementElicitationParams = { +/** + * Thread whose out-of-band elicitation counter should be decremented. + */ +threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadDecrementElicitationResponse.ts b/src/generated/app-server/v2/ThreadDecrementElicitationResponse.ts new file mode 100644 index 00000000..d61f67ee --- /dev/null +++ b/src/generated/app-server/v2/ThreadDecrementElicitationResponse.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Response for `thread/decrement_elicitation`. + */ +export type ThreadDecrementElicitationResponse = { +/** + * Current out-of-band elicitation count after the decrement. + */ +count: bigint, +/** + * Whether timeout accounting remains paused after applying the decrement. + */ +paused: boolean, }; diff --git a/src/generated/app-server/v2/ThreadForkParams.ts b/src/generated/app-server/v2/ThreadForkParams.ts new file mode 100644 index 00000000..1db4835e --- /dev/null +++ b/src/generated/app-server/v2/ThreadForkParams.ts @@ -0,0 +1,56 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AskForApproval } from "./AskForApproval"; +import type { PermissionProfileSelectionParams } from "./PermissionProfileSelectionParams"; +import type { SandboxMode } from "./SandboxMode"; +import type { ThreadSource } from "./ThreadSource"; + +/** + * There are two ways to fork a thread: + * 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. + * 2. By path: load the thread from disk by path and fork it into a new thread. + * + * If using path, the thread_id param will be ignored. + * + * Prefer using thread_id whenever possible. + */ +export type ThreadForkParams = { threadId: string, +/** + * [UNSTABLE] Specify the rollout path to fork from. + * If specified, the thread_id param will be ignored. + */ +path?: string | null, +/** + * Configuration overrides for the forked thread, if any. + */ +model?: string | null, modelProvider?: string | null, serviceTier?: string | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, +/** + * Override where approval requests are routed for review on this thread + * and subsequent turns. + */ +approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, +/** + * Named profile selection for the forked thread. Cannot be combined with + * `sandbox`. Use bounded `modifications` for supported thread + * adjustments instead of replacing the full permissions profile. + */ +permissions?: PermissionProfileSelectionParams | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, ephemeral?: boolean, +/** + * Optional client-supplied analytics source classification for this forked thread. + */ +threadSource?: ThreadSource | null, +/** + * When true, return only thread metadata and live fork state without + * populating `thread.turns`. This is useful when the client plans to call + * `thread/turns/list` immediately after forking. + */ +excludeTurns?: boolean, +/** + * Deprecated and ignored by app-server. Kept only so older clients can + * continue sending the field while rollout persistence always uses the + * limited history policy. + */ +persistExtendedHistory: boolean, }; diff --git a/src/generated/app-server/v2/ThreadForkResponse.ts b/src/generated/app-server/v2/ThreadForkResponse.ts new file mode 100644 index 00000000..aa810e26 --- /dev/null +++ b/src/generated/app-server/v2/ThreadForkResponse.ts @@ -0,0 +1,37 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ActivePermissionProfile } from "./ActivePermissionProfile"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AskForApproval } from "./AskForApproval"; +import type { PermissionProfile } from "./PermissionProfile"; +import type { SandboxPolicy } from "./SandboxPolicy"; +import type { Thread } from "./Thread"; + +export type ThreadForkResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: string | null, cwd: AbsolutePathBuf, +/** + * Instruction source files currently loaded for this thread. + */ +instructionSources: Array, approvalPolicy: AskForApproval, +/** + * Reviewer currently used for approval requests on this thread. + */ +approvalsReviewer: ApprovalsReviewer, +/** + * Legacy sandbox policy retained for compatibility. Experimental clients + * should prefer `permissionProfile` when they need exact runtime + * permissions. + */ +sandbox: SandboxPolicy, +/** + * Full active permissions for this thread. `activePermissionProfile` + * carries display/provenance metadata for this runtime profile. + */ +permissionProfile: PermissionProfile | null, +/** + * Named or implicit built-in profile that produced the active + * permissions, when known. + */ +activePermissionProfile: ActivePermissionProfile | null, reasoningEffort: ReasoningEffort | null, }; diff --git a/src/generated/app-server/v2/ThreadGoal.ts b/src/generated/app-server/v2/ThreadGoal.ts new file mode 100644 index 00000000..c6873232 --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoal.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadGoalStatus } from "./ThreadGoalStatus"; + +export type ThreadGoal = { threadId: string, objective: string, status: ThreadGoalStatus, tokenBudget: number | null, tokensUsed: number, timeUsedSeconds: number, createdAt: number, updatedAt: number, }; diff --git a/src/generated/app-server/v2/ThreadGoalClearParams.ts b/src/generated/app-server/v2/ThreadGoalClearParams.ts new file mode 100644 index 00000000..efefc253 --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoalClearParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadGoalClearParams = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadGoalClearResponse.ts b/src/generated/app-server/v2/ThreadGoalClearResponse.ts new file mode 100644 index 00000000..882176df --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoalClearResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadGoalClearResponse = { cleared: boolean, }; diff --git a/src/generated/app-server/v2/ThreadGoalClearedNotification.ts b/src/generated/app-server/v2/ThreadGoalClearedNotification.ts new file mode 100644 index 00000000..e8e5a8b6 --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoalClearedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadGoalClearedNotification = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadGoalGetParams.ts b/src/generated/app-server/v2/ThreadGoalGetParams.ts new file mode 100644 index 00000000..59f0006c --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoalGetParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadGoalGetParams = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadGoalGetResponse.ts b/src/generated/app-server/v2/ThreadGoalGetResponse.ts new file mode 100644 index 00000000..fa2b6089 --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoalGetResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadGoal } from "./ThreadGoal"; + +export type ThreadGoalGetResponse = { goal: ThreadGoal | null, }; diff --git a/src/generated/app-server/v2/ThreadGoalSetParams.ts b/src/generated/app-server/v2/ThreadGoalSetParams.ts new file mode 100644 index 00000000..b92720c1 --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoalSetParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadGoalStatus } from "./ThreadGoalStatus"; + +export type ThreadGoalSetParams = { threadId: string, objective?: string | null, status?: ThreadGoalStatus | null, tokenBudget?: number | null, }; diff --git a/src/generated/app-server/v2/ThreadGoalSetResponse.ts b/src/generated/app-server/v2/ThreadGoalSetResponse.ts new file mode 100644 index 00000000..0f57130c --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoalSetResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadGoal } from "./ThreadGoal"; + +export type ThreadGoalSetResponse = { goal: ThreadGoal, }; diff --git a/src/generated/app-server/v2/ThreadGoalStatus.ts b/src/generated/app-server/v2/ThreadGoalStatus.ts new file mode 100644 index 00000000..7a4bf332 --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoalStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadGoalStatus = "active" | "paused" | "budgetLimited" | "complete"; diff --git a/src/generated/app-server/v2/ThreadGoalUpdatedNotification.ts b/src/generated/app-server/v2/ThreadGoalUpdatedNotification.ts new file mode 100644 index 00000000..c9972afa --- /dev/null +++ b/src/generated/app-server/v2/ThreadGoalUpdatedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadGoal } from "./ThreadGoal"; + +export type ThreadGoalUpdatedNotification = { threadId: string, turnId: string | null, goal: ThreadGoal, }; diff --git a/src/generated/app-server/v2/ThreadIncrementElicitationParams.ts b/src/generated/app-server/v2/ThreadIncrementElicitationParams.ts new file mode 100644 index 00000000..94fc390d --- /dev/null +++ b/src/generated/app-server/v2/ThreadIncrementElicitationParams.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Parameters for `thread/increment_elicitation`. + */ +export type ThreadIncrementElicitationParams = { +/** + * Thread whose out-of-band elicitation counter should be incremented. + */ +threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadIncrementElicitationResponse.ts b/src/generated/app-server/v2/ThreadIncrementElicitationResponse.ts new file mode 100644 index 00000000..863ba329 --- /dev/null +++ b/src/generated/app-server/v2/ThreadIncrementElicitationResponse.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Response for `thread/increment_elicitation`. + */ +export type ThreadIncrementElicitationResponse = { +/** + * Current out-of-band elicitation count after the increment. + */ +count: bigint, +/** + * Whether timeout accounting is paused after applying the increment. + */ +paused: boolean, }; diff --git a/src/generated/app-server/v2/ThreadInjectItemsParams.ts b/src/generated/app-server/v2/ThreadInjectItemsParams.ts new file mode 100644 index 00000000..4a49224a --- /dev/null +++ b/src/generated/app-server/v2/ThreadInjectItemsParams.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; + +export type ThreadInjectItemsParams = { threadId: string, +/** + * Raw Responses API items to append to the thread's model-visible history. + */ +items: Array, }; diff --git a/src/generated/app-server/v2/ThreadInjectItemsResponse.ts b/src/generated/app-server/v2/ThreadInjectItemsResponse.ts new file mode 100644 index 00000000..60dcf0d0 --- /dev/null +++ b/src/generated/app-server/v2/ThreadInjectItemsResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadInjectItemsResponse = Record; diff --git a/src/generated/app-server/v2/ThreadItem.ts b/src/generated/app-server/v2/ThreadItem.ts new file mode 100644 index 00000000..f7880c9d --- /dev/null +++ b/src/generated/app-server/v2/ThreadItem.ts @@ -0,0 +1,101 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { MessagePhase } from "../MessagePhase"; +import type { ReasoningEffort } from "../ReasoningEffort"; +import type { JsonValue } from "../serde_json/JsonValue"; +import type { CollabAgentState } from "./CollabAgentState"; +import type { CollabAgentTool } from "./CollabAgentTool"; +import type { CollabAgentToolCallStatus } from "./CollabAgentToolCallStatus"; +import type { CommandAction } from "./CommandAction"; +import type { CommandExecutionSource } from "./CommandExecutionSource"; +import type { CommandExecutionStatus } from "./CommandExecutionStatus"; +import type { DynamicToolCallOutputContentItem } from "./DynamicToolCallOutputContentItem"; +import type { DynamicToolCallStatus } from "./DynamicToolCallStatus"; +import type { FileUpdateChange } from "./FileUpdateChange"; +import type { HookPromptFragment } from "./HookPromptFragment"; +import type { McpToolCallError } from "./McpToolCallError"; +import type { McpToolCallResult } from "./McpToolCallResult"; +import type { McpToolCallStatus } from "./McpToolCallStatus"; +import type { MemoryCitation } from "./MemoryCitation"; +import type { PatchApplyStatus } from "./PatchApplyStatus"; +import type { UserInput } from "./UserInput"; +import type { WebSearchAction } from "./WebSearchAction"; + +export type ThreadItem = { "type": "userMessage", id: string, content: Array, } | { "type": "hookPrompt", id: string, fragments: Array, } | { "type": "agentMessage", id: string, text: string, phase: MessagePhase | null, memoryCitation: MemoryCitation | null, } | { "type": "plan", id: string, text: string, } | { "type": "reasoning", id: string, summary: Array, content: Array, } | { "type": "commandExecution", id: string, +/** + * The command to be executed. + */ +command: string, +/** + * The command's working directory. + */ +cwd: AbsolutePathBuf, +/** + * Identifier for the underlying PTY process (when available). + */ +processId: string | null, source: CommandExecutionSource, status: CommandExecutionStatus, +/** + * A best-effort parsing of the command to understand the action(s) it will perform. + * This returns a list of CommandAction objects because a single shell command may + * be composed of many commands piped together. + */ +commandActions: Array, +/** + * The command's output, aggregated from stdout and stderr. + */ +aggregatedOutput: string | null, +/** + * The command's exit code. + */ +exitCode: number | null, +/** + * The duration of the command execution in milliseconds. + */ +durationMs: number | null, } | { "type": "fileChange", id: string, changes: Array, status: PatchApplyStatus, } | { "type": "mcpToolCall", id: string, server: string, tool: string, status: McpToolCallStatus, arguments: JsonValue, mcpAppResourceUri?: string, result: McpToolCallResult | null, error: McpToolCallError | null, +/** + * The duration of the MCP tool call in milliseconds. + */ +durationMs: number | null, } | { "type": "dynamicToolCall", id: string, namespace: string | null, tool: string, arguments: JsonValue, status: DynamicToolCallStatus, contentItems: Array | null, success: boolean | null, +/** + * The duration of the dynamic tool call in milliseconds. + */ +durationMs: number | null, } | { "type": "collabAgentToolCall", +/** + * Unique identifier for this collab tool call. + */ +id: string, +/** + * Name of the collab tool that was invoked. + */ +tool: CollabAgentTool, +/** + * Current status of the collab tool call. + */ +status: CollabAgentToolCallStatus, +/** + * Thread ID of the agent issuing the collab request. + */ +senderThreadId: string, +/** + * Thread ID of the receiving agent, when applicable. In case of spawn operation, + * this corresponds to the newly spawned agent. + */ +receiverThreadIds: Array, +/** + * Prompt text sent as part of the collab tool call, when available. + */ +prompt: string | null, +/** + * Model requested for the spawned agent, when applicable. + */ +model: string | null, +/** + * Reasoning effort requested for the spawned agent, when applicable. + */ +reasoningEffort: ReasoningEffort | null, +/** + * Last known status of the target agents, when available. + */ +agentsStates: { [key in string]?: CollabAgentState }, } | { "type": "webSearch", id: string, query: string, action: WebSearchAction | null, } | { "type": "imageView", id: string, path: AbsolutePathBuf, } | { "type": "imageGeneration", id: string, status: string, revisedPrompt: string | null, result: string, savedPath?: AbsolutePathBuf, } | { "type": "enteredReviewMode", id: string, review: string, } | { "type": "exitedReviewMode", id: string, review: string, } | { "type": "contextCompaction", id: string, }; diff --git a/src/generated/app-server/v2/ThreadListParams.ts b/src/generated/app-server/v2/ThreadListParams.ts new file mode 100644 index 00000000..ce5b6a79 --- /dev/null +++ b/src/generated/app-server/v2/ThreadListParams.ts @@ -0,0 +1,54 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SortDirection } from "./SortDirection"; +import type { ThreadSortKey } from "./ThreadSortKey"; +import type { ThreadSourceKind } from "./ThreadSourceKind"; + +export type ThreadListParams = { +/** + * Opaque pagination cursor returned by a previous call. + */ +cursor?: string | null, +/** + * Optional page size; defaults to a reasonable server-side value. + */ +limit?: number | null, +/** + * Optional sort key; defaults to created_at. + */ +sortKey?: ThreadSortKey | null, +/** + * Optional sort direction; defaults to descending (newest first). + */ +sortDirection?: SortDirection | null, +/** + * Optional provider filter; when set, only sessions recorded under these + * providers are returned. When present but empty, includes all providers. + */ +modelProviders?: Array | null, +/** + * Optional source filter; when set, only sessions from these source kinds + * are returned. When omitted or empty, defaults to interactive sources. + */ +sourceKinds?: Array | null, +/** + * Optional archived filter; when set to true, only archived threads are returned. + * If false or null, only non-archived threads are returned. + */ +archived?: boolean | null, +/** + * Optional cwd filter or filters; when set, only threads whose session cwd + * exactly matches one of these paths are returned. + */ +cwd?: string | Array | null, +/** + * If true, return from the state DB without scanning JSONL rollouts to + * repair thread metadata. Omitted or false preserves scan-and-repair + * behavior. + */ +useStateDbOnly?: boolean, +/** + * Optional substring filter for the extracted thread title. + */ +searchTerm?: string | null, }; diff --git a/src/generated/app-server/v2/ThreadListResponse.ts b/src/generated/app-server/v2/ThreadListResponse.ts new file mode 100644 index 00000000..51757e24 --- /dev/null +++ b/src/generated/app-server/v2/ThreadListResponse.ts @@ -0,0 +1,18 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Thread } from "./Thread"; + +export type ThreadListResponse = { data: Array, +/** + * Opaque cursor to pass to the next call to continue after the last item. + * if None, there are no more items to return. + */ +nextCursor: string | null, +/** + * Opaque cursor to pass as `cursor` when reversing `sortDirection`. + * This is only populated when the page contains at least one thread. + * Use it with the opposite `sortDirection`; for timestamp sorts it anchors + * at the start of the page timestamp so same-second updates are not skipped. + */ +backwardsCursor: string | null, }; diff --git a/src/generated/app-server/v2/ThreadLoadedListParams.ts b/src/generated/app-server/v2/ThreadLoadedListParams.ts new file mode 100644 index 00000000..a7889e4f --- /dev/null +++ b/src/generated/app-server/v2/ThreadLoadedListParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadLoadedListParams = { +/** + * Opaque pagination cursor returned by a previous call. + */ +cursor?: string | null, +/** + * Optional page size; defaults to no limit. + */ +limit?: number | null, }; diff --git a/src/generated/app-server/v2/ThreadLoadedListResponse.ts b/src/generated/app-server/v2/ThreadLoadedListResponse.ts new file mode 100644 index 00000000..21a48c37 --- /dev/null +++ b/src/generated/app-server/v2/ThreadLoadedListResponse.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadLoadedListResponse = { +/** + * Thread ids for sessions currently loaded in memory. + */ +data: Array, +/** + * Opaque cursor to pass to the next call to continue after the last item. + * if None, there are no more items to return. + */ +nextCursor: string | null, }; diff --git a/src/generated/app-server/v2/ThreadMemoryModeSetParams.ts b/src/generated/app-server/v2/ThreadMemoryModeSetParams.ts new file mode 100644 index 00000000..676edf2c --- /dev/null +++ b/src/generated/app-server/v2/ThreadMemoryModeSetParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadMemoryMode } from "../ThreadMemoryMode"; + +export type ThreadMemoryModeSetParams = { threadId: string, mode: ThreadMemoryMode, }; diff --git a/src/generated/app-server/v2/ThreadMemoryModeSetResponse.ts b/src/generated/app-server/v2/ThreadMemoryModeSetResponse.ts new file mode 100644 index 00000000..49b42fd9 --- /dev/null +++ b/src/generated/app-server/v2/ThreadMemoryModeSetResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadMemoryModeSetResponse = Record; diff --git a/src/generated/app-server/v2/ThreadMetadataGitInfoUpdateParams.ts b/src/generated/app-server/v2/ThreadMetadataGitInfoUpdateParams.ts new file mode 100644 index 00000000..865b5346 --- /dev/null +++ b/src/generated/app-server/v2/ThreadMetadataGitInfoUpdateParams.ts @@ -0,0 +1,20 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadMetadataGitInfoUpdateParams = { +/** + * Omit to leave the stored commit unchanged, set to `null` to clear it, + * or provide a non-empty string to replace it. + */ +sha?: string | null, +/** + * Omit to leave the stored branch unchanged, set to `null` to clear it, + * or provide a non-empty string to replace it. + */ +branch?: string | null, +/** + * Omit to leave the stored origin URL unchanged, set to `null` to clear it, + * or provide a non-empty string to replace it. + */ +originUrl?: string | null, }; diff --git a/src/generated/app-server/v2/ThreadMetadataUpdateParams.ts b/src/generated/app-server/v2/ThreadMetadataUpdateParams.ts new file mode 100644 index 00000000..bec4bc12 --- /dev/null +++ b/src/generated/app-server/v2/ThreadMetadataUpdateParams.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadMetadataGitInfoUpdateParams } from "./ThreadMetadataGitInfoUpdateParams"; + +export type ThreadMetadataUpdateParams = { threadId: string, +/** + * Patch the stored Git metadata for this thread. + * Omit a field to leave it unchanged, set it to `null` to clear it, or + * provide a string to replace the stored value. + */ +gitInfo?: ThreadMetadataGitInfoUpdateParams | null, }; diff --git a/src/generated/app-server/v2/ThreadMetadataUpdateResponse.ts b/src/generated/app-server/v2/ThreadMetadataUpdateResponse.ts new file mode 100644 index 00000000..d9c09ef2 --- /dev/null +++ b/src/generated/app-server/v2/ThreadMetadataUpdateResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Thread } from "./Thread"; + +export type ThreadMetadataUpdateResponse = { thread: Thread, }; diff --git a/src/generated/app-server/v2/ThreadNameUpdatedNotification.ts b/src/generated/app-server/v2/ThreadNameUpdatedNotification.ts new file mode 100644 index 00000000..c944b5aa --- /dev/null +++ b/src/generated/app-server/v2/ThreadNameUpdatedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadNameUpdatedNotification = { threadId: string, threadName?: string, }; diff --git a/src/generated/app-server/v2/ThreadReadParams.ts b/src/generated/app-server/v2/ThreadReadParams.ts new file mode 100644 index 00000000..64169d2b --- /dev/null +++ b/src/generated/app-server/v2/ThreadReadParams.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadReadParams = { threadId: string, +/** + * When true, include turns and their items from rollout history. + */ +includeTurns: boolean, }; diff --git a/src/generated/app-server/v2/ThreadReadResponse.ts b/src/generated/app-server/v2/ThreadReadResponse.ts new file mode 100644 index 00000000..a6da5064 --- /dev/null +++ b/src/generated/app-server/v2/ThreadReadResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Thread } from "./Thread"; + +export type ThreadReadResponse = { thread: Thread, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeAppendAudioParams.ts b/src/generated/app-server/v2/ThreadRealtimeAppendAudioParams.ts new file mode 100644 index 00000000..9de0c2bc --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeAppendAudioParams.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadRealtimeAudioChunk } from "./ThreadRealtimeAudioChunk"; + +/** + * EXPERIMENTAL - append audio input to thread realtime. + */ +export type ThreadRealtimeAppendAudioParams = { threadId: string, audio: ThreadRealtimeAudioChunk, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeAppendAudioResponse.ts b/src/generated/app-server/v2/ThreadRealtimeAppendAudioResponse.ts new file mode 100644 index 00000000..063e8cba --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeAppendAudioResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - response for appending realtime audio input. + */ +export type ThreadRealtimeAppendAudioResponse = Record; diff --git a/src/generated/app-server/v2/ThreadRealtimeAppendTextParams.ts b/src/generated/app-server/v2/ThreadRealtimeAppendTextParams.ts new file mode 100644 index 00000000..6e3bef4e --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeAppendTextParams.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - append text input to thread realtime. + */ +export type ThreadRealtimeAppendTextParams = { threadId: string, text: string, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeAppendTextResponse.ts b/src/generated/app-server/v2/ThreadRealtimeAppendTextResponse.ts new file mode 100644 index 00000000..1fb9f073 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeAppendTextResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - response for appending realtime text input. + */ +export type ThreadRealtimeAppendTextResponse = Record; diff --git a/src/generated/app-server/v2/ThreadRealtimeAudioChunk.ts b/src/generated/app-server/v2/ThreadRealtimeAudioChunk.ts new file mode 100644 index 00000000..eefb79dd --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeAudioChunk.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - thread realtime audio chunk. + */ +export type ThreadRealtimeAudioChunk = { data: string, sampleRate: number, numChannels: number, samplesPerChannel: number | null, itemId: string | null, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeClosedNotification.ts b/src/generated/app-server/v2/ThreadRealtimeClosedNotification.ts new file mode 100644 index 00000000..a39cd71e --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeClosedNotification.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - emitted when thread realtime transport closes. + */ +export type ThreadRealtimeClosedNotification = { threadId: string, reason: string | null, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeErrorNotification.ts b/src/generated/app-server/v2/ThreadRealtimeErrorNotification.ts new file mode 100644 index 00000000..0b24879e --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeErrorNotification.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - emitted when thread realtime encounters an error. + */ +export type ThreadRealtimeErrorNotification = { threadId: string, message: string, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeItemAddedNotification.ts b/src/generated/app-server/v2/ThreadRealtimeItemAddedNotification.ts new file mode 100644 index 00000000..f996e77c --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeItemAddedNotification.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; + +/** + * EXPERIMENTAL - raw non-audio thread realtime item emitted by the backend. + */ +export type ThreadRealtimeItemAddedNotification = { threadId: string, item: JsonValue, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeListVoicesParams.ts b/src/generated/app-server/v2/ThreadRealtimeListVoicesParams.ts new file mode 100644 index 00000000..b456d89c --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeListVoicesParams.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - list voices supported by thread realtime. + */ +export type ThreadRealtimeListVoicesParams = Record; diff --git a/src/generated/app-server/v2/ThreadRealtimeListVoicesResponse.ts b/src/generated/app-server/v2/ThreadRealtimeListVoicesResponse.ts new file mode 100644 index 00000000..272cbadd --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeListVoicesResponse.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RealtimeVoicesList } from "../RealtimeVoicesList"; + +/** + * EXPERIMENTAL - response for listing supported realtime voices. + */ +export type ThreadRealtimeListVoicesResponse = { voices: RealtimeVoicesList, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeOutputAudioDeltaNotification.ts b/src/generated/app-server/v2/ThreadRealtimeOutputAudioDeltaNotification.ts new file mode 100644 index 00000000..1d03fd89 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeOutputAudioDeltaNotification.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadRealtimeAudioChunk } from "./ThreadRealtimeAudioChunk"; + +/** + * EXPERIMENTAL - streamed output audio emitted by thread realtime. + */ +export type ThreadRealtimeOutputAudioDeltaNotification = { threadId: string, audio: ThreadRealtimeAudioChunk, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeSdpNotification.ts b/src/generated/app-server/v2/ThreadRealtimeSdpNotification.ts new file mode 100644 index 00000000..16a7fd18 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeSdpNotification.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - emitted with the remote SDP for a WebRTC realtime session. + */ +export type ThreadRealtimeSdpNotification = { threadId: string, sdp: string, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeStartParams.ts b/src/generated/app-server/v2/ThreadRealtimeStartParams.ts new file mode 100644 index 00000000..1a149164 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeStartParams.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RealtimeOutputModality } from "../RealtimeOutputModality"; +import type { RealtimeVoice } from "../RealtimeVoice"; +import type { ThreadRealtimeStartTransport } from "./ThreadRealtimeStartTransport"; + +/** + * EXPERIMENTAL - start a thread-scoped realtime session. + */ +export type ThreadRealtimeStartParams = { threadId: string, +/** + * Selects text or audio output for the realtime session. Transport and voice stay + * independent so clients can choose how they connect separately from what the model emits. + */ +outputModality: RealtimeOutputModality, prompt?: string | null | null, realtimeSessionId?: string | null, transport?: ThreadRealtimeStartTransport | null, voice?: RealtimeVoice | null, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeStartResponse.ts b/src/generated/app-server/v2/ThreadRealtimeStartResponse.ts new file mode 100644 index 00000000..56254564 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeStartResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - response for starting thread realtime. + */ +export type ThreadRealtimeStartResponse = Record; diff --git a/src/generated/app-server/v2/ThreadRealtimeStartTransport.ts b/src/generated/app-server/v2/ThreadRealtimeStartTransport.ts new file mode 100644 index 00000000..339e1b1b --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeStartTransport.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - transport used by thread realtime. + */ +export type ThreadRealtimeStartTransport = { "type": "websocket" } | { "type": "webrtc", +/** + * SDP offer generated by a WebRTC RTCPeerConnection after configuring audio and the + * realtime events data channel. + */ +sdp: string, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeStartedNotification.ts b/src/generated/app-server/v2/ThreadRealtimeStartedNotification.ts new file mode 100644 index 00000000..56763777 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeStartedNotification.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RealtimeConversationVersion } from "../RealtimeConversationVersion"; + +/** + * EXPERIMENTAL - emitted when thread realtime startup is accepted. + */ +export type ThreadRealtimeStartedNotification = { threadId: string, realtimeSessionId: string | null, version: RealtimeConversationVersion, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeStopParams.ts b/src/generated/app-server/v2/ThreadRealtimeStopParams.ts new file mode 100644 index 00000000..b9adbff6 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeStopParams.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - stop thread realtime. + */ +export type ThreadRealtimeStopParams = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeStopResponse.ts b/src/generated/app-server/v2/ThreadRealtimeStopResponse.ts new file mode 100644 index 00000000..c87f4402 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeStopResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - response for stopping thread realtime. + */ +export type ThreadRealtimeStopResponse = Record; diff --git a/src/generated/app-server/v2/ThreadRealtimeTranscriptDeltaNotification.ts b/src/generated/app-server/v2/ThreadRealtimeTranscriptDeltaNotification.ts new file mode 100644 index 00000000..805eeddd --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeTranscriptDeltaNotification.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - flat transcript delta emitted whenever realtime + * transcript text changes. + */ +export type ThreadRealtimeTranscriptDeltaNotification = { threadId: string, role: string, +/** + * Live transcript delta from the realtime event. + */ +delta: string, }; diff --git a/src/generated/app-server/v2/ThreadRealtimeTranscriptDoneNotification.ts b/src/generated/app-server/v2/ThreadRealtimeTranscriptDoneNotification.ts new file mode 100644 index 00000000..d4667ad0 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRealtimeTranscriptDoneNotification.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL - final transcript text emitted when realtime completes + * a transcript part. + */ +export type ThreadRealtimeTranscriptDoneNotification = { threadId: string, role: string, +/** + * Final complete text for the transcript part. + */ +text: string, }; diff --git a/src/generated/app-server/v2/ThreadResumeParams.ts b/src/generated/app-server/v2/ThreadResumeParams.ts new file mode 100644 index 00000000..24c6eca7 --- /dev/null +++ b/src/generated/app-server/v2/ThreadResumeParams.ts @@ -0,0 +1,61 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Personality } from "../Personality"; +import type { ResponseItem } from "../ResponseItem"; +import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AskForApproval } from "./AskForApproval"; +import type { PermissionProfileSelectionParams } from "./PermissionProfileSelectionParams"; +import type { SandboxMode } from "./SandboxMode"; + +/** + * There are three ways to resume a thread: + * 1. By thread_id: load the thread from disk by thread_id and resume it. + * 2. By history: instantiate the thread from memory and resume it. + * 3. By path: load the thread from disk by path and resume it. + * + * The precedence is: history > path > thread_id. + * If using history or path, the thread_id param will be ignored. + * + * Prefer using thread_id whenever possible. + */ +export type ThreadResumeParams = { threadId: string, +/** + * [UNSTABLE] FOR CODEX CLOUD - DO NOT USE. + * If specified, the thread will be resumed with the provided history + * instead of loaded from disk. + */ +history?: Array | null, +/** + * [UNSTABLE] Specify the rollout path to resume from. + * If specified, the thread_id param will be ignored. + */ +path?: string | null, +/** + * Configuration overrides for the resumed thread, if any. + */ +model?: string | null, modelProvider?: string | null, serviceTier?: string | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, +/** + * Override where approval requests are routed for review on this thread + * and subsequent turns. + */ +approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, +/** + * Named profile selection for the resumed thread. Cannot be combined + * with `sandbox`. Use bounded `modifications` for supported thread + * adjustments instead of replacing the full permissions profile. + */ +permissions?: PermissionProfileSelectionParams | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, +/** + * When true, return only thread metadata and live-resume state without + * populating `thread.turns`. This is useful when the client plans to call + * `thread/turns/list` immediately after resuming. + */ +excludeTurns?: boolean, +/** + * Deprecated and ignored by app-server. Kept only so older clients can + * continue sending the field while rollout persistence always uses the + * limited history policy. + */ +persistExtendedHistory: boolean, }; diff --git a/src/generated/app-server/v2/ThreadResumeResponse.ts b/src/generated/app-server/v2/ThreadResumeResponse.ts new file mode 100644 index 00000000..faf673ec --- /dev/null +++ b/src/generated/app-server/v2/ThreadResumeResponse.ts @@ -0,0 +1,37 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ActivePermissionProfile } from "./ActivePermissionProfile"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AskForApproval } from "./AskForApproval"; +import type { PermissionProfile } from "./PermissionProfile"; +import type { SandboxPolicy } from "./SandboxPolicy"; +import type { Thread } from "./Thread"; + +export type ThreadResumeResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: string | null, cwd: AbsolutePathBuf, +/** + * Instruction source files currently loaded for this thread. + */ +instructionSources: Array, approvalPolicy: AskForApproval, +/** + * Reviewer currently used for approval requests on this thread. + */ +approvalsReviewer: ApprovalsReviewer, +/** + * Legacy sandbox policy retained for compatibility. Experimental clients + * should prefer `permissionProfile` when they need exact runtime + * permissions. + */ +sandbox: SandboxPolicy, +/** + * Full active permissions for this thread. `activePermissionProfile` + * carries display/provenance metadata for this runtime profile. + */ +permissionProfile: PermissionProfile | null, +/** + * Named or implicit built-in profile that produced the active + * permissions, when known. + */ +activePermissionProfile: ActivePermissionProfile | null, reasoningEffort: ReasoningEffort | null, }; diff --git a/src/generated/app-server/v2/ThreadRollbackParams.ts b/src/generated/app-server/v2/ThreadRollbackParams.ts new file mode 100644 index 00000000..1c938e3b --- /dev/null +++ b/src/generated/app-server/v2/ThreadRollbackParams.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadRollbackParams = { threadId: string, +/** + * The number of turns to drop from the end of the thread. Must be >= 1. + * + * This only modifies the thread's history and does not revert local file changes + * that have been made by the agent. Clients are responsible for reverting these changes. + */ +numTurns: number, }; diff --git a/src/generated/app-server/v2/ThreadRollbackResponse.ts b/src/generated/app-server/v2/ThreadRollbackResponse.ts new file mode 100644 index 00000000..6597cc81 --- /dev/null +++ b/src/generated/app-server/v2/ThreadRollbackResponse.ts @@ -0,0 +1,14 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Thread } from "./Thread"; + +export type ThreadRollbackResponse = { +/** + * The updated thread after applying the rollback, with `turns` populated. + * + * The ThreadItems stored in each Turn are lossy since we explicitly do not + * persist all agent interactions, such as command executions. This is the same + * behavior as `thread/resume`. + */ +thread: Thread, }; diff --git a/src/generated/app-server/v2/ThreadSetNameParams.ts b/src/generated/app-server/v2/ThreadSetNameParams.ts new file mode 100644 index 00000000..82b9b3a1 --- /dev/null +++ b/src/generated/app-server/v2/ThreadSetNameParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadSetNameParams = { threadId: string, name: string, }; diff --git a/src/generated/app-server/v2/ThreadSetNameResponse.ts b/src/generated/app-server/v2/ThreadSetNameResponse.ts new file mode 100644 index 00000000..09143d25 --- /dev/null +++ b/src/generated/app-server/v2/ThreadSetNameResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadSetNameResponse = Record; diff --git a/src/generated/app-server/v2/ThreadShellCommandParams.ts b/src/generated/app-server/v2/ThreadShellCommandParams.ts new file mode 100644 index 00000000..2761dee2 --- /dev/null +++ b/src/generated/app-server/v2/ThreadShellCommandParams.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadShellCommandParams = { threadId: string, +/** + * Shell command string evaluated by the thread's configured shell. + * Unlike `command/exec`, this intentionally preserves shell syntax + * such as pipes, redirects, and quoting. This runs unsandboxed with full + * access rather than inheriting the thread sandbox policy. + */ +command: string, }; diff --git a/src/generated/app-server/v2/ThreadShellCommandResponse.ts b/src/generated/app-server/v2/ThreadShellCommandResponse.ts new file mode 100644 index 00000000..9c54b458 --- /dev/null +++ b/src/generated/app-server/v2/ThreadShellCommandResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadShellCommandResponse = Record; diff --git a/src/generated/app-server/v2/ThreadSortKey.ts b/src/generated/app-server/v2/ThreadSortKey.ts new file mode 100644 index 00000000..dbf1b6c4 --- /dev/null +++ b/src/generated/app-server/v2/ThreadSortKey.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadSortKey = "created_at" | "updated_at"; diff --git a/src/generated/app-server/v2/ThreadSource.ts b/src/generated/app-server/v2/ThreadSource.ts new file mode 100644 index 00000000..8f555248 --- /dev/null +++ b/src/generated/app-server/v2/ThreadSource.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadSource = "user" | "subagent" | "memory_consolidation"; diff --git a/src/generated/app-server/v2/ThreadSourceKind.ts b/src/generated/app-server/v2/ThreadSourceKind.ts new file mode 100644 index 00000000..0a464e3d --- /dev/null +++ b/src/generated/app-server/v2/ThreadSourceKind.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadSourceKind = "cli" | "vscode" | "exec" | "appServer" | "subAgent" | "subAgentReview" | "subAgentCompact" | "subAgentThreadSpawn" | "subAgentOther" | "unknown"; diff --git a/src/generated/app-server/v2/ThreadStartParams.ts b/src/generated/app-server/v2/ThreadStartParams.ts new file mode 100644 index 00000000..b38e069b --- /dev/null +++ b/src/generated/app-server/v2/ThreadStartParams.ts @@ -0,0 +1,55 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Personality } from "../Personality"; +import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AskForApproval } from "./AskForApproval"; +import type { DynamicToolSpec } from "./DynamicToolSpec"; +import type { PermissionProfileSelectionParams } from "./PermissionProfileSelectionParams"; +import type { SandboxMode } from "./SandboxMode"; +import type { ThreadSource } from "./ThreadSource"; +import type { ThreadStartSource } from "./ThreadStartSource"; +import type { TurnEnvironmentParams } from "./TurnEnvironmentParams"; + +export type ThreadStartParams = { model?: string | null, modelProvider?: string | null, serviceTier?: string | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, +/** + * Override where approval requests are routed for review on this thread + * and subsequent turns. + */ +approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, +/** + * Named profile selection for this thread. Cannot be combined with + * `sandbox`. Use bounded `modifications` for supported turn/thread + * adjustments instead of replacing the full permissions profile. + */ +permissions?: PermissionProfileSelectionParams | null, config?: { [key in string]?: JsonValue } | null, serviceName?: string | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, sessionStartSource?: ThreadStartSource | null, +/** + * Optional client-supplied analytics source classification for this thread. + */ +threadSource?: ThreadSource | null, +/** + * Optional sticky environments for this thread. + * + * Omitted selects the default environment when environment access is + * enabled. Empty disables environment access for turns that do not + * provide a turn override. Non-empty selects the first environment as the + * current turn environment. + */ +environments?: Array | null, dynamicTools?: Array | null, +/** + * Test-only experimental field used to validate experimental gating and + * schema filtering behavior in a stable way. + */ +mockExperimentalField?: string | null, +/** + * If true, opt into emitting raw Responses API items on the event stream. + * This is for internal use only (e.g. Codex Cloud). + */ +experimentalRawEvents: boolean, +/** + * Deprecated and ignored by app-server. Kept only so older clients can + * continue sending the field while rollout persistence always uses the + * limited history policy. + */ +persistExtendedHistory: boolean, }; diff --git a/src/generated/app-server/v2/ThreadStartResponse.ts b/src/generated/app-server/v2/ThreadStartResponse.ts new file mode 100644 index 00000000..803e3804 --- /dev/null +++ b/src/generated/app-server/v2/ThreadStartResponse.ts @@ -0,0 +1,37 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ActivePermissionProfile } from "./ActivePermissionProfile"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AskForApproval } from "./AskForApproval"; +import type { PermissionProfile } from "./PermissionProfile"; +import type { SandboxPolicy } from "./SandboxPolicy"; +import type { Thread } from "./Thread"; + +export type ThreadStartResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: string | null, cwd: AbsolutePathBuf, +/** + * Instruction source files currently loaded for this thread. + */ +instructionSources: Array, approvalPolicy: AskForApproval, +/** + * Reviewer currently used for approval requests on this thread. + */ +approvalsReviewer: ApprovalsReviewer, +/** + * Legacy sandbox policy retained for compatibility. Experimental clients + * should prefer `permissionProfile` when they need exact runtime + * permissions. + */ +sandbox: SandboxPolicy, +/** + * Full active permissions for this thread. `activePermissionProfile` + * carries display/provenance metadata for this runtime profile. + */ +permissionProfile: PermissionProfile | null, +/** + * Named or implicit built-in profile that produced the active + * permissions, when known. + */ +activePermissionProfile: ActivePermissionProfile | null, reasoningEffort: ReasoningEffort | null, }; diff --git a/src/generated/app-server/v2/ThreadStartSource.ts b/src/generated/app-server/v2/ThreadStartSource.ts new file mode 100644 index 00000000..ea1b839c --- /dev/null +++ b/src/generated/app-server/v2/ThreadStartSource.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadStartSource = "startup" | "clear"; diff --git a/src/generated/app-server/v2/ThreadStartedNotification.ts b/src/generated/app-server/v2/ThreadStartedNotification.ts new file mode 100644 index 00000000..83be5577 --- /dev/null +++ b/src/generated/app-server/v2/ThreadStartedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Thread } from "./Thread"; + +export type ThreadStartedNotification = { thread: Thread, }; diff --git a/src/generated/app-server/v2/ThreadStatus.ts b/src/generated/app-server/v2/ThreadStatus.ts new file mode 100644 index 00000000..7cc6c8a6 --- /dev/null +++ b/src/generated/app-server/v2/ThreadStatus.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadActiveFlag } from "./ThreadActiveFlag"; + +export type ThreadStatus = { "type": "notLoaded" } | { "type": "idle" } | { "type": "systemError" } | { "type": "active", activeFlags: Array, }; diff --git a/src/generated/app-server/v2/ThreadStatusChangedNotification.ts b/src/generated/app-server/v2/ThreadStatusChangedNotification.ts new file mode 100644 index 00000000..3242c892 --- /dev/null +++ b/src/generated/app-server/v2/ThreadStatusChangedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadStatus } from "./ThreadStatus"; + +export type ThreadStatusChangedNotification = { threadId: string, status: ThreadStatus, }; diff --git a/src/generated/app-server/v2/ThreadTokenUsage.ts b/src/generated/app-server/v2/ThreadTokenUsage.ts new file mode 100644 index 00000000..b452c408 --- /dev/null +++ b/src/generated/app-server/v2/ThreadTokenUsage.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { TokenUsageBreakdown } from "./TokenUsageBreakdown"; + +export type ThreadTokenUsage = { total: TokenUsageBreakdown, last: TokenUsageBreakdown, modelContextWindow: number | null, }; diff --git a/src/generated/app-server/v2/ThreadTokenUsageUpdatedNotification.ts b/src/generated/app-server/v2/ThreadTokenUsageUpdatedNotification.ts new file mode 100644 index 00000000..1be28250 --- /dev/null +++ b/src/generated/app-server/v2/ThreadTokenUsageUpdatedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadTokenUsage } from "./ThreadTokenUsage"; + +export type ThreadTokenUsageUpdatedNotification = { threadId: string, turnId: string, tokenUsage: ThreadTokenUsage, }; diff --git a/src/generated/app-server/v2/ThreadTurnsItemsListParams.ts b/src/generated/app-server/v2/ThreadTurnsItemsListParams.ts new file mode 100644 index 00000000..68c506a6 --- /dev/null +++ b/src/generated/app-server/v2/ThreadTurnsItemsListParams.ts @@ -0,0 +1,18 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SortDirection } from "./SortDirection"; + +export type ThreadTurnsItemsListParams = { threadId: string, turnId: string, +/** + * Opaque cursor to pass to the next call to continue after the last item. + */ +cursor?: string | null, +/** + * Optional item page size. + */ +limit?: number | null, +/** + * Optional item pagination direction; defaults to ascending. + */ +sortDirection?: SortDirection | null, }; diff --git a/src/generated/app-server/v2/ThreadTurnsItemsListResponse.ts b/src/generated/app-server/v2/ThreadTurnsItemsListResponse.ts new file mode 100644 index 00000000..4375e29a --- /dev/null +++ b/src/generated/app-server/v2/ThreadTurnsItemsListResponse.ts @@ -0,0 +1,16 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadItem } from "./ThreadItem"; + +export type ThreadTurnsItemsListResponse = { data: Array, +/** + * Opaque cursor to pass to the next call to continue after the last item. + * if None, there are no more items to return. + */ +nextCursor: string | null, +/** + * Opaque cursor to pass as `cursor` when reversing `sortDirection`. + * This is only populated when the page contains at least one item. + */ +backwardsCursor: string | null, }; diff --git a/src/generated/app-server/v2/ThreadTurnsListParams.ts b/src/generated/app-server/v2/ThreadTurnsListParams.ts new file mode 100644 index 00000000..a1aa682a --- /dev/null +++ b/src/generated/app-server/v2/ThreadTurnsListParams.ts @@ -0,0 +1,23 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SortDirection } from "./SortDirection"; +import type { TurnItemsView } from "./TurnItemsView"; + +export type ThreadTurnsListParams = { threadId: string, +/** + * Opaque cursor to pass to the next call to continue after the last turn. + */ +cursor?: string | null, +/** + * Optional turn page size. + */ +limit?: number | null, +/** + * Optional turn pagination direction; defaults to descending. + */ +sortDirection?: SortDirection | null, +/** + * How much item detail to include for each returned turn; defaults to summary. + */ +itemsView?: TurnItemsView | null, }; diff --git a/src/generated/app-server/v2/ThreadTurnsListResponse.ts b/src/generated/app-server/v2/ThreadTurnsListResponse.ts new file mode 100644 index 00000000..1dbed91a --- /dev/null +++ b/src/generated/app-server/v2/ThreadTurnsListResponse.ts @@ -0,0 +1,18 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Turn } from "./Turn"; + +export type ThreadTurnsListResponse = { data: Array, +/** + * Opaque cursor to pass to the next call to continue after the last turn. + * if None, there are no more turns to return. + */ +nextCursor: string | null, +/** + * Opaque cursor to pass as `cursor` when reversing `sortDirection`. + * This is only populated when the page contains at least one turn. + * Use it with the opposite `sortDirection` to include the anchor turn again + * and catch updates to that turn. + */ +backwardsCursor: string | null, }; diff --git a/src/generated/app-server/v2/ThreadUnarchiveParams.ts b/src/generated/app-server/v2/ThreadUnarchiveParams.ts new file mode 100644 index 00000000..4e464989 --- /dev/null +++ b/src/generated/app-server/v2/ThreadUnarchiveParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadUnarchiveParams = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadUnarchiveResponse.ts b/src/generated/app-server/v2/ThreadUnarchiveResponse.ts new file mode 100644 index 00000000..96ea5dcd --- /dev/null +++ b/src/generated/app-server/v2/ThreadUnarchiveResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Thread } from "./Thread"; + +export type ThreadUnarchiveResponse = { thread: Thread, }; diff --git a/src/generated/app-server/v2/ThreadUnarchivedNotification.ts b/src/generated/app-server/v2/ThreadUnarchivedNotification.ts new file mode 100644 index 00000000..e2c16171 --- /dev/null +++ b/src/generated/app-server/v2/ThreadUnarchivedNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadUnarchivedNotification = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadUnsubscribeParams.ts b/src/generated/app-server/v2/ThreadUnsubscribeParams.ts new file mode 100644 index 00000000..3d5f3a04 --- /dev/null +++ b/src/generated/app-server/v2/ThreadUnsubscribeParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadUnsubscribeParams = { threadId: string, }; diff --git a/src/generated/app-server/v2/ThreadUnsubscribeResponse.ts b/src/generated/app-server/v2/ThreadUnsubscribeResponse.ts new file mode 100644 index 00000000..6f8f66b2 --- /dev/null +++ b/src/generated/app-server/v2/ThreadUnsubscribeResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadUnsubscribeStatus } from "./ThreadUnsubscribeStatus"; + +export type ThreadUnsubscribeResponse = { status: ThreadUnsubscribeStatus, }; diff --git a/src/generated/app-server/v2/ThreadUnsubscribeStatus.ts b/src/generated/app-server/v2/ThreadUnsubscribeStatus.ts new file mode 100644 index 00000000..2970598d --- /dev/null +++ b/src/generated/app-server/v2/ThreadUnsubscribeStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadUnsubscribeStatus = "notLoaded" | "notSubscribed" | "unsubscribed"; diff --git a/src/generated/app-server/v2/TokenUsageBreakdown.ts b/src/generated/app-server/v2/TokenUsageBreakdown.ts new file mode 100644 index 00000000..1d4e408f --- /dev/null +++ b/src/generated/app-server/v2/TokenUsageBreakdown.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TokenUsageBreakdown = { totalTokens: number, inputTokens: number, cachedInputTokens: number, outputTokens: number, reasoningOutputTokens: number, }; diff --git a/src/generated/app-server/v2/ToolRequestUserInputAnswer.ts b/src/generated/app-server/v2/ToolRequestUserInputAnswer.ts new file mode 100644 index 00000000..0c912db0 --- /dev/null +++ b/src/generated/app-server/v2/ToolRequestUserInputAnswer.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL. Captures a user's answer to a request_user_input question. + */ +export type ToolRequestUserInputAnswer = { answers: Array, }; diff --git a/src/generated/app-server/v2/ToolRequestUserInputOption.ts b/src/generated/app-server/v2/ToolRequestUserInputOption.ts new file mode 100644 index 00000000..ab21aca0 --- /dev/null +++ b/src/generated/app-server/v2/ToolRequestUserInputOption.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * EXPERIMENTAL. Defines a single selectable option for request_user_input. + */ +export type ToolRequestUserInputOption = { label: string, description: string, }; diff --git a/src/generated/app-server/v2/ToolRequestUserInputParams.ts b/src/generated/app-server/v2/ToolRequestUserInputParams.ts new file mode 100644 index 00000000..bee81cb8 --- /dev/null +++ b/src/generated/app-server/v2/ToolRequestUserInputParams.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ToolRequestUserInputQuestion } from "./ToolRequestUserInputQuestion"; + +/** + * EXPERIMENTAL. Params sent with a request_user_input event. + */ +export type ToolRequestUserInputParams = { threadId: string, turnId: string, itemId: string, questions: Array, }; diff --git a/src/generated/app-server/v2/ToolRequestUserInputQuestion.ts b/src/generated/app-server/v2/ToolRequestUserInputQuestion.ts new file mode 100644 index 00000000..1afc4e47 --- /dev/null +++ b/src/generated/app-server/v2/ToolRequestUserInputQuestion.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ToolRequestUserInputOption } from "./ToolRequestUserInputOption"; + +/** + * EXPERIMENTAL. Represents one request_user_input question and its required options. + */ +export type ToolRequestUserInputQuestion = { id: string, header: string, question: string, isOther: boolean, isSecret: boolean, options: Array | null, }; diff --git a/src/generated/app-server/v2/ToolRequestUserInputResponse.ts b/src/generated/app-server/v2/ToolRequestUserInputResponse.ts new file mode 100644 index 00000000..e4dd8bbc --- /dev/null +++ b/src/generated/app-server/v2/ToolRequestUserInputResponse.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ToolRequestUserInputAnswer } from "./ToolRequestUserInputAnswer"; + +/** + * EXPERIMENTAL. Response payload mapping question ids to answers. + */ +export type ToolRequestUserInputResponse = { answers: { [key in string]?: ToolRequestUserInputAnswer }, }; diff --git a/src/generated/app-server/v2/ToolsV2.ts b/src/generated/app-server/v2/ToolsV2.ts new file mode 100644 index 00000000..784991f0 --- /dev/null +++ b/src/generated/app-server/v2/ToolsV2.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { WebSearchToolConfig } from "../WebSearchToolConfig"; + +export type ToolsV2 = { web_search: WebSearchToolConfig | null, view_image: boolean | null, }; diff --git a/src/generated/app-server/v2/Turn.ts b/src/generated/app-server/v2/Turn.ts new file mode 100644 index 00000000..6505ec34 --- /dev/null +++ b/src/generated/app-server/v2/Turn.ts @@ -0,0 +1,33 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ThreadItem } from "./ThreadItem"; +import type { TurnError } from "./TurnError"; +import type { TurnItemsView } from "./TurnItemsView"; +import type { TurnStatus } from "./TurnStatus"; + +export type Turn = { id: string, +/** + * Thread items currently included in this turn payload. + */ +items: Array, +/** + * Describes how much of `items` has been loaded for this turn. + */ +itemsView: TurnItemsView, status: TurnStatus, +/** + * Only populated when the Turn's status is failed. + */ +error: TurnError | null, +/** + * Unix timestamp (in seconds) when the turn started. + */ +startedAt: number | null, +/** + * Unix timestamp (in seconds) when the turn completed. + */ +completedAt: number | null, +/** + * Duration between turn start and completion in milliseconds, if known. + */ +durationMs: number | null, }; diff --git a/src/generated/app-server/v2/TurnCompletedNotification.ts b/src/generated/app-server/v2/TurnCompletedNotification.ts new file mode 100644 index 00000000..e1b151bf --- /dev/null +++ b/src/generated/app-server/v2/TurnCompletedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Turn } from "./Turn"; + +export type TurnCompletedNotification = { threadId: string, turn: Turn, }; diff --git a/src/generated/app-server/v2/TurnDiffUpdatedNotification.ts b/src/generated/app-server/v2/TurnDiffUpdatedNotification.ts new file mode 100644 index 00000000..ec2b3334 --- /dev/null +++ b/src/generated/app-server/v2/TurnDiffUpdatedNotification.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Notification that the turn-level unified diff has changed. + * Contains the latest aggregated diff across all file changes in the turn. + */ +export type TurnDiffUpdatedNotification = { threadId: string, turnId: string, diff: string, }; diff --git a/src/generated/app-server/v2/TurnEnvironmentParams.ts b/src/generated/app-server/v2/TurnEnvironmentParams.ts new file mode 100644 index 00000000..bb981b0a --- /dev/null +++ b/src/generated/app-server/v2/TurnEnvironmentParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type TurnEnvironmentParams = { environmentId: string, cwd: AbsolutePathBuf, }; diff --git a/src/generated/app-server/v2/TurnError.ts b/src/generated/app-server/v2/TurnError.ts new file mode 100644 index 00000000..765a8e05 --- /dev/null +++ b/src/generated/app-server/v2/TurnError.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CodexErrorInfo } from "./CodexErrorInfo"; + +export type TurnError = { message: string, codexErrorInfo: CodexErrorInfo | null, additionalDetails: string | null, }; diff --git a/src/generated/app-server/v2/TurnInterruptParams.ts b/src/generated/app-server/v2/TurnInterruptParams.ts new file mode 100644 index 00000000..ec35689e --- /dev/null +++ b/src/generated/app-server/v2/TurnInterruptParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TurnInterruptParams = { threadId: string, turnId: string, }; diff --git a/src/generated/app-server/v2/TurnInterruptResponse.ts b/src/generated/app-server/v2/TurnInterruptResponse.ts new file mode 100644 index 00000000..7ce6e35b --- /dev/null +++ b/src/generated/app-server/v2/TurnInterruptResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TurnInterruptResponse = Record; diff --git a/src/generated/app-server/v2/TurnItemsView.ts b/src/generated/app-server/v2/TurnItemsView.ts new file mode 100644 index 00000000..90569230 --- /dev/null +++ b/src/generated/app-server/v2/TurnItemsView.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TurnItemsView = "notLoaded" | "summary" | "full"; diff --git a/src/generated/app-server/v2/TurnPlanStep.ts b/src/generated/app-server/v2/TurnPlanStep.ts new file mode 100644 index 00000000..22d1fbb6 --- /dev/null +++ b/src/generated/app-server/v2/TurnPlanStep.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { TurnPlanStepStatus } from "./TurnPlanStepStatus"; + +export type TurnPlanStep = { step: string, status: TurnPlanStepStatus, }; diff --git a/src/generated/app-server/v2/TurnPlanStepStatus.ts b/src/generated/app-server/v2/TurnPlanStepStatus.ts new file mode 100644 index 00000000..f6733a68 --- /dev/null +++ b/src/generated/app-server/v2/TurnPlanStepStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TurnPlanStepStatus = "pending" | "inProgress" | "completed"; diff --git a/src/generated/app-server/v2/TurnPlanUpdatedNotification.ts b/src/generated/app-server/v2/TurnPlanUpdatedNotification.ts new file mode 100644 index 00000000..ed13cb4a --- /dev/null +++ b/src/generated/app-server/v2/TurnPlanUpdatedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { TurnPlanStep } from "./TurnPlanStep"; + +export type TurnPlanUpdatedNotification = { threadId: string, turnId: string, explanation: string | null, plan: Array, }; diff --git a/src/generated/app-server/v2/TurnStartParams.ts b/src/generated/app-server/v2/TurnStartParams.ts new file mode 100644 index 00000000..ec8692bb --- /dev/null +++ b/src/generated/app-server/v2/TurnStartParams.ts @@ -0,0 +1,85 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CollaborationMode } from "../CollaborationMode"; +import type { Personality } from "../Personality"; +import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ReasoningSummary } from "../ReasoningSummary"; +import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; +import type { AskForApproval } from "./AskForApproval"; +import type { PermissionProfileSelectionParams } from "./PermissionProfileSelectionParams"; +import type { SandboxPolicy } from "./SandboxPolicy"; +import type { TurnEnvironmentParams } from "./TurnEnvironmentParams"; +import type { UserInput } from "./UserInput"; + +export type TurnStartParams = { threadId: string, input: Array, +/** + * Optional turn-scoped Responses API client metadata. + */ +responsesapiClientMetadata?: { [key in string]?: string } | null, +/** + * Optional turn-scoped environments. + * + * Omitted uses the thread sticky environments. Empty disables + * environment access for this turn. Non-empty selects the first + * environment as the current turn environment for this turn. + */ +environments?: Array | null, +/** + * Override the working directory for this turn and subsequent turns. + */ +cwd?: string | null, +/** + * Override the approval policy for this turn and subsequent turns. + */ +approvalPolicy?: AskForApproval | null, +/** + * Override where approval requests are routed for review on this turn and + * subsequent turns. + */ +approvalsReviewer?: ApprovalsReviewer | null, +/** + * Override the sandbox policy for this turn and subsequent turns. + */ +sandboxPolicy?: SandboxPolicy | null, +/** + * Select a named permissions profile for this turn and subsequent turns. + * Cannot be combined with `sandboxPolicy`. Use bounded `modifications` + * for supported turn adjustments instead of replacing the full + * permissions profile. + */ +permissions?: PermissionProfileSelectionParams | null, +/** + * Override the model for this turn and subsequent turns. + */ +model?: string | null, +/** + * Override the service tier for this turn and subsequent turns. + */ +serviceTier?: string | null | null, +/** + * Override the reasoning effort for this turn and subsequent turns. + */ +effort?: ReasoningEffort | null, +/** + * Override the reasoning summary for this turn and subsequent turns. + */ +summary?: ReasoningSummary | null, +/** + * Override the personality for this turn and subsequent turns. + */ +personality?: Personality | null, +/** + * Optional JSON Schema used to constrain the final assistant message for + * this turn. + */ +outputSchema?: JsonValue | null, +/** + * EXPERIMENTAL - Set a pre-set collaboration mode. + * Takes precedence over model, reasoning_effort, and developer instructions if set. + * + * For `collaboration_mode.settings.developer_instructions`, `null` means + * "use the built-in instructions for the selected mode". + */ +collaborationMode?: CollaborationMode | null, }; diff --git a/src/generated/app-server/v2/TurnStartResponse.ts b/src/generated/app-server/v2/TurnStartResponse.ts new file mode 100644 index 00000000..cc2ee377 --- /dev/null +++ b/src/generated/app-server/v2/TurnStartResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Turn } from "./Turn"; + +export type TurnStartResponse = { turn: Turn, }; diff --git a/src/generated/app-server/v2/TurnStartedNotification.ts b/src/generated/app-server/v2/TurnStartedNotification.ts new file mode 100644 index 00000000..34f71b24 --- /dev/null +++ b/src/generated/app-server/v2/TurnStartedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Turn } from "./Turn"; + +export type TurnStartedNotification = { threadId: string, turn: Turn, }; diff --git a/src/generated/app-server/v2/TurnStatus.ts b/src/generated/app-server/v2/TurnStatus.ts new file mode 100644 index 00000000..476922ed --- /dev/null +++ b/src/generated/app-server/v2/TurnStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TurnStatus = "completed" | "interrupted" | "failed" | "inProgress"; diff --git a/src/generated/app-server/v2/TurnSteerParams.ts b/src/generated/app-server/v2/TurnSteerParams.ts new file mode 100644 index 00000000..0361b5c3 --- /dev/null +++ b/src/generated/app-server/v2/TurnSteerParams.ts @@ -0,0 +1,15 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { UserInput } from "./UserInput"; + +export type TurnSteerParams = { threadId: string, input: Array, +/** + * Optional turn-scoped Responses API client metadata. + */ +responsesapiClientMetadata?: { [key in string]?: string } | null, +/** + * Required active turn id precondition. The request fails when it does not + * match the currently active turn. + */ +expectedTurnId: string, }; diff --git a/src/generated/app-server/v2/TurnSteerResponse.ts b/src/generated/app-server/v2/TurnSteerResponse.ts new file mode 100644 index 00000000..390adb4f --- /dev/null +++ b/src/generated/app-server/v2/TurnSteerResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TurnSteerResponse = { turnId: string, }; diff --git a/src/generated/app-server/v2/UserInput.ts b/src/generated/app-server/v2/UserInput.ts new file mode 100644 index 00000000..38abc233 --- /dev/null +++ b/src/generated/app-server/v2/UserInput.ts @@ -0,0 +1,10 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { TextElement } from "./TextElement"; + +export type UserInput = { "type": "text", text: string, +/** + * UI-defined spans within `text` used to render or persist special elements. + */ +text_elements: Array, } | { "type": "image", url: string, } | { "type": "localImage", path: string, } | { "type": "skill", name: string, path: string, } | { "type": "mention", name: string, path: string, }; diff --git a/src/generated/app-server/v2/WarningNotification.ts b/src/generated/app-server/v2/WarningNotification.ts new file mode 100644 index 00000000..bd3433be --- /dev/null +++ b/src/generated/app-server/v2/WarningNotification.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WarningNotification = { +/** + * Optional thread target when the warning applies to a specific thread. + */ +threadId: string | null, +/** + * Concise warning message for the user. + */ +message: string, }; diff --git a/src/generated/app-server/v2/WebSearchAction.ts b/src/generated/app-server/v2/WebSearchAction.ts new file mode 100644 index 00000000..309bff45 --- /dev/null +++ b/src/generated/app-server/v2/WebSearchAction.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WebSearchAction = { "type": "search", query: string | null, queries: Array | null, } | { "type": "openPage", url: string | null, } | { "type": "findInPage", url: string | null, pattern: string | null, } | { "type": "other" }; diff --git a/src/generated/app-server/v2/WindowsSandboxReadiness.ts b/src/generated/app-server/v2/WindowsSandboxReadiness.ts new file mode 100644 index 00000000..41b1161a --- /dev/null +++ b/src/generated/app-server/v2/WindowsSandboxReadiness.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WindowsSandboxReadiness = "ready" | "notConfigured" | "updateRequired"; diff --git a/src/generated/app-server/v2/WindowsSandboxReadinessResponse.ts b/src/generated/app-server/v2/WindowsSandboxReadinessResponse.ts new file mode 100644 index 00000000..bc42a1d9 --- /dev/null +++ b/src/generated/app-server/v2/WindowsSandboxReadinessResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { WindowsSandboxReadiness } from "./WindowsSandboxReadiness"; + +export type WindowsSandboxReadinessResponse = { status: WindowsSandboxReadiness, }; diff --git a/src/generated/app-server/v2/WindowsSandboxSetupCompletedNotification.ts b/src/generated/app-server/v2/WindowsSandboxSetupCompletedNotification.ts new file mode 100644 index 00000000..d4c0b6cf --- /dev/null +++ b/src/generated/app-server/v2/WindowsSandboxSetupCompletedNotification.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { WindowsSandboxSetupMode } from "./WindowsSandboxSetupMode"; + +export type WindowsSandboxSetupCompletedNotification = { mode: WindowsSandboxSetupMode, success: boolean, error: string | null, }; diff --git a/src/generated/app-server/v2/WindowsSandboxSetupMode.ts b/src/generated/app-server/v2/WindowsSandboxSetupMode.ts new file mode 100644 index 00000000..a74bea42 --- /dev/null +++ b/src/generated/app-server/v2/WindowsSandboxSetupMode.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WindowsSandboxSetupMode = "elevated" | "unelevated"; diff --git a/src/generated/app-server/v2/WindowsSandboxSetupStartParams.ts b/src/generated/app-server/v2/WindowsSandboxSetupStartParams.ts new file mode 100644 index 00000000..596c9f5b --- /dev/null +++ b/src/generated/app-server/v2/WindowsSandboxSetupStartParams.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { WindowsSandboxSetupMode } from "./WindowsSandboxSetupMode"; + +export type WindowsSandboxSetupStartParams = { mode: WindowsSandboxSetupMode, cwd?: AbsolutePathBuf | null, }; diff --git a/src/generated/app-server/v2/WindowsSandboxSetupStartResponse.ts b/src/generated/app-server/v2/WindowsSandboxSetupStartResponse.ts new file mode 100644 index 00000000..a1900494 --- /dev/null +++ b/src/generated/app-server/v2/WindowsSandboxSetupStartResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WindowsSandboxSetupStartResponse = { started: boolean, }; diff --git a/src/generated/app-server/v2/WindowsWorldWritableWarningNotification.ts b/src/generated/app-server/v2/WindowsWorldWritableWarningNotification.ts new file mode 100644 index 00000000..a11e7cef --- /dev/null +++ b/src/generated/app-server/v2/WindowsWorldWritableWarningNotification.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WindowsWorldWritableWarningNotification = { samplePaths: Array, extraCount: number, failedScan: boolean, }; diff --git a/src/generated/app-server/v2/WriteStatus.ts b/src/generated/app-server/v2/WriteStatus.ts new file mode 100644 index 00000000..068eb3bd --- /dev/null +++ b/src/generated/app-server/v2/WriteStatus.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type WriteStatus = "ok" | "okOverridden"; diff --git a/src/generated/app-server/v2/index.ts b/src/generated/app-server/v2/index.ts new file mode 100644 index 00000000..d60e3b68 --- /dev/null +++ b/src/generated/app-server/v2/index.ts @@ -0,0 +1,500 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +export type { Account } from "./Account"; +export type { AccountLoginCompletedNotification } from "./AccountLoginCompletedNotification"; +export type { AccountRateLimitsUpdatedNotification } from "./AccountRateLimitsUpdatedNotification"; +export type { AccountUpdatedNotification } from "./AccountUpdatedNotification"; +export type { ActivePermissionProfile } from "./ActivePermissionProfile"; +export type { ActivePermissionProfileModification } from "./ActivePermissionProfileModification"; +export type { AddCreditsNudgeCreditType } from "./AddCreditsNudgeCreditType"; +export type { AddCreditsNudgeEmailStatus } from "./AddCreditsNudgeEmailStatus"; +export type { AdditionalFileSystemPermissions } from "./AdditionalFileSystemPermissions"; +export type { AdditionalNetworkPermissions } from "./AdditionalNetworkPermissions"; +export type { AdditionalPermissionProfile } from "./AdditionalPermissionProfile"; +export type { AgentMessageDeltaNotification } from "./AgentMessageDeltaNotification"; +export type { AnalyticsConfig } from "./AnalyticsConfig"; +export type { AppBranding } from "./AppBranding"; +export type { AppInfo } from "./AppInfo"; +export type { AppListUpdatedNotification } from "./AppListUpdatedNotification"; +export type { AppMetadata } from "./AppMetadata"; +export type { AppReview } from "./AppReview"; +export type { AppScreenshot } from "./AppScreenshot"; +export type { AppSummary } from "./AppSummary"; +export type { AppToolApproval } from "./AppToolApproval"; +export type { AppToolsConfig } from "./AppToolsConfig"; +export type { ApprovalsReviewer } from "./ApprovalsReviewer"; +export type { AppsConfig } from "./AppsConfig"; +export type { AppsDefaultConfig } from "./AppsDefaultConfig"; +export type { AppsListParams } from "./AppsListParams"; +export type { AppsListResponse } from "./AppsListResponse"; +export type { AskForApproval } from "./AskForApproval"; +export type { AutoReviewDecisionSource } from "./AutoReviewDecisionSource"; +export type { ByteRange } from "./ByteRange"; +export type { CancelLoginAccountParams } from "./CancelLoginAccountParams"; +export type { CancelLoginAccountResponse } from "./CancelLoginAccountResponse"; +export type { CancelLoginAccountStatus } from "./CancelLoginAccountStatus"; +export type { ChatgptAuthTokensRefreshParams } from "./ChatgptAuthTokensRefreshParams"; +export type { ChatgptAuthTokensRefreshReason } from "./ChatgptAuthTokensRefreshReason"; +export type { ChatgptAuthTokensRefreshResponse } from "./ChatgptAuthTokensRefreshResponse"; +export type { CodexErrorInfo } from "./CodexErrorInfo"; +export type { CollabAgentState } from "./CollabAgentState"; +export type { CollabAgentStatus } from "./CollabAgentStatus"; +export type { CollabAgentTool } from "./CollabAgentTool"; +export type { CollabAgentToolCallStatus } from "./CollabAgentToolCallStatus"; +export type { CollaborationModeListParams } from "./CollaborationModeListParams"; +export type { CollaborationModeListResponse } from "./CollaborationModeListResponse"; +export type { CollaborationModeMask } from "./CollaborationModeMask"; +export type { CommandAction } from "./CommandAction"; +export type { CommandExecOutputDeltaNotification } from "./CommandExecOutputDeltaNotification"; +export type { CommandExecOutputStream } from "./CommandExecOutputStream"; +export type { CommandExecParams } from "./CommandExecParams"; +export type { CommandExecResizeParams } from "./CommandExecResizeParams"; +export type { CommandExecResizeResponse } from "./CommandExecResizeResponse"; +export type { CommandExecResponse } from "./CommandExecResponse"; +export type { CommandExecTerminalSize } from "./CommandExecTerminalSize"; +export type { CommandExecTerminateParams } from "./CommandExecTerminateParams"; +export type { CommandExecTerminateResponse } from "./CommandExecTerminateResponse"; +export type { CommandExecWriteParams } from "./CommandExecWriteParams"; +export type { CommandExecWriteResponse } from "./CommandExecWriteResponse"; +export type { CommandExecutionApprovalDecision } from "./CommandExecutionApprovalDecision"; +export type { CommandExecutionOutputDeltaNotification } from "./CommandExecutionOutputDeltaNotification"; +export type { CommandExecutionRequestApprovalParams } from "./CommandExecutionRequestApprovalParams"; +export type { CommandExecutionRequestApprovalResponse } from "./CommandExecutionRequestApprovalResponse"; +export type { CommandExecutionSource } from "./CommandExecutionSource"; +export type { CommandExecutionStatus } from "./CommandExecutionStatus"; +export type { CommandMigration } from "./CommandMigration"; +export type { Config } from "./Config"; +export type { ConfigBatchWriteParams } from "./ConfigBatchWriteParams"; +export type { ConfigEdit } from "./ConfigEdit"; +export type { ConfigLayer } from "./ConfigLayer"; +export type { ConfigLayerMetadata } from "./ConfigLayerMetadata"; +export type { ConfigLayerSource } from "./ConfigLayerSource"; +export type { ConfigReadParams } from "./ConfigReadParams"; +export type { ConfigReadResponse } from "./ConfigReadResponse"; +export type { ConfigRequirements } from "./ConfigRequirements"; +export type { ConfigRequirementsReadResponse } from "./ConfigRequirementsReadResponse"; +export type { ConfigValueWriteParams } from "./ConfigValueWriteParams"; +export type { ConfigWarningNotification } from "./ConfigWarningNotification"; +export type { ConfigWriteResponse } from "./ConfigWriteResponse"; +export type { ConfiguredHookHandler } from "./ConfiguredHookHandler"; +export type { ConfiguredHookMatcherGroup } from "./ConfiguredHookMatcherGroup"; +export type { ContextCompactedNotification } from "./ContextCompactedNotification"; +export type { CreditsSnapshot } from "./CreditsSnapshot"; +export type { DeprecationNoticeNotification } from "./DeprecationNoticeNotification"; +export type { DeviceKeyAlgorithm } from "./DeviceKeyAlgorithm"; +export type { DeviceKeyCreateParams } from "./DeviceKeyCreateParams"; +export type { DeviceKeyCreateResponse } from "./DeviceKeyCreateResponse"; +export type { DeviceKeyProtectionClass } from "./DeviceKeyProtectionClass"; +export type { DeviceKeyProtectionPolicy } from "./DeviceKeyProtectionPolicy"; +export type { DeviceKeyPublicParams } from "./DeviceKeyPublicParams"; +export type { DeviceKeyPublicResponse } from "./DeviceKeyPublicResponse"; +export type { DeviceKeySignParams } from "./DeviceKeySignParams"; +export type { DeviceKeySignPayload } from "./DeviceKeySignPayload"; +export type { DeviceKeySignResponse } from "./DeviceKeySignResponse"; +export type { DynamicToolCallOutputContentItem } from "./DynamicToolCallOutputContentItem"; +export type { DynamicToolCallParams } from "./DynamicToolCallParams"; +export type { DynamicToolCallResponse } from "./DynamicToolCallResponse"; +export type { DynamicToolCallStatus } from "./DynamicToolCallStatus"; +export type { DynamicToolSpec } from "./DynamicToolSpec"; +export type { ErrorNotification } from "./ErrorNotification"; +export type { ExecPolicyAmendment } from "./ExecPolicyAmendment"; +export type { ExperimentalFeature } from "./ExperimentalFeature"; +export type { ExperimentalFeatureEnablementSetParams } from "./ExperimentalFeatureEnablementSetParams"; +export type { ExperimentalFeatureEnablementSetResponse } from "./ExperimentalFeatureEnablementSetResponse"; +export type { ExperimentalFeatureListParams } from "./ExperimentalFeatureListParams"; +export type { ExperimentalFeatureListResponse } from "./ExperimentalFeatureListResponse"; +export type { ExperimentalFeatureStage } from "./ExperimentalFeatureStage"; +export type { ExternalAgentConfigDetectParams } from "./ExternalAgentConfigDetectParams"; +export type { ExternalAgentConfigDetectResponse } from "./ExternalAgentConfigDetectResponse"; +export type { ExternalAgentConfigImportCompletedNotification } from "./ExternalAgentConfigImportCompletedNotification"; +export type { ExternalAgentConfigImportParams } from "./ExternalAgentConfigImportParams"; +export type { ExternalAgentConfigImportResponse } from "./ExternalAgentConfigImportResponse"; +export type { ExternalAgentConfigMigrationItem } from "./ExternalAgentConfigMigrationItem"; +export type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType"; +export type { FeedbackUploadParams } from "./FeedbackUploadParams"; +export type { FeedbackUploadResponse } from "./FeedbackUploadResponse"; +export type { FileChangeApprovalDecision } from "./FileChangeApprovalDecision"; +export type { FileChangeOutputDeltaNotification } from "./FileChangeOutputDeltaNotification"; +export type { FileChangePatchUpdatedNotification } from "./FileChangePatchUpdatedNotification"; +export type { FileChangeRequestApprovalParams } from "./FileChangeRequestApprovalParams"; +export type { FileChangeRequestApprovalResponse } from "./FileChangeRequestApprovalResponse"; +export type { FileSystemAccessMode } from "./FileSystemAccessMode"; +export type { FileSystemPath } from "./FileSystemPath"; +export type { FileSystemSandboxEntry } from "./FileSystemSandboxEntry"; +export type { FileSystemSpecialPath } from "./FileSystemSpecialPath"; +export type { FileUpdateChange } from "./FileUpdateChange"; +export type { FsChangedNotification } from "./FsChangedNotification"; +export type { FsCopyParams } from "./FsCopyParams"; +export type { FsCopyResponse } from "./FsCopyResponse"; +export type { FsCreateDirectoryParams } from "./FsCreateDirectoryParams"; +export type { FsCreateDirectoryResponse } from "./FsCreateDirectoryResponse"; +export type { FsGetMetadataParams } from "./FsGetMetadataParams"; +export type { FsGetMetadataResponse } from "./FsGetMetadataResponse"; +export type { FsReadDirectoryEntry } from "./FsReadDirectoryEntry"; +export type { FsReadDirectoryParams } from "./FsReadDirectoryParams"; +export type { FsReadDirectoryResponse } from "./FsReadDirectoryResponse"; +export type { FsReadFileParams } from "./FsReadFileParams"; +export type { FsReadFileResponse } from "./FsReadFileResponse"; +export type { FsRemoveParams } from "./FsRemoveParams"; +export type { FsRemoveResponse } from "./FsRemoveResponse"; +export type { FsUnwatchParams } from "./FsUnwatchParams"; +export type { FsUnwatchResponse } from "./FsUnwatchResponse"; +export type { FsWatchParams } from "./FsWatchParams"; +export type { FsWatchResponse } from "./FsWatchResponse"; +export type { FsWriteFileParams } from "./FsWriteFileParams"; +export type { FsWriteFileResponse } from "./FsWriteFileResponse"; +export type { GetAccountParams } from "./GetAccountParams"; +export type { GetAccountRateLimitsResponse } from "./GetAccountRateLimitsResponse"; +export type { GetAccountResponse } from "./GetAccountResponse"; +export type { GitInfo } from "./GitInfo"; +export type { GrantedPermissionProfile } from "./GrantedPermissionProfile"; +export type { GuardianApprovalReview } from "./GuardianApprovalReview"; +export type { GuardianApprovalReviewAction } from "./GuardianApprovalReviewAction"; +export type { GuardianApprovalReviewStatus } from "./GuardianApprovalReviewStatus"; +export type { GuardianCommandSource } from "./GuardianCommandSource"; +export type { GuardianRiskLevel } from "./GuardianRiskLevel"; +export type { GuardianUserAuthorization } from "./GuardianUserAuthorization"; +export type { GuardianWarningNotification } from "./GuardianWarningNotification"; +export type { HookCompletedNotification } from "./HookCompletedNotification"; +export type { HookErrorInfo } from "./HookErrorInfo"; +export type { HookEventName } from "./HookEventName"; +export type { HookExecutionMode } from "./HookExecutionMode"; +export type { HookHandlerType } from "./HookHandlerType"; +export type { HookMetadata } from "./HookMetadata"; +export type { HookMigration } from "./HookMigration"; +export type { HookOutputEntry } from "./HookOutputEntry"; +export type { HookOutputEntryKind } from "./HookOutputEntryKind"; +export type { HookPromptFragment } from "./HookPromptFragment"; +export type { HookRunStatus } from "./HookRunStatus"; +export type { HookRunSummary } from "./HookRunSummary"; +export type { HookScope } from "./HookScope"; +export type { HookSource } from "./HookSource"; +export type { HookStartedNotification } from "./HookStartedNotification"; +export type { HookTrustStatus } from "./HookTrustStatus"; +export type { HooksListEntry } from "./HooksListEntry"; +export type { HooksListParams } from "./HooksListParams"; +export type { HooksListResponse } from "./HooksListResponse"; +export type { ItemCompletedNotification } from "./ItemCompletedNotification"; +export type { ItemGuardianApprovalReviewCompletedNotification } from "./ItemGuardianApprovalReviewCompletedNotification"; +export type { ItemGuardianApprovalReviewStartedNotification } from "./ItemGuardianApprovalReviewStartedNotification"; +export type { ItemStartedNotification } from "./ItemStartedNotification"; +export type { ListMcpServerStatusParams } from "./ListMcpServerStatusParams"; +export type { ListMcpServerStatusResponse } from "./ListMcpServerStatusResponse"; +export type { LoginAccountParams } from "./LoginAccountParams"; +export type { LoginAccountResponse } from "./LoginAccountResponse"; +export type { LogoutAccountResponse } from "./LogoutAccountResponse"; +export type { ManagedHooksRequirements } from "./ManagedHooksRequirements"; +export type { MarketplaceAddParams } from "./MarketplaceAddParams"; +export type { MarketplaceAddResponse } from "./MarketplaceAddResponse"; +export type { MarketplaceInterface } from "./MarketplaceInterface"; +export type { MarketplaceLoadErrorInfo } from "./MarketplaceLoadErrorInfo"; +export type { MarketplaceRemoveParams } from "./MarketplaceRemoveParams"; +export type { MarketplaceRemoveResponse } from "./MarketplaceRemoveResponse"; +export type { MarketplaceUpgradeErrorInfo } from "./MarketplaceUpgradeErrorInfo"; +export type { MarketplaceUpgradeParams } from "./MarketplaceUpgradeParams"; +export type { MarketplaceUpgradeResponse } from "./MarketplaceUpgradeResponse"; +export type { McpAuthStatus } from "./McpAuthStatus"; +export type { McpElicitationArrayType } from "./McpElicitationArrayType"; +export type { McpElicitationBooleanSchema } from "./McpElicitationBooleanSchema"; +export type { McpElicitationBooleanType } from "./McpElicitationBooleanType"; +export type { McpElicitationConstOption } from "./McpElicitationConstOption"; +export type { McpElicitationEnumSchema } from "./McpElicitationEnumSchema"; +export type { McpElicitationLegacyTitledEnumSchema } from "./McpElicitationLegacyTitledEnumSchema"; +export type { McpElicitationMultiSelectEnumSchema } from "./McpElicitationMultiSelectEnumSchema"; +export type { McpElicitationNumberSchema } from "./McpElicitationNumberSchema"; +export type { McpElicitationNumberType } from "./McpElicitationNumberType"; +export type { McpElicitationObjectType } from "./McpElicitationObjectType"; +export type { McpElicitationPrimitiveSchema } from "./McpElicitationPrimitiveSchema"; +export type { McpElicitationSchema } from "./McpElicitationSchema"; +export type { McpElicitationSingleSelectEnumSchema } from "./McpElicitationSingleSelectEnumSchema"; +export type { McpElicitationStringFormat } from "./McpElicitationStringFormat"; +export type { McpElicitationStringSchema } from "./McpElicitationStringSchema"; +export type { McpElicitationStringType } from "./McpElicitationStringType"; +export type { McpElicitationTitledEnumItems } from "./McpElicitationTitledEnumItems"; +export type { McpElicitationTitledMultiSelectEnumSchema } from "./McpElicitationTitledMultiSelectEnumSchema"; +export type { McpElicitationTitledSingleSelectEnumSchema } from "./McpElicitationTitledSingleSelectEnumSchema"; +export type { McpElicitationUntitledEnumItems } from "./McpElicitationUntitledEnumItems"; +export type { McpElicitationUntitledMultiSelectEnumSchema } from "./McpElicitationUntitledMultiSelectEnumSchema"; +export type { McpElicitationUntitledSingleSelectEnumSchema } from "./McpElicitationUntitledSingleSelectEnumSchema"; +export type { McpResourceReadParams } from "./McpResourceReadParams"; +export type { McpResourceReadResponse } from "./McpResourceReadResponse"; +export type { McpServerElicitationAction } from "./McpServerElicitationAction"; +export type { McpServerElicitationRequestParams } from "./McpServerElicitationRequestParams"; +export type { McpServerElicitationRequestResponse } from "./McpServerElicitationRequestResponse"; +export type { McpServerMigration } from "./McpServerMigration"; +export type { McpServerOauthLoginCompletedNotification } from "./McpServerOauthLoginCompletedNotification"; +export type { McpServerOauthLoginParams } from "./McpServerOauthLoginParams"; +export type { McpServerOauthLoginResponse } from "./McpServerOauthLoginResponse"; +export type { McpServerRefreshResponse } from "./McpServerRefreshResponse"; +export type { McpServerStartupState } from "./McpServerStartupState"; +export type { McpServerStatus } from "./McpServerStatus"; +export type { McpServerStatusDetail } from "./McpServerStatusDetail"; +export type { McpServerStatusUpdatedNotification } from "./McpServerStatusUpdatedNotification"; +export type { McpServerToolCallParams } from "./McpServerToolCallParams"; +export type { McpServerToolCallResponse } from "./McpServerToolCallResponse"; +export type { McpToolCallError } from "./McpToolCallError"; +export type { McpToolCallProgressNotification } from "./McpToolCallProgressNotification"; +export type { McpToolCallResult } from "./McpToolCallResult"; +export type { McpToolCallStatus } from "./McpToolCallStatus"; +export type { MemoryCitation } from "./MemoryCitation"; +export type { MemoryCitationEntry } from "./MemoryCitationEntry"; +export type { MemoryResetResponse } from "./MemoryResetResponse"; +export type { MergeStrategy } from "./MergeStrategy"; +export type { MigrationDetails } from "./MigrationDetails"; +export type { MockExperimentalMethodParams } from "./MockExperimentalMethodParams"; +export type { MockExperimentalMethodResponse } from "./MockExperimentalMethodResponse"; +export type { Model } from "./Model"; +export type { ModelAvailabilityNux } from "./ModelAvailabilityNux"; +export type { ModelListParams } from "./ModelListParams"; +export type { ModelListResponse } from "./ModelListResponse"; +export type { ModelProviderCapabilitiesReadParams } from "./ModelProviderCapabilitiesReadParams"; +export type { ModelProviderCapabilitiesReadResponse } from "./ModelProviderCapabilitiesReadResponse"; +export type { ModelRerouteReason } from "./ModelRerouteReason"; +export type { ModelReroutedNotification } from "./ModelReroutedNotification"; +export type { ModelServiceTier } from "./ModelServiceTier"; +export type { ModelUpgradeInfo } from "./ModelUpgradeInfo"; +export type { ModelVerification } from "./ModelVerification"; +export type { ModelVerificationNotification } from "./ModelVerificationNotification"; +export type { NetworkAccess } from "./NetworkAccess"; +export type { NetworkApprovalContext } from "./NetworkApprovalContext"; +export type { NetworkApprovalProtocol } from "./NetworkApprovalProtocol"; +export type { NetworkDomainPermission } from "./NetworkDomainPermission"; +export type { NetworkPolicyAmendment } from "./NetworkPolicyAmendment"; +export type { NetworkPolicyRuleAction } from "./NetworkPolicyRuleAction"; +export type { NetworkRequirements } from "./NetworkRequirements"; +export type { NetworkUnixSocketPermission } from "./NetworkUnixSocketPermission"; +export type { NonSteerableTurnKind } from "./NonSteerableTurnKind"; +export type { OverriddenMetadata } from "./OverriddenMetadata"; +export type { PatchApplyStatus } from "./PatchApplyStatus"; +export type { PatchChangeKind } from "./PatchChangeKind"; +export type { PermissionGrantScope } from "./PermissionGrantScope"; +export type { PermissionProfile } from "./PermissionProfile"; +export type { PermissionProfileFileSystemPermissions } from "./PermissionProfileFileSystemPermissions"; +export type { PermissionProfileModificationParams } from "./PermissionProfileModificationParams"; +export type { PermissionProfileNetworkPermissions } from "./PermissionProfileNetworkPermissions"; +export type { PermissionProfileSelectionParams } from "./PermissionProfileSelectionParams"; +export type { PermissionsRequestApprovalParams } from "./PermissionsRequestApprovalParams"; +export type { PermissionsRequestApprovalResponse } from "./PermissionsRequestApprovalResponse"; +export type { PlanDeltaNotification } from "./PlanDeltaNotification"; +export type { PluginAuthPolicy } from "./PluginAuthPolicy"; +export type { PluginAvailability } from "./PluginAvailability"; +export type { PluginDetail } from "./PluginDetail"; +export type { PluginHookSummary } from "./PluginHookSummary"; +export type { PluginInstallParams } from "./PluginInstallParams"; +export type { PluginInstallPolicy } from "./PluginInstallPolicy"; +export type { PluginInstallResponse } from "./PluginInstallResponse"; +export type { PluginInterface } from "./PluginInterface"; +export type { PluginListMarketplaceKind } from "./PluginListMarketplaceKind"; +export type { PluginListParams } from "./PluginListParams"; +export type { PluginListResponse } from "./PluginListResponse"; +export type { PluginMarketplaceEntry } from "./PluginMarketplaceEntry"; +export type { PluginReadParams } from "./PluginReadParams"; +export type { PluginReadResponse } from "./PluginReadResponse"; +export type { PluginShareContext } from "./PluginShareContext"; +export type { PluginShareDeleteParams } from "./PluginShareDeleteParams"; +export type { PluginShareDeleteResponse } from "./PluginShareDeleteResponse"; +export type { PluginShareDiscoverability } from "./PluginShareDiscoverability"; +export type { PluginShareListItem } from "./PluginShareListItem"; +export type { PluginShareListParams } from "./PluginShareListParams"; +export type { PluginShareListResponse } from "./PluginShareListResponse"; +export type { PluginSharePrincipal } from "./PluginSharePrincipal"; +export type { PluginSharePrincipalType } from "./PluginSharePrincipalType"; +export type { PluginShareSaveParams } from "./PluginShareSaveParams"; +export type { PluginShareSaveResponse } from "./PluginShareSaveResponse"; +export type { PluginShareTarget } from "./PluginShareTarget"; +export type { PluginShareUpdateDiscoverability } from "./PluginShareUpdateDiscoverability"; +export type { PluginShareUpdateTargetsParams } from "./PluginShareUpdateTargetsParams"; +export type { PluginShareUpdateTargetsResponse } from "./PluginShareUpdateTargetsResponse"; +export type { PluginSkillReadParams } from "./PluginSkillReadParams"; +export type { PluginSkillReadResponse } from "./PluginSkillReadResponse"; +export type { PluginSource } from "./PluginSource"; +export type { PluginSummary } from "./PluginSummary"; +export type { PluginUninstallParams } from "./PluginUninstallParams"; +export type { PluginUninstallResponse } from "./PluginUninstallResponse"; +export type { PluginsMigration } from "./PluginsMigration"; +export type { ProcessExitedNotification } from "./ProcessExitedNotification"; +export type { ProcessKillParams } from "./ProcessKillParams"; +export type { ProcessKillResponse } from "./ProcessKillResponse"; +export type { ProcessOutputDeltaNotification } from "./ProcessOutputDeltaNotification"; +export type { ProcessOutputStream } from "./ProcessOutputStream"; +export type { ProcessResizePtyParams } from "./ProcessResizePtyParams"; +export type { ProcessResizePtyResponse } from "./ProcessResizePtyResponse"; +export type { ProcessSpawnParams } from "./ProcessSpawnParams"; +export type { ProcessSpawnResponse } from "./ProcessSpawnResponse"; +export type { ProcessTerminalSize } from "./ProcessTerminalSize"; +export type { ProcessWriteStdinParams } from "./ProcessWriteStdinParams"; +export type { ProcessWriteStdinResponse } from "./ProcessWriteStdinResponse"; +export type { ProfileV2 } from "./ProfileV2"; +export type { RateLimitReachedType } from "./RateLimitReachedType"; +export type { RateLimitSnapshot } from "./RateLimitSnapshot"; +export type { RateLimitWindow } from "./RateLimitWindow"; +export type { RawResponseItemCompletedNotification } from "./RawResponseItemCompletedNotification"; +export type { ReasoningEffortOption } from "./ReasoningEffortOption"; +export type { ReasoningSummaryPartAddedNotification } from "./ReasoningSummaryPartAddedNotification"; +export type { ReasoningSummaryTextDeltaNotification } from "./ReasoningSummaryTextDeltaNotification"; +export type { ReasoningTextDeltaNotification } from "./ReasoningTextDeltaNotification"; +export type { RemoteControlClientConnectionAudience } from "./RemoteControlClientConnectionAudience"; +export type { RemoteControlClientEnrollmentAudience } from "./RemoteControlClientEnrollmentAudience"; +export type { RemoteControlConnectionStatus } from "./RemoteControlConnectionStatus"; +export type { RemoteControlStatusChangedNotification } from "./RemoteControlStatusChangedNotification"; +export type { RequestPermissionProfile } from "./RequestPermissionProfile"; +export type { ResidencyRequirement } from "./ResidencyRequirement"; +export type { ReviewDelivery } from "./ReviewDelivery"; +export type { ReviewStartParams } from "./ReviewStartParams"; +export type { ReviewStartResponse } from "./ReviewStartResponse"; +export type { ReviewTarget } from "./ReviewTarget"; +export type { SandboxMode } from "./SandboxMode"; +export type { SandboxPolicy } from "./SandboxPolicy"; +export type { SandboxWorkspaceWrite } from "./SandboxWorkspaceWrite"; +export type { SendAddCreditsNudgeEmailParams } from "./SendAddCreditsNudgeEmailParams"; +export type { SendAddCreditsNudgeEmailResponse } from "./SendAddCreditsNudgeEmailResponse"; +export type { ServerRequestResolvedNotification } from "./ServerRequestResolvedNotification"; +export type { SessionMigration } from "./SessionMigration"; +export type { SessionSource } from "./SessionSource"; +export type { SkillDependencies } from "./SkillDependencies"; +export type { SkillErrorInfo } from "./SkillErrorInfo"; +export type { SkillInterface } from "./SkillInterface"; +export type { SkillMetadata } from "./SkillMetadata"; +export type { SkillScope } from "./SkillScope"; +export type { SkillSummary } from "./SkillSummary"; +export type { SkillToolDependency } from "./SkillToolDependency"; +export type { SkillsChangedNotification } from "./SkillsChangedNotification"; +export type { SkillsConfigWriteParams } from "./SkillsConfigWriteParams"; +export type { SkillsConfigWriteResponse } from "./SkillsConfigWriteResponse"; +export type { SkillsListEntry } from "./SkillsListEntry"; +export type { SkillsListExtraRootsForCwd } from "./SkillsListExtraRootsForCwd"; +export type { SkillsListParams } from "./SkillsListParams"; +export type { SkillsListResponse } from "./SkillsListResponse"; +export type { SortDirection } from "./SortDirection"; +export type { SubagentMigration } from "./SubagentMigration"; +export type { TerminalInteractionNotification } from "./TerminalInteractionNotification"; +export type { TextElement } from "./TextElement"; +export type { TextPosition } from "./TextPosition"; +export type { TextRange } from "./TextRange"; +export type { Thread } from "./Thread"; +export type { ThreadActiveFlag } from "./ThreadActiveFlag"; +export type { ThreadApproveGuardianDeniedActionParams } from "./ThreadApproveGuardianDeniedActionParams"; +export type { ThreadApproveGuardianDeniedActionResponse } from "./ThreadApproveGuardianDeniedActionResponse"; +export type { ThreadArchiveParams } from "./ThreadArchiveParams"; +export type { ThreadArchiveResponse } from "./ThreadArchiveResponse"; +export type { ThreadArchivedNotification } from "./ThreadArchivedNotification"; +export type { ThreadBackgroundTerminalsCleanParams } from "./ThreadBackgroundTerminalsCleanParams"; +export type { ThreadBackgroundTerminalsCleanResponse } from "./ThreadBackgroundTerminalsCleanResponse"; +export type { ThreadClosedNotification } from "./ThreadClosedNotification"; +export type { ThreadCompactStartParams } from "./ThreadCompactStartParams"; +export type { ThreadCompactStartResponse } from "./ThreadCompactStartResponse"; +export type { ThreadDecrementElicitationParams } from "./ThreadDecrementElicitationParams"; +export type { ThreadDecrementElicitationResponse } from "./ThreadDecrementElicitationResponse"; +export type { ThreadForkParams } from "./ThreadForkParams"; +export type { ThreadForkResponse } from "./ThreadForkResponse"; +export type { ThreadGoal } from "./ThreadGoal"; +export type { ThreadGoalClearParams } from "./ThreadGoalClearParams"; +export type { ThreadGoalClearResponse } from "./ThreadGoalClearResponse"; +export type { ThreadGoalClearedNotification } from "./ThreadGoalClearedNotification"; +export type { ThreadGoalGetParams } from "./ThreadGoalGetParams"; +export type { ThreadGoalGetResponse } from "./ThreadGoalGetResponse"; +export type { ThreadGoalSetParams } from "./ThreadGoalSetParams"; +export type { ThreadGoalSetResponse } from "./ThreadGoalSetResponse"; +export type { ThreadGoalStatus } from "./ThreadGoalStatus"; +export type { ThreadGoalUpdatedNotification } from "./ThreadGoalUpdatedNotification"; +export type { ThreadIncrementElicitationParams } from "./ThreadIncrementElicitationParams"; +export type { ThreadIncrementElicitationResponse } from "./ThreadIncrementElicitationResponse"; +export type { ThreadInjectItemsParams } from "./ThreadInjectItemsParams"; +export type { ThreadInjectItemsResponse } from "./ThreadInjectItemsResponse"; +export type { ThreadItem } from "./ThreadItem"; +export type { ThreadListParams } from "./ThreadListParams"; +export type { ThreadListResponse } from "./ThreadListResponse"; +export type { ThreadLoadedListParams } from "./ThreadLoadedListParams"; +export type { ThreadLoadedListResponse } from "./ThreadLoadedListResponse"; +export type { ThreadMemoryModeSetParams } from "./ThreadMemoryModeSetParams"; +export type { ThreadMemoryModeSetResponse } from "./ThreadMemoryModeSetResponse"; +export type { ThreadMetadataGitInfoUpdateParams } from "./ThreadMetadataGitInfoUpdateParams"; +export type { ThreadMetadataUpdateParams } from "./ThreadMetadataUpdateParams"; +export type { ThreadMetadataUpdateResponse } from "./ThreadMetadataUpdateResponse"; +export type { ThreadNameUpdatedNotification } from "./ThreadNameUpdatedNotification"; +export type { ThreadReadParams } from "./ThreadReadParams"; +export type { ThreadReadResponse } from "./ThreadReadResponse"; +export type { ThreadRealtimeAppendAudioParams } from "./ThreadRealtimeAppendAudioParams"; +export type { ThreadRealtimeAppendAudioResponse } from "./ThreadRealtimeAppendAudioResponse"; +export type { ThreadRealtimeAppendTextParams } from "./ThreadRealtimeAppendTextParams"; +export type { ThreadRealtimeAppendTextResponse } from "./ThreadRealtimeAppendTextResponse"; +export type { ThreadRealtimeAudioChunk } from "./ThreadRealtimeAudioChunk"; +export type { ThreadRealtimeClosedNotification } from "./ThreadRealtimeClosedNotification"; +export type { ThreadRealtimeErrorNotification } from "./ThreadRealtimeErrorNotification"; +export type { ThreadRealtimeItemAddedNotification } from "./ThreadRealtimeItemAddedNotification"; +export type { ThreadRealtimeListVoicesParams } from "./ThreadRealtimeListVoicesParams"; +export type { ThreadRealtimeListVoicesResponse } from "./ThreadRealtimeListVoicesResponse"; +export type { ThreadRealtimeOutputAudioDeltaNotification } from "./ThreadRealtimeOutputAudioDeltaNotification"; +export type { ThreadRealtimeSdpNotification } from "./ThreadRealtimeSdpNotification"; +export type { ThreadRealtimeStartParams } from "./ThreadRealtimeStartParams"; +export type { ThreadRealtimeStartResponse } from "./ThreadRealtimeStartResponse"; +export type { ThreadRealtimeStartTransport } from "./ThreadRealtimeStartTransport"; +export type { ThreadRealtimeStartedNotification } from "./ThreadRealtimeStartedNotification"; +export type { ThreadRealtimeStopParams } from "./ThreadRealtimeStopParams"; +export type { ThreadRealtimeStopResponse } from "./ThreadRealtimeStopResponse"; +export type { ThreadRealtimeTranscriptDeltaNotification } from "./ThreadRealtimeTranscriptDeltaNotification"; +export type { ThreadRealtimeTranscriptDoneNotification } from "./ThreadRealtimeTranscriptDoneNotification"; +export type { ThreadResumeParams } from "./ThreadResumeParams"; +export type { ThreadResumeResponse } from "./ThreadResumeResponse"; +export type { ThreadRollbackParams } from "./ThreadRollbackParams"; +export type { ThreadRollbackResponse } from "./ThreadRollbackResponse"; +export type { ThreadSetNameParams } from "./ThreadSetNameParams"; +export type { ThreadSetNameResponse } from "./ThreadSetNameResponse"; +export type { ThreadShellCommandParams } from "./ThreadShellCommandParams"; +export type { ThreadShellCommandResponse } from "./ThreadShellCommandResponse"; +export type { ThreadSortKey } from "./ThreadSortKey"; +export type { ThreadSource } from "./ThreadSource"; +export type { ThreadSourceKind } from "./ThreadSourceKind"; +export type { ThreadStartParams } from "./ThreadStartParams"; +export type { ThreadStartResponse } from "./ThreadStartResponse"; +export type { ThreadStartSource } from "./ThreadStartSource"; +export type { ThreadStartedNotification } from "./ThreadStartedNotification"; +export type { ThreadStatus } from "./ThreadStatus"; +export type { ThreadStatusChangedNotification } from "./ThreadStatusChangedNotification"; +export type { ThreadTokenUsage } from "./ThreadTokenUsage"; +export type { ThreadTokenUsageUpdatedNotification } from "./ThreadTokenUsageUpdatedNotification"; +export type { ThreadTurnsItemsListParams } from "./ThreadTurnsItemsListParams"; +export type { ThreadTurnsItemsListResponse } from "./ThreadTurnsItemsListResponse"; +export type { ThreadTurnsListParams } from "./ThreadTurnsListParams"; +export type { ThreadTurnsListResponse } from "./ThreadTurnsListResponse"; +export type { ThreadUnarchiveParams } from "./ThreadUnarchiveParams"; +export type { ThreadUnarchiveResponse } from "./ThreadUnarchiveResponse"; +export type { ThreadUnarchivedNotification } from "./ThreadUnarchivedNotification"; +export type { ThreadUnsubscribeParams } from "./ThreadUnsubscribeParams"; +export type { ThreadUnsubscribeResponse } from "./ThreadUnsubscribeResponse"; +export type { ThreadUnsubscribeStatus } from "./ThreadUnsubscribeStatus"; +export type { TokenUsageBreakdown } from "./TokenUsageBreakdown"; +export type { ToolRequestUserInputAnswer } from "./ToolRequestUserInputAnswer"; +export type { ToolRequestUserInputOption } from "./ToolRequestUserInputOption"; +export type { ToolRequestUserInputParams } from "./ToolRequestUserInputParams"; +export type { ToolRequestUserInputQuestion } from "./ToolRequestUserInputQuestion"; +export type { ToolRequestUserInputResponse } from "./ToolRequestUserInputResponse"; +export type { ToolsV2 } from "./ToolsV2"; +export type { Turn } from "./Turn"; +export type { TurnCompletedNotification } from "./TurnCompletedNotification"; +export type { TurnDiffUpdatedNotification } from "./TurnDiffUpdatedNotification"; +export type { TurnEnvironmentParams } from "./TurnEnvironmentParams"; +export type { TurnError } from "./TurnError"; +export type { TurnInterruptParams } from "./TurnInterruptParams"; +export type { TurnInterruptResponse } from "./TurnInterruptResponse"; +export type { TurnItemsView } from "./TurnItemsView"; +export type { TurnPlanStep } from "./TurnPlanStep"; +export type { TurnPlanStepStatus } from "./TurnPlanStepStatus"; +export type { TurnPlanUpdatedNotification } from "./TurnPlanUpdatedNotification"; +export type { TurnStartParams } from "./TurnStartParams"; +export type { TurnStartResponse } from "./TurnStartResponse"; +export type { TurnStartedNotification } from "./TurnStartedNotification"; +export type { TurnStatus } from "./TurnStatus"; +export type { TurnSteerParams } from "./TurnSteerParams"; +export type { TurnSteerResponse } from "./TurnSteerResponse"; +export type { UserInput } from "./UserInput"; +export type { WarningNotification } from "./WarningNotification"; +export type { WebSearchAction } from "./WebSearchAction"; +export type { WindowsSandboxReadiness } from "./WindowsSandboxReadiness"; +export type { WindowsSandboxReadinessResponse } from "./WindowsSandboxReadinessResponse"; +export type { WindowsSandboxSetupCompletedNotification } from "./WindowsSandboxSetupCompletedNotification"; +export type { WindowsSandboxSetupMode } from "./WindowsSandboxSetupMode"; +export type { WindowsSandboxSetupStartParams } from "./WindowsSandboxSetupStartParams"; +export type { WindowsSandboxSetupStartResponse } from "./WindowsSandboxSetupStartResponse"; +export type { WindowsWorldWritableWarningNotification } from "./WindowsWorldWritableWarningNotification"; +export type { WriteStatus } from "./WriteStatus"; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 00000000..916b1a6a --- /dev/null +++ b/src/main.ts @@ -0,0 +1,1325 @@ +import { ItemView, MarkdownRenderer, Notice, Platform, Plugin, TFile, type WorkspaceLeaf } from "obsidian"; +import { existsSync } from "fs"; +import { join } from "path"; + +import type { AppServerClient } from "./app-server/client"; +import { ConnectionManager, StaleConnectionError } from "./app-server/connection-manager"; +import type { ApprovalAction, PendingApproval } from "./approvals/model"; +import { + activeComposerSuggestions, + applyComposerSuggestionInsertion, + composerSuggestionNavigationDirection, + nextComposerSuggestionIndex, + parseSlashCommand, + type ComposerSuggestion, + type NoteCandidate, +} from "./composer/suggestions"; +import { userInputWithWikiLinkMentions } from "./composer/wikilink-context"; +import { VIEW_TYPE_CODEX_PANEL } from "./constants"; +import { createSystemItem } from "./display/model"; +import type { DisplayItem } from "./display/types"; +import type { ReasoningEffort } from "./generated/app-server/ReasoningEffort"; +import type { UserInput } from "./generated/app-server/v2/UserInput"; +import type { ServiceTier } from "./app-server/service-tier"; +import { + collaborationModeLabel as formatCollaborationModeLabel, + collaborationModeToggleMessage, + nextCollaborationMode, +} from "./panel/collaboration-mode"; +import { PanelController } from "./panel/controller"; +import { connectionDiagnosticLines, connectionDiagnosticRows } from "./panel/diagnostics"; +import { contextSummary, effectiveConfigSections, rateLimitSummary } from "./panel/runtime-view"; +import { + configRecord, + currentModel, + currentReasoningEffort, + currentServiceTier, + fastModeLabel, + commitRuntimeOverride, + resetRuntimeOverride, + requestedOrConfiguredServiceTier, + requestedTurnRuntimeSettings, + runtimeSummaryLabel, + runtimeOverrideLabel, + serviceTierLabel, + setRuntimeOverride, + sortedAvailableModels, + supportedReasoningEfforts, + type RuntimeSnapshot, +} from "./panel/runtime-state"; +import { compactContextLabel, modelOverrideMessage, reasoningEffortOverrideMessage } from "./panel/runtime-settings"; +import { executeSlashCommand as runSlashCommand, type SlashCommandExecutionResult, type SlashCommandName } from "./panel/slash-commands"; +import { PanelSessionController } from "./panel/session-controller"; +import { ThreadHistoryLoader } from "./panel/thread-history"; +import { ThreadRenameController } from "./panel/thread-rename"; +import { DEFAULT_SETTINGS, getVaultPath, normalizeSettings, settingsMatchNormalizedData, type CodexPanelSettings } from "./settings"; +import { CodexPanelSettingTab } from "./settings-tab"; +import { clearActiveThreadState, clearConnectionScopedState, createPanelState, type PanelState } from "./state/panel-state"; +import { userInputDraftKey, userInputOtherDraftKey } from "./panel/request-state"; +import { getThreadTitle } from "./threads"; +import { errorMessage } from "./utils"; +import { questionDefaultAnswer, type PendingUserInput } from "./user-input/model"; +import { + renderComposerShell, + renderComposerSuggestions, + syncComposerControls as syncComposerControlElements, + syncComposerHeight, +} from "./view/composer"; +import { renderTextWithWikiLinks as renderInlineWikiLinks, shortSignature } from "./view/dom"; +import { messageRenderBlocks } from "./view/message-stream"; +import { renderPendingRequestMessage } from "./view/pending-request-message"; +import { bottomScrollTop, captureScrollAnchor, isNearScrollBottom, restoreScrollAnchor } from "./view/scroll"; +import { renderToolbar, toolbarSignature, type ToolbarChoice, type ToolbarViewModel } from "./view/toolbar"; + +interface ElectronShell { + openPath(path: string): Promise; +} + +export default class CodexPanelPlugin extends Plugin { + settings: CodexPanelSettings = DEFAULT_SETTINGS; + vaultPath = ""; + + async onload(): Promise { + this.vaultPath = getVaultPath(this.app); + await this.loadSettings(); + + this.registerView(VIEW_TYPE_CODEX_PANEL, (leaf) => new CodexPanelView(leaf, this)); + + this.addRibbonIcon("terminal", "Open panel", () => { + void this.activateView(); + }); + + this.addCommand({ + id: "open-codex-panel", + name: "Open panel", + callback: () => void this.activateView(), + }); + + this.addCommand({ + id: "new-codex-chat", + name: "New chat", + callback: async () => { + const view = await this.activateView(); + await view.startNewThread(); + }, + }); + + this.addSettingTab(new CodexPanelSettingTab(this.app, this)); + } + + async activateView(): Promise { + const existing = this.app.workspace.getLeavesOfType(VIEW_TYPE_CODEX_PANEL)[0]; + const leaf = existing ?? this.app.workspace.getRightLeaf(false); + if (!leaf) throw new Error("Could not create a right sidebar leaf."); + + await leaf.setViewState({ type: VIEW_TYPE_CODEX_PANEL, active: true }); + await this.app.workspace.revealLeaf(leaf); + return leaf.view as CodexPanelView; + } + + refreshOpenViews(): void { + for (const leaf of this.app.workspace.getLeavesOfType(VIEW_TYPE_CODEX_PANEL)) { + if (leaf.view instanceof CodexPanelView) { + leaf.view.refreshSettings(); + } + } + } + + refreshOpenThreadLists(): void { + for (const leaf of this.app.workspace.getLeavesOfType(VIEW_TYPE_CODEX_PANEL)) { + if (leaf.view instanceof CodexPanelView) { + leaf.view.refreshThreadList(); + } + } + } + + async loadSettings(): Promise { + const data = await this.loadData(); + this.settings = normalizeSettings(data); + if (!settingsMatchNormalizedData(data, this.settings)) { + await this.saveSettings(); + } + } + + async saveSettings(): Promise { + await this.saveData(this.settings); + } +} + +class CodexPanelView extends ItemView { + private client: AppServerClient | null = null; + private readonly connection: ConnectionManager; + private readonly controller: PanelController; + private readonly session: PanelSessionController; + private readonly history: ThreadHistoryLoader; + private readonly threadRename: ThreadRenameController; + private readonly state: PanelState = createPanelState(); + private readonly viewId = `codex-panel-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`; + private readonly blockSignatures = new Map(); + private noteCandidatesCache: NoteCandidate[] | null = null; + private noteEventsRegistered = false; + private composer: HTMLTextAreaElement | null = null; + private composerSuggestEl: HTMLElement | null = null; + private toolbarEl: HTMLElement | null = null; + private configSlotEl: HTMLElement | null = null; + private messagesSlotEl: HTMLElement | null = null; + private composerSlotEl: HTMLElement | null = null; + private scheduledRenderTimer: number | null = null; + private toolbarSignature: string | null = null; + private forceScrollMessagesToBottomOnNextRender = false; + + constructor( + leaf: WorkspaceLeaf, + private readonly plugin: CodexPanelPlugin, + ) { + super(leaf); + this.connection = new ConnectionManager(() => this.plugin.settings.codexPath, this.plugin.vaultPath, { + onNotification: (notification) => { + this.controller.handleNotification(notification); + this.scheduleRender(); + }, + onServerRequest: (request) => { + this.controller.handleServerRequest(request); + this.render(); + }, + onLog: (message) => { + this.controller.handleAppServerLog(message); + this.render(); + }, + onExit: () => { + this.setStatus("Codex app-server stopped."); + clearConnectionScopedState(this.state); + this.threadRename.resetThreadTurnPresence(false); + this.client = null; + this.render(); + }, + }); + this.controller = new PanelController(this.state, { + refreshThreads: () => void this.refreshThreads(), + maybeNameThread: (threadId, turn) => this.threadRename.maybeAutoNameThread(threadId, turn), + respondToServerRequest: (requestId, result) => this.respondToServerRequest(requestId, result), + rejectServerRequest: (requestId, code, message) => this.rejectServerRequest(requestId, code, message), + }); + this.session = new PanelSessionController({ + state: this.state, + vaultPath: this.plugin.vaultPath, + currentClient: () => this.connection.currentClient(), + runtimeSnapshot: () => this.runtimeSnapshot(), + setStatus: (status) => this.setStatus(status), + addSystemMessage: (text) => this.addSystemMessage(text), + addDedupedSystemMessage: (text) => this.addDedupedSystemMessage(text), + forceMessagesToBottom: () => this.forceMessagesToBottom(), + }); + this.history = new ThreadHistoryLoader({ + state: this.state, + currentClient: () => this.client, + render: () => this.render(), + addSystemMessage: (text) => this.addSystemMessage(text), + forceMessagesToBottom: () => this.forceMessagesToBottom(), + keepCurrentScrollPosition: () => { + this.forceScrollMessagesToBottomOnNextRender = false; + }, + setThreadTurnPresence: (hadTurns) => this.threadRename.resetThreadTurnPresence(hadTurns), + }); + this.threadRename = new ThreadRenameController({ + state: this.state, + vaultPath: this.plugin.vaultPath, + settings: () => this.plugin.settings, + ensureConnected: () => this.ensureConnected(), + currentClient: () => this.connection.currentClient(), + refreshThreads: () => this.refreshThreads(), + render: () => this.render(), + addSystemMessage: (text) => this.addSystemMessage(text), + }); + } + + getViewType(): string { + return VIEW_TYPE_CODEX_PANEL; + } + + getDisplayText(): string { + return "Codex"; + } + + getIcon(): string { + return "terminal"; + } + + refreshSettings(): void { + this.render(); + } + + refreshThreadList(): void { + void this.refreshThreads(); + } + + async onOpen(): Promise { + this.registerNoteIndexInvalidation(); + this.registerDomEvent(document, "pointerdown", (event) => this.closeToolbarPanelOnOutsidePointer(event)); + this.render(); + await this.ensureConnected(); + } + + async onClose(): Promise { + if (this.scheduledRenderTimer !== null) { + window.clearTimeout(this.scheduledRenderTimer); + this.scheduledRenderTimer = null; + } + this.connection.disconnect(); + this.client = null; + } + + setComposerText(text: string): void { + this.state.composerDraft = text; + if (this.composer) { + this.composer.value = text; + syncComposerHeight(this.composer); + this.composer.focus(); + } else { + this.render(); + } + } + + private async ensureConnected(): Promise { + if (this.connection.isConnected()) { + this.client = this.connection.currentClient(); + return; + } + + this.setStatus("Starting Codex app-server..."); + try { + this.state.initializeResponse = await this.connection.connect(); + this.client = this.connection.currentClient(); + if (!this.client) throw new Error("Codex app-server connection did not initialize."); + await this.session.refreshSessionMetadata(); + await this.session.refreshThreadList(); + this.setStatus("Connected."); + } catch (error) { + if (error instanceof StaleConnectionError) return; + this.setStatus("Connection failed."); + this.addSystemMessage(error instanceof Error ? error.message : String(error)); + new Notice("Codex app-server connection failed."); + } + this.scheduleRender(); + } + + async startNewThread(): Promise { + if (this.state.busy) return; + + await this.ensureConnected(); + if (!this.client) return; + + try { + const response = await this.session.startThread(); + if (!response) return; + this.threadRename.resetThreadTurnPresence(false); + this.state.displayItems = [this.systemItem(`Started thread ${response.thread.id}`)]; + this.forceMessagesToBottom(); + await this.refreshThreads(); + this.render(); + } catch (error) { + this.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + } + + private async refreshThreads(): Promise { + this.client = this.connection.currentClient(); + if (!this.client) return; + try { + await this.session.refreshThreadList(); + await this.session.refreshSessionMetadata(); + this.render(); + } catch (error) { + this.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + } + + private async resumeThread(threadId: string): Promise { + if (this.state.busy && threadId !== this.state.activeThreadId) { + this.addSystemMessage("Finish or interrupt the current turn before switching threads."); + return; + } + await this.ensureConnected(); + if (!this.client) return; + + try { + const response = await this.client.resumeThread(threadId, this.plugin.vaultPath); + this.state.activeThreadId = response.thread.id; + this.state.activeThreadCwd = response.cwd ?? response.thread.cwd ?? this.plugin.vaultPath; + this.state.activeTurnId = null; + this.state.activeModel = response.model ?? null; + this.state.activeServiceTier = response.serviceTier ?? null; + this.state.activeThreadCliVersion = response.thread.cliVersion ?? null; + this.state.tokenUsage = null; + this.state.displayItems = []; + this.state.historyCursor = null; + this.threadRename.resetThreadTurnPresence(false); + this.forceMessagesToBottom(); + await this.history.loadLatest(response.thread.id); + if (this.state.displayItems.length === 0) { + this.state.displayItems.push(this.systemItem(`Resumed thread ${response.thread.id}`)); + this.forceMessagesToBottom(); + this.render(); + } + } catch (error) { + this.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + } + + private async sendMessage(): Promise { + const text = this.state.composerDraft.trim(); + if (!text) return; + + await this.ensureConnected(); + if (!this.client) return; + + const slashCommand = parseSlashCommand(text); + if (slashCommand) { + this.state.composerDraft = ""; + if (this.composer) { + this.composer.value = ""; + syncComposerHeight(this.composer); + } + this.clearComposerSuggestions(); + const result = await this.executeSlashCommand(slashCommand.command, slashCommand.args); + if (result?.sendText) { + await this.sendTurnText(result.sendText); + } + this.render(); + return; + } + + await this.sendTurnText(text); + } + + private async sendTurnText(text: string): Promise { + const client = this.client; + if (!client) return; + + if (this.state.busy) { + await this.steerCurrentTurn(text); + return; + } + + let optimisticUserId: string | null = null; + try { + if (!this.state.activeThreadId) { + const threadResponse = await this.session.startThread(); + if (!threadResponse) return; + this.threadRename.resetThreadTurnPresence(false); + } + + optimisticUserId = `local-user-${Date.now()}`; + this.state.displayItems.push({ + id: optimisticUserId, + kind: "message", + role: "user", + text, + markdown: true, + }); + this.forceMessagesToBottom(); + this.state.composerDraft = ""; + if (this.composer) { + this.composer.value = ""; + syncComposerHeight(this.composer); + } + this.state.busy = true; + this.render(); + + const turnSettings = requestedTurnRuntimeSettings(this.runtimeSnapshot()); + if (turnSettings.warning) { + this.addSystemMessage(`${this.collaborationModeLabel()} mode is selected, but ${turnSettings.warning}`); + } + const codexInput = this.codexInput(text); + const activeThreadId = this.state.activeThreadId; + if (!activeThreadId) return; + const response = await client.startTurn( + activeThreadId, + this.plugin.vaultPath, + codexInput, + requestedOrConfiguredServiceTier(this.runtimeSnapshot()), + turnSettings.collaborationMode, + turnSettings.model, + turnSettings.effort, + ); + this.state.requestedModel = commitRuntimeOverride(this.state.requestedModel); + this.state.requestedReasoningEffort = commitRuntimeOverride(this.state.requestedReasoningEffort); + this.state.activeTurnId = response.turn.id; + this.state.displayItems = this.state.displayItems.map((item) => + item.id === optimisticUserId ? { ...item, turnId: response.turn.id } : item, + ); + this.setStatus("Turn running..."); + } catch (error) { + this.state.busy = false; + if (optimisticUserId) this.state.displayItems = this.state.displayItems.filter((item) => item.id !== optimisticUserId); + this.state.composerDraft = text; + if (this.composer) { + this.composer.value = text; + syncComposerHeight(this.composer); + } + this.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + this.scheduleRender(); + } + + private async steerCurrentTurn(text: string): Promise { + if (!this.client || !this.state.activeThreadId || !this.state.activeTurnId) { + this.addSystemMessage("Current turn is not steerable yet."); + return; + } + + const threadId = this.state.activeThreadId; + const expectedTurnId = this.state.activeTurnId; + + this.state.composerDraft = ""; + if (this.composer) { + this.composer.value = ""; + syncComposerHeight(this.composer); + } + this.clearComposerSuggestions(); + this.syncComposerControls(); + + try { + await this.client.steerTurn(threadId, expectedTurnId, this.codexInput(text)); + this.state.displayItems.push({ + id: `local-steer-${Date.now()}`, + kind: "message", + role: "user", + text, + turnId: expectedTurnId, + markdown: true, + }); + this.forceMessagesToBottom(); + this.setStatus("Steered current turn."); + } catch (error) { + this.state.composerDraft = text; + if (this.composer) { + this.composer.value = text; + syncComposerHeight(this.composer); + this.composer.focus(); + } + this.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + + this.scheduleRender(); + } + + private async interruptTurn(): Promise { + if (!this.client || !this.state.activeThreadId || !this.state.activeTurnId) return; + try { + await this.client.interruptTurn(this.state.activeThreadId, this.state.activeTurnId); + this.setStatus("Interrupt requested."); + } catch (error) { + this.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + } + + private async executeSlashCommand(command: SlashCommandName, args: string): Promise { + if (!this.client) return; + return runSlashCommand(command, args, { + activeThreadId: this.state.activeThreadId, + compactThread: async (threadId) => { + await this.client?.compactThread(threadId); + }, + toggleFastMode: () => this.toggleFastMode(), + toggleCollaborationMode: () => this.toggleCollaborationMode(), + addSystemMessage: (text) => this.addSystemMessage(text), + setStatus: (status) => this.setStatus(status), + setRequestedModel: (model) => this.setRequestedModel(model), + setRequestedReasoningEffort: (effort) => this.setRequestedReasoningEffort(effort), + statusSummaryLines: () => this.statusSummaryLines(), + connectionDiagnosticLines: () => this.connectionDiagnosticLines(), + modelStatusLines: () => this.modelStatusLines(), + effortStatusLines: () => this.effortStatusLines(), + }); + } + + private toggleFastMode(): void { + const current = currentServiceTier(this.runtimeSnapshot(), configRecord(this.state.effectiveConfig)); + const next: ServiceTier = current === "fast" ? "standard" : "fast"; + this.state.requestedServiceTier = next; + this.state.activeServiceTier = next; + this.state.runtimePicker = null; + this.addSystemMessage(next === "fast" ? "Fast mode on for subsequent turns." : "Fast mode off for subsequent turns."); + } + + private toggleCollaborationMode(): void { + const next = nextCollaborationMode(this.state.requestedCollaborationMode); + this.state.requestedCollaborationMode = next; + this.state.runtimePicker = null; + this.addSystemMessage(collaborationModeToggleMessage(next)); + } + + private toggleRuntimePicker(picker: NonNullable): void { + this.state.runtimePicker = this.state.runtimePicker === picker ? null : picker; + if (this.state.runtimePicker !== null) { + this.state.openDetails.delete("history"); + this.state.openDetails.delete("status-panel"); + } + this.render(); + } + + private setRequestedModelFromUi(model: string | null): void { + this.setRequestedModel(model); + this.state.runtimePicker = null; + this.addSystemMessage(modelOverrideMessage(model)); + } + + private setRequestedModel(model: string | null): void { + this.state.requestedModel = model === null ? resetRuntimeOverride() : setRuntimeOverride(model); + } + + private setRequestedReasoningEffortFromUi(effort: ReasoningEffort | null): void { + this.setRequestedReasoningEffort(effort); + this.state.runtimePicker = null; + this.addSystemMessage(reasoningEffortOverrideMessage(effort)); + } + + private setRequestedReasoningEffort(effort: ReasoningEffort | null): void { + this.state.requestedReasoningEffort = effort === null ? resetRuntimeOverride() : setRuntimeOverride(effort); + } + + private async resolveApproval(approval: PendingApproval, action: ApprovalAction): Promise { + this.controller.resolveApproval(approval, action); + this.render(); + } + + private respondToServerRequest(requestId: Parameters[0], result: unknown): boolean { + try { + this.client?.respondToServerRequest(requestId, result); + return Boolean(this.client); + } catch { + return false; + } + } + + private rejectServerRequest(requestId: Parameters[0], code: number, message: string): boolean { + try { + this.client?.rejectServerRequest(requestId, code, message); + return Boolean(this.client); + } catch { + return false; + } + } + + private async resolveUserInput(input: PendingUserInput): Promise { + this.controller.resolveUserInput(input, this.answersForUserInput(input)); + this.render(); + } + + private async cancelUserInput(input: PendingUserInput): Promise { + this.controller.cancelUserInput(input); + this.render(); + } + + private systemItem(text: string): DisplayItem { + return createSystemItem(text); + } + + private addSystemMessage(text: string): void { + this.controller.addSystemMessage(text); + this.render(); + } + + private addDedupedSystemMessage(text: string): void { + this.controller.addDedupedSystemMessage(text); + this.render(); + } + + private setStatus(status: string): void { + this.state.status = status; + } + + private render(): void { + if (this.scheduledRenderTimer !== null) { + window.clearTimeout(this.scheduledRenderTimer); + this.scheduledRenderTimer = null; + } + const root = this.containerEl.children[1] as HTMLElement; + if (!this.toolbarEl || !this.configSlotEl || !this.messagesSlotEl || !this.composerSlotEl) { + this.renderShell(root); + } + if (!this.toolbarEl || !this.configSlotEl || !this.messagesSlotEl || !this.composerSlotEl) { + return; + } + + this.renderToolbarIfNeeded(this.toolbarEl); + + this.configSlotEl.empty(); + + this.renderMessages(this.messagesSlotEl); + this.renderComposer(this.composerSlotEl); + this.syncComposerControls(); + } + + private renderToolbarIfNeeded(toolbar: HTMLElement): void { + const model = this.toolbarViewModel(); + const signature = toolbarSignature(model); + if (this.toolbarSignature === signature) return; + + this.toolbarSignature = signature; + renderToolbar(toolbar, model, { + toggleHistory: () => this.toggleHistoryPanel(), + toggleStatusPanel: () => this.toggleStatusPanel(), + togglePlan: () => this.toggleCollaborationMode(), + toggleFast: () => this.toggleFastMode(), + toggleRuntime: () => this.toggleRuntimePicker("model"), + connect: () => void this.reconnectFromToolbar(), + openCodexConfigFolder: () => void this.openCodexConfigFolder(), + refreshThreads: () => { + this.state.openDetails.delete("status-panel"); + void this.refreshThreads(); + }, + resumeThread: (threadId) => { + if (this.state.busy && threadId !== this.state.activeThreadId) return; + this.state.openDetails.delete("history"); + void this.resumeThread(threadId); + }, + archiveThread: (threadId) => void this.archiveThread(threadId), + startRenameThread: (threadId) => this.threadRename.start(threadId), + updateRenameDraft: (threadId, value) => this.threadRename.updateDraft(threadId, value), + saveRenameThread: (threadId, value) => void this.threadRename.save(threadId, value), + cancelRenameThread: (threadId) => this.threadRename.cancel(threadId), + autoNameThread: (threadId) => void this.threadRename.autoNameDraft(threadId), + }); + } + + private async openCodexConfigFolder(): Promise { + if (!Platform.isDesktopApp) { + new Notice("Opening .codex requires Obsidian desktop."); + return; + } + + try { + const shell = (require("electron") as { shell?: ElectronShell }).shell; + if (!shell) throw new Error("Electron shell is not available."); + + const vaultCodexPath = join(this.plugin.vaultPath, ".codex"); + const targetPath = existsSync(vaultCodexPath) + ? vaultCodexPath + : (this.state.initializeResponse?.codexHome ?? join(process.env.HOME ?? "", ".codex")); + const message = await shell.openPath(targetPath); + if (message) throw new Error(message); + if (targetPath !== vaultCodexPath) { + new Notice("Vault .codex folder not found. Opened Codex home instead."); + } + } catch (error) { + new Notice(`Could not open Codex config folder: ${errorMessage(error)}`); + } + } + + private toolbarViewModel(): ToolbarViewModel { + const snapshot = this.runtimeSnapshot(); + const config = configRecord(this.state.effectiveConfig); + const context = contextSummary(snapshot); + const limit = rateLimitSummary(snapshot); + const historyOpen = this.state.openDetails.has("history"); + const statusPanelOpen = this.state.openDetails.has("status-panel"); + const runtimeOpen = this.state.runtimePicker !== null; + const statusState = this.state.busy ? "running" : this.connection.isConnected() ? "connected" : "offline"; + const model = currentModel(snapshot, config); + const effort = currentReasoningEffort(snapshot, config); + const threads = this.state.listedThreads; + return { + connected: this.connection.isConnected(), + status: this.state.status, + statusState, + historyOpen, + statusPanelOpen, + runtimeOpen, + planActive: this.state.requestedCollaborationMode === "plan", + fastActive: currentServiceTier(snapshot, config) === "fast", + runtimeSummary: runtimeSummaryLabel(model, effort), + runtimeTitle: `Model: ${model ?? "(from default)"}; Effort: ${effort ?? "(from default)"}`, + runtimeAriaLabel: `Runtime: ${model ?? "default"} ${effort ?? "default"}`, + runtimeEmphasized: this.state.requestedModel.kind !== "default" || this.state.requestedReasoningEffort.kind !== "default", + context: context ? { ...context, label: compactContextLabel(context.percent, context.label) } : null, + rateLimit: limit, + configSections: effectiveConfigSections(snapshot, this.plugin.vaultPath), + openPanel: historyOpen ? "history" : runtimeOpen ? "runtime" : statusPanelOpen ? "status" : null, + threads: threads.map((thread) => { + const threadId = thread.id; + return { + title: getThreadTitle(thread), + threadId, + selected: threadId === this.state.activeThreadId, + disabled: this.state.busy && threadId !== this.state.activeThreadId, + canArchive: true, + rename: this.threadRename.editState(threadId), + }; + }), + modelChoices: this.modelToolbarChoices(), + effortChoices: this.effortToolbarChoices(), + connectLabel: this.connection.isConnected() ? "Reconnect" : "Connect", + diagnostics: this.connectionDiagnosticRows(), + }; + } + + private async reconnectFromToolbar(): Promise { + const threadId = this.state.activeThreadId; + this.state.openDetails.delete("status-panel"); + this.connection.reconnect(); + this.client = null; + this.state.busy = false; + this.state.activeTurnId = null; + this.state.approvals = []; + this.state.pendingUserInputs = []; + this.state.userInputDrafts.clear(); + this.setStatus("Reconnecting..."); + this.render(); + + await this.ensureConnected(); + if (!threadId || !this.client) return; + + try { + await this.resumeThread(threadId); + } catch (error) { + this.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + } + + private modelToolbarChoices(): ToolbarChoice[] { + const snapshot = this.runtimeSnapshot(); + const models = sortedAvailableModels(this.state.availableModels); + const choices: ToolbarChoice[] = [ + { + label: "Default", + selected: this.state.requestedModel.kind !== "set", + onClick: () => this.setRequestedModelFromUi(null), + }, + ]; + choices.push( + ...models.slice(0, 12).map((model) => ({ + label: model.model, + selected: currentModel(snapshot) === model.model, + onClick: () => this.setRequestedModelFromUi(model.model), + })), + ); + if (models.length === 0) { + choices.push({ + label: "No model list available.", + disabled: true, + onClick: () => undefined, + }); + } + return choices; + } + + private effortToolbarChoices(): ToolbarChoice[] { + const snapshot = this.runtimeSnapshot(); + return [ + { + label: "Default", + selected: this.state.requestedReasoningEffort.kind !== "set", + onClick: () => this.setRequestedReasoningEffortFromUi(null), + }, + ...supportedReasoningEfforts(snapshot).map((effort) => ({ + label: effort, + selected: currentReasoningEffort(snapshot) === effort, + onClick: () => this.setRequestedReasoningEffortFromUi(effort), + })), + ]; + } + + private toggleHistoryPanel(): void { + if (this.state.openDetails.has("history")) { + this.state.openDetails.delete("history"); + } else { + this.state.openDetails.delete("status-panel"); + this.state.runtimePicker = null; + this.state.openDetails.add("history"); + } + this.scheduleRender(); + } + + private closeToolbarPanelOnOutsidePointer(event: PointerEvent): void { + if (!this.hasOpenToolbarPanel()) return; + + const target = event.target; + if (target instanceof Element) { + const insideToolbarPanel = target.closest(".codex-panel__toolbar-primary, .codex-panel__toolbar-panel"); + if (insideToolbarPanel && this.containerEl.contains(insideToolbarPanel)) return; + } + + this.closeToolbarPanel(); + } + + private hasOpenToolbarPanel(): boolean { + return this.state.openDetails.has("history") || this.state.openDetails.has("status-panel") || this.state.runtimePicker !== null; + } + + private closeToolbarPanel(): void { + if (!this.hasOpenToolbarPanel()) return; + + this.state.openDetails.delete("history"); + this.state.openDetails.delete("status-panel"); + this.state.runtimePicker = null; + this.scheduleRender(); + } + + private scheduleRender(): void { + if (this.scheduledRenderTimer !== null) return; + this.scheduledRenderTimer = window.setTimeout(() => { + this.scheduledRenderTimer = null; + this.render(); + }, 50); + } + + private renderShell(root: HTMLElement): void { + root.empty(); + root.addClass("codex-panel"); + this.toolbarEl = root.createDiv({ cls: "codex-panel__toolbar" }); + const body = root.createDiv({ cls: "codex-panel__body" }); + this.configSlotEl = body.createDiv({ cls: "codex-panel__slot codex-panel__slot--config" }); + this.messagesSlotEl = body.createDiv({ cls: "codex-panel__slot codex-panel__slot--messages" }); + this.composerSlotEl = body.createDiv({ cls: "codex-panel__slot codex-panel__slot--composer" }); + } + + private toggleStatusPanel(): void { + if (this.state.openDetails.has("status-panel")) { + this.state.openDetails.delete("status-panel"); + } else { + this.state.openDetails.delete("history"); + this.state.runtimePicker = null; + this.state.openDetails.add("status-panel"); + } + this.scheduleRender(); + } + + private statusSummaryLines(): string[] { + const snapshot = this.runtimeSnapshot(); + const context = contextSummary(snapshot); + const config = configRecord(this.state.effectiveConfig); + const model = currentModel(snapshot, config) ?? "(from default)"; + const effort = currentReasoningEffort(snapshot, config); + return [ + "Session status", + `Status: ${this.state.status}`, + `Thread: ${this.state.activeThreadId ?? "(none)"}`, + `Turn: ${this.state.activeTurnId ?? "(none)"}`, + `Mode: ${this.collaborationModeLabel()}`, + `Runtime: ${model}${effort ? ` ${effort}` : ""}, fast ${fastModeLabel(snapshot, config)}`, + `Connection: ${this.connection.isConnected() ? "connected" : "offline"}`, + context ? context.title : "Context: not available", + ]; + } + + private modelStatusLines(): string[] { + const snapshot = this.runtimeSnapshot(); + const config = configRecord(this.state.effectiveConfig); + return [ + `Model: ${currentModel(snapshot, config) ?? "(from default)"}`, + `Override: ${runtimeOverrideLabel(this.state.requestedModel)}`, + `Provider: ${config.model_provider ?? "(from default)"}`, + `Effort: ${currentReasoningEffort(snapshot, config) ?? "(from default)"}`, + `Mode: ${this.collaborationModeLabel()}`, + `Service tier: ${serviceTierLabel(snapshot, config)}`, + ]; + } + + private effortStatusLines(): string[] { + const snapshot = this.runtimeSnapshot(); + const config = configRecord(this.state.effectiveConfig); + return [ + `Effort: ${currentReasoningEffort(snapshot, config) ?? "(from default)"}`, + `Override: ${runtimeOverrideLabel(this.state.requestedReasoningEffort)}`, + `Supported: ${supportedReasoningEfforts(snapshot).join(", ")}`, + ]; + } + + private connectionDiagnosticRows() { + return connectionDiagnosticRows({ + connected: this.connection.isConnected(), + configuredCommand: this.plugin.settings.codexPath, + initializeResponse: this.state.initializeResponse, + activeThreadCliVersion: this.state.activeThreadCliVersion, + compatibility: this.state.appServerCompatibility, + }); + } + + private connectionDiagnosticLines(): string[] { + return connectionDiagnosticLines(this.connectionDiagnosticRows()); + } + + private collaborationModeLabel(): string { + return formatCollaborationModeLabel(this.state.requestedCollaborationMode); + } + + private runtimeSnapshot(): RuntimeSnapshot { + return { + effectiveConfig: this.state.effectiveConfig, + activeThreadId: this.state.activeThreadId, + activeModel: this.state.activeModel, + activeServiceTier: this.state.activeServiceTier, + requestedModel: this.state.requestedModel, + requestedReasoningEffort: this.state.requestedReasoningEffort, + requestedCollaborationMode: this.state.requestedCollaborationMode, + requestedServiceTier: this.state.requestedServiceTier, + tokenUsage: this.state.tokenUsage, + rateLimit: this.state.rateLimit, + displayItems: this.state.displayItems, + availableModels: this.state.availableModels, + }; + } + + private renderPendingRequestMessage(parent: HTMLElement): void { + renderPendingRequestMessage( + parent, + this.state.approvals, + this.state.pendingUserInputs, + { + values: this.state.userInputDrafts, + draftKey: userInputDraftKey, + otherDraftKey: userInputOtherDraftKey, + }, + this.state.openDetails, + { + resolveApproval: (approval, action) => void this.resolveApproval(approval, action), + resolveUserInput: (input) => void this.resolveUserInput(input), + cancelUserInput: (input) => void this.cancelUserInput(input), + }, + ); + } + + private answersForUserInput(input: PendingUserInput): Record { + return Object.fromEntries( + input.params.questions.map((question) => [ + question.id, + this.state.userInputDrafts.get(userInputDraftKey(input.requestId, question.id)) ?? questionDefaultAnswer(question), + ]), + ); + } + + private async archiveThread(threadId: string): Promise { + if (this.state.busy) { + this.addSystemMessage("Finish or interrupt the current turn before archiving threads."); + return; + } + if (!this.client) return; + try { + await this.client.archiveThread(threadId); + if (this.state.activeThreadId === threadId) { + clearActiveThreadState(this.state); + this.threadRename.resetThreadTurnPresence(false); + } + await this.refreshThreads(); + this.render(); + } catch (error) { + this.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + } + + private forceMessagesToBottom(): void { + this.state.messagesPinnedToBottom = true; + this.forceScrollMessagesToBottomOnNextRender = true; + } + + private renderMessages(parent: HTMLElement): void { + const messagesEl = parent.querySelector(".codex-panel__messages") ?? parent.createDiv({ cls: "codex-panel__messages" }); + messagesEl.onscroll = () => { + this.state.messagesPinnedToBottom = isNearScrollBottom(messagesEl); + }; + const wasNearBottom = isNearScrollBottom(messagesEl); + const shouldScrollToBottom = this.forceScrollMessagesToBottomOnNextRender || wasNearBottom; + const scrollAnchor = shouldScrollToBottom ? null : captureScrollAnchor(messagesEl); + this.forceScrollMessagesToBottomOnNextRender = false; + this.state.messagesPinnedToBottom = shouldScrollToBottom; + + const blocks = messageRenderBlocks({ + activeThreadId: this.state.activeThreadId, + activeTurnId: this.state.activeTurnId, + historyCursor: this.state.historyCursor, + loadingHistory: this.state.loadingHistory, + busy: this.state.busy, + displayItems: this.state.displayItems, + workspaceRoot: this.state.activeThreadCwd ?? this.plugin.vaultPath, + openDetails: this.state.openDetails, + onDetailsToggle: () => { + requestAnimationFrame(() => { + this.state.messagesPinnedToBottom = isNearScrollBottom(messagesEl); + }); + }, + loadOlderTurns: () => void this.history.loadOlder(), + renderMarkdown: (element, text) => this.renderMarkdownMessage(element, text), + renderTextWithWikiLinks: (element, text) => this.renderTextWithWikiLinks(element, text), + pendingRequestsSignature: this.pendingRequestsSignature(), + renderPendingRequests: () => this.createPendingRequestsElement(), + }); + const existing = new Map(); + messagesEl.querySelectorAll(":scope > [data-codex-panel-block-key]").forEach((element) => { + const key = element.dataset.codexPanelBlockKey; + if (key) existing.set(key, element); + }); + + const seen = new Set(); + for (const block of blocks) { + const current = existing.get(block.key); + let element = current; + if (!element || this.blockSignatures.get(block.key) !== block.signature) { + element = block.render(); + element.dataset.codexPanelBlockKey = block.key; + element.dataset.codexPanelBlockSignature = shortSignature(block.signature); + this.blockSignatures.set(block.key, block.signature); + if (current) { + current.replaceWith(element); + } + } + messagesEl.appendChild(element); + seen.add(block.key); + } + + for (const [key, element] of existing) { + if (!seen.has(key)) { + this.blockSignatures.delete(key); + element.remove(); + } + } + + requestAnimationFrame(() => { + if (shouldScrollToBottom) { + messagesEl.scrollTop = bottomScrollTop(messagesEl); + } else { + restoreScrollAnchor(messagesEl, scrollAnchor); + } + this.state.messagesPinnedToBottom = isNearScrollBottom(messagesEl); + }); + } + + private renderMarkdownMessage(parent: HTMLElement, text: string): void { + const sourcePath = this.app.workspace.getActiveFile()?.path ?? ""; + void MarkdownRenderer.render(this.app, text, parent, sourcePath, this).then(() => { + this.bindRenderedWikiLinks(parent, sourcePath); + this.scrollMarkdownMessageIntoPinnedBottom(parent); + }); + } + + private pendingRequestsSignature(): string { + if (this.state.approvals.length === 0 && this.state.pendingUserInputs.length === 0) return ""; + return JSON.stringify({ + approvals: this.state.approvals.map((approval) => ({ id: approval.requestId, method: approval.method })), + inputs: this.state.pendingUserInputs.map((input) => ({ + id: input.requestId, + questions: input.params.questions.map((question) => ({ + id: question.id, + header: question.header, + question: question.question, + options: question.options?.map((option) => option.label) ?? null, + })), + })), + drafts: Array.from(this.state.userInputDrafts.entries()).sort(([left], [right]) => left.localeCompare(right)), + }); + } + + private createPendingRequestsElement(): HTMLElement | null { + if (this.state.approvals.length === 0 && this.state.pendingUserInputs.length === 0) return null; + const container = createDiv(); + this.renderPendingRequestMessage(container); + return container.firstElementChild as HTMLElement | null; + } + + private scrollMarkdownMessageIntoPinnedBottom(parent: HTMLElement): void { + if (!this.state.messagesPinnedToBottom) return; + const messagesEl = parent.closest(".codex-panel__messages"); + if (!messagesEl) return; + requestAnimationFrame(() => { + if (!this.state.messagesPinnedToBottom) return; + messagesEl.scrollTop = bottomScrollTop(messagesEl); + this.state.messagesPinnedToBottom = isNearScrollBottom(messagesEl); + }); + } + + private bindRenderedWikiLinks(parent: HTMLElement, sourcePath: string): void { + parent.querySelectorAll("a.internal-link").forEach((link) => { + link.addClass("codex-panel__wikilink"); + link.onclick = (event) => { + event.preventDefault(); + const target = link.getAttribute("data-href") ?? link.getAttribute("href") ?? link.textContent ?? ""; + if (target.trim().length > 0) { + void this.app.workspace.openLinkText(target, sourcePath, false); + } + }; + }); + } + + private renderTextWithWikiLinks(parent: HTMLElement, text: string): void { + renderInlineWikiLinks(parent, text, (target) => { + const sourcePath = this.app.workspace.getActiveFile()?.path ?? ""; + void this.app.workspace.openLinkText(target, sourcePath, false); + }); + } + + private renderComposer(parent: HTMLElement): void { + if (this.composer && parent.contains(this.composer)) { + return; + } + + const elements = renderComposerShell(parent, this.viewId, this.state.composerDraft, this.state.busy, { + onInput: () => { + this.state.composerDraft = this.composer?.value ?? ""; + this.updateComposerSuggestions(); + this.syncComposerControls(); + }, + onUpdateSuggestions: () => this.updateComposerSuggestions(), + onKeydown: (event) => { + if (this.handleComposerSuggestionKeydown(event)) { + return; + } + if ((event.metaKey || event.ctrlKey) && event.key === "Enter") { + event.preventDefault(); + void this.sendMessage(); + } + }, + onNewThread: () => void this.startNewThread(), + onSendOrInterrupt: () => { + const draft = this.composer?.value.trim() ?? this.state.composerDraft.trim(); + if (this.state.busy && this.state.activeThreadId && this.state.activeTurnId && draft.length === 0) { + void this.interruptTurn(); + } else { + void this.sendMessage(); + } + }, + onSuggestionHover: (index) => { + if (this.state.composerSuggestSelected === index) return; + this.state.composerSuggestSelected = index; + this.renderComposerSuggestions(); + }, + onSuggestionInsert: (suggestion) => this.insertComposerSuggestion(suggestion), + }); + this.composer = elements.composer; + this.composerSuggestEl = elements.suggestions; + this.updateComposerSuggestions(); + } + + private syncComposerControls(): void { + const canInterrupt = this.state.busy && Boolean(this.state.activeThreadId && this.state.activeTurnId); + syncComposerControlElements(this.composerSlotEl, this.composer, this.state.busy, canInterrupt); + } + + private handleComposerSuggestionKeydown(event: KeyboardEvent): boolean { + if (this.state.composerSuggestions.length === 0) return false; + + const direction = composerSuggestionNavigationDirection(event); + if (direction) { + event.preventDefault(); + this.state.composerSuggestSelected = nextComposerSuggestionIndex( + this.state.composerSuggestSelected, + this.state.composerSuggestions.length, + direction, + ); + this.renderComposerSuggestions(); + return true; + } + if (event.metaKey || event.ctrlKey) return false; + + if (event.key === "Enter" || event.key === "Tab") { + event.preventDefault(); + this.insertComposerSuggestion(this.state.composerSuggestions[this.state.composerSuggestSelected]); + return true; + } + + if (event.key === "Escape") { + event.preventDefault(); + this.clearComposerSuggestions(); + return true; + } + + return false; + } + + private updateComposerSuggestions(): void { + if (!this.composer) { + this.clearComposerSuggestions(); + return; + } + + const cursor = this.composer.selectionStart; + const beforeCursor = this.composer.value.slice(0, cursor); + const suggestions = activeComposerSuggestions(beforeCursor, this.noteCandidates(), this.state.availableSkills); + + this.state.composerSuggestions = suggestions; + if (this.state.composerSuggestSelected >= this.state.composerSuggestions.length) { + this.state.composerSuggestSelected = 0; + } + this.renderComposerSuggestions(); + } + + private renderComposerSuggestions(): void { + renderComposerSuggestions( + this.composerSuggestEl, + this.composer, + this.viewId, + this.state.composerSuggestions, + this.state.composerSuggestSelected, + { + onSuggestionHover: (index) => { + if (this.state.composerSuggestSelected === index) return; + this.state.composerSuggestSelected = index; + this.renderComposerSuggestions(); + }, + onSuggestionInsert: (suggestion) => this.insertComposerSuggestion(suggestion), + }, + ); + } + + private insertComposerSuggestion(suggestion: ComposerSuggestion | undefined): void { + if (!this.composer || !suggestion) return; + + const cursor = this.composer.selectionStart; + const value = this.composer.value; + const insertion = applyComposerSuggestionInsertion(value, cursor, suggestion); + + this.state.composerDraft = insertion.value; + this.composer.value = insertion.value; + syncComposerHeight(this.composer); + this.composer.focus(); + this.composer.setSelectionRange(insertion.cursor, insertion.cursor); + this.clearComposerSuggestions(); + } + + private clearComposerSuggestions(): void { + this.state.composerSuggestSelected = 0; + this.state.composerSuggestions = []; + this.composer?.setAttr("aria-expanded", "false"); + this.composer?.removeAttribute("aria-activedescendant"); + this.composerSuggestEl?.empty(); + this.composerSuggestEl?.hide(); + } + + private noteCandidates(): NoteCandidate[] { + if (!this.noteCandidatesCache) { + this.noteCandidatesCache = this.app.vault.getMarkdownFiles().map((file) => ({ + basename: file.basename, + path: file.path, + mtime: file.stat.mtime, + })); + } + return this.noteCandidatesCache; + } + + private codexInput(text: string): UserInput[] { + return userInputWithWikiLinkMentions(text, (target) => this.resolveWikiLinkMention(target)); + } + + private resolveWikiLinkMention(target: string): { name: string; path: string } | null { + const sourcePath = this.app.workspace.getActiveFile()?.path ?? ""; + const linkedFile = this.app.metadataCache.getFirstLinkpathDest(target, sourcePath); + if (linkedFile?.path) return { name: linkedFile.basename, path: linkedFile.path }; + + const directPath = target.endsWith(".md") ? target : `${target}.md`; + const abstractFile = this.app.vault.getAbstractFileByPath(directPath); + if (abstractFile instanceof TFile) return { name: abstractFile.basename, path: abstractFile.path }; + return null; + } + + private registerNoteIndexInvalidation(): void { + if (this.noteEventsRegistered) return; + this.noteEventsRegistered = true; + const invalidate = () => { + this.noteCandidatesCache = null; + }; + this.registerEvent(this.app.vault.on("create", invalidate)); + this.registerEvent(this.app.vault.on("delete", invalidate)); + this.registerEvent(this.app.vault.on("rename", invalidate)); + this.registerEvent(this.app.vault.on("modify", invalidate)); + } +} diff --git a/src/panel/app-server-logs.ts b/src/panel/app-server-logs.ts new file mode 100644 index 00000000..415d9872 --- /dev/null +++ b/src/panel/app-server-logs.ts @@ -0,0 +1,34 @@ +export type ClassifiedAppServerLog = { kind: "plain"; text: string } | { kind: "error"; text: string } | null; + +export function classifyAppServerLog(message: string): ClassifiedAppServerLog { + const normalized = stripAnsi(message).trimEnd(); + if (!normalized || isMcpTokenRefreshLog(normalized)) return null; + const parsed = parseAppServerLog(normalized); + if (!parsed) return null; + + const level = String(parsed.level ?? "").toUpperCase(); + const fields = parsed.fields && typeof parsed.fields === "object" ? (parsed.fields as Record) : {}; + const text = stripAnsi(String(fields.message ?? normalized)); + const target = String(parsed.target ?? ""); + if (target.includes("rmcp::transport::worker") && isMcpTokenRefreshLog(text)) return null; + if (target.includes("codex_core::tools::router") && text.includes("apply_patch verification failed")) return null; + if (level === "ERROR") return { kind: "error", text }; + return null; +} + +function parseAppServerLog(message: string): Record | null { + try { + const parsed = JSON.parse(message) as unknown; + return parsed && typeof parsed === "object" ? (parsed as Record) : null; + } catch { + return null; + } +} + +function isMcpTokenRefreshLog(message: string): boolean { + return message.includes("TokenRefreshFailed") || message.includes("Transport channel closed, when Auth"); +} + +function stripAnsi(message: string): string { + return message.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, ""); +} diff --git a/src/panel/collaboration-mode.ts b/src/panel/collaboration-mode.ts new file mode 100644 index 00000000..73c060fb --- /dev/null +++ b/src/panel/collaboration-mode.ts @@ -0,0 +1,37 @@ +import type { CollaborationMode } from "../generated/app-server/CollaborationMode"; +import type { ModeKind } from "../generated/app-server/ModeKind"; +import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; + +export function nextCollaborationMode(mode: ModeKind): ModeKind { + return mode === "plan" ? "default" : "plan"; +} + +export function collaborationModeLabel(mode: ModeKind): string { + return mode === "plan" ? "Plan" : "Default"; +} + +export function collaborationModeToggleMessage(mode: ModeKind): string { + return mode === "plan" ? "Plan mode on for subsequent turns." : "Plan mode off for subsequent turns."; +} + +export function planCollaborationMode(model: string, reasoningEffort: ReasoningEffort | null): CollaborationMode { + return { + mode: "plan", + settings: { + model, + reasoning_effort: reasoningEffort, + developer_instructions: null, + }, + }; +} + +export function defaultCollaborationMode(model: string, reasoningEffort: ReasoningEffort | null): CollaborationMode { + return { + mode: "default", + settings: { + model, + reasoning_effort: reasoningEffort, + developer_instructions: null, + }, + }; +} diff --git a/src/panel/controller.ts b/src/panel/controller.ts new file mode 100644 index 00000000..5552ea5c --- /dev/null +++ b/src/panel/controller.ts @@ -0,0 +1,390 @@ +import { approvalResponse, approvalTitle, toPendingApproval, type ApprovalAction, type PendingApproval } from "../approvals/model"; +import { + appendAssistantDelta, + appendItemOutput, + appendItemText, + appendPlanDelta, + appendToolOutput, + createAutoReviewResultItem, + completeReasoningItems, + createReviewResultItem, + createSystemItem, + displayItemFromThreadItem, + displayItemsFromTurns, + normalizeFileChanges, + planProgressDisplayItem, + shouldSuppressLifecycleItem, + shouldSuppressThreadItem, + upsertDisplayItem, +} from "../display/model"; +import type { DisplayItem, DisplayKind, MessageDisplayItem } from "../display/types"; +import type { RequestId } from "../generated/app-server/RequestId"; +import type { ServerNotification } from "../generated/app-server/ServerNotification"; +import type { ServerRequest } from "../generated/app-server/ServerRequest"; +import type { ThreadItem } from "../generated/app-server/v2/ThreadItem"; +import type { Turn } from "../generated/app-server/v2/Turn"; +import { clearActiveThreadState, type PanelState } from "../state/panel-state"; +import { toPendingUserInput, userInputResponse, type PendingUserInput } from "../user-input/model"; +import { jsonPreview } from "../utils"; +import { classifyAppServerLog } from "./app-server-logs"; +import { hookRunDisplayItem } from "./hook-display"; +import { clearUserInputDrafts, createUserInputResultItem } from "./request-state"; + +export interface PanelControllerActions { + refreshThreads: () => void; + maybeNameThread: (threadId: string, turn: Turn) => void; + respondToServerRequest: (requestId: RequestId, result: unknown) => boolean; + rejectServerRequest: (requestId: RequestId, code: number, message: string) => boolean; +} + +export class PanelController { + constructor( + private readonly state: PanelState, + private readonly actions: PanelControllerActions, + ) {} + + handleNotification(notification: ServerNotification): void { + if (!this.isInActiveScope(notification)) return; + + const { method, params } = notification; + if (method === "turn/started") { + this.state.activeThreadId = params.threadId; + this.state.activeTurnId = params.turn.id ?? this.state.activeTurnId; + this.state.busy = true; + this.state.status = "Turn running..."; + } else if (method === "item/agentMessage/delta") { + this.state.displayItems = completeReasoningItems(this.state.displayItems, params.turnId); + this.state.displayItems = appendAssistantDelta(this.state.displayItems, params.itemId, params.turnId, params.delta ?? ""); + } else if (method === "item/plan/delta") { + this.state.displayItems = appendPlanDelta(this.state.displayItems, params.itemId, params.turnId, params.delta ?? ""); + } else if (method === "turn/plan/updated") { + this.state.displayItems = upsertDisplayItem( + this.state.displayItems, + planProgressDisplayItem(params.turnId, params.explanation ?? null, params.plan ?? []), + ); + } else if (method === "item/reasoning/summaryTextDelta") { + this.appendToolText(params.itemId, params.turnId, "reasoning", params.delta ?? "", "reasoning"); + } else if (method === "item/reasoning/textDelta") { + this.appendToolText(params.itemId, params.turnId, "reasoning", params.delta ?? "", "reasoning"); + } else if (method === "item/reasoning/summaryPartAdded") { + this.appendToolText(params.itemId, params.turnId, "reasoning", "", "reasoning"); + } else if (method === "item/started") { + this.handleStartedItem(params.item, params.turnId); + } else if (method === "item/completed") { + this.handleCompletedItem(params.item, params.turnId); + } else if (method === "item/commandExecution/outputDelta") { + this.state.displayItems = appendItemOutput( + this.state.displayItems, + params.itemId, + params.turnId, + params.delta ?? "", + "command", + "Command running", + ); + } else if (method === "item/fileChange/patchUpdated") { + this.upsertFileChange(params.itemId, params.turnId, params.changes ?? [], "inProgress"); + } else if (method === "item/fileChange/outputDelta") { + this.state.displayItems = appendItemOutput( + this.state.displayItems, + params.itemId, + params.turnId, + params.delta ?? "", + "fileChange", + "File change inProgress", + ); + } else if (method === "turn/completed") { + this.reconcileCompletedTurn(params.turn); + this.state.displayItems = completeReasoningItems(this.state.displayItems, params.turn.id); + this.state.busy = false; + this.state.activeTurnId = null; + this.state.status = `Turn ${params.turn.status ?? "completed"}.`; + this.actions.maybeNameThread(params.threadId, params.turn); + this.actions.refreshThreads(); + } else if (method === "thread/tokenUsage/updated") { + this.state.tokenUsage = params.tokenUsage ?? null; + } else if (method === "account/rateLimits/updated") { + this.state.rateLimit = params.rateLimits ?? null; + } else if (method === "hook/started") { + this.upsertHookRun(params.run, params.turnId, "running"); + } else if (method === "hook/completed") { + this.upsertHookRun(params.run, params.turnId, params.run?.status ?? "completed"); + } else if (method === "item/mcpToolCall/progress") { + this.state.displayItems = appendToolOutput( + this.state.displayItems, + params.itemId, + params.turnId, + params.message ?? "", + "mcp progress", + ); + } else if (method === "item/autoApprovalReview/started" || method === "item/autoApprovalReview/completed") { + this.state.displayItems = upsertDisplayItem(this.state.displayItems, createAutoReviewResultItem(params)); + } else if (method === "thread/started") { + if (params.thread) { + if (!this.state.activeThreadId || this.state.activeThreadId === params.thread.id) { + this.state.activeThreadCwd = params.thread.cwd ?? this.state.activeThreadCwd; + } + } + } else if (method === "thread/archived") { + this.state.listedThreads = this.state.listedThreads.filter((thread) => thread.id !== params.threadId); + if (this.state.activeThreadId === params.threadId) { + clearActiveThreadState(this.state); + } + } else if (method === "thread/unarchived") { + this.actions.refreshThreads(); + } else if (method === "thread/name/updated") { + const name = typeof params.threadName === "string" && params.threadName.trim() ? params.threadName.trim() : null; + this.state.listedThreads = this.state.listedThreads.map((thread) => (thread.id === params.threadId ? { ...thread, name } : thread)); + this.actions.refreshThreads(); + } else if (method === "serverRequest/resolved") { + this.state.approvals = this.state.approvals.filter((approval) => approval.requestId !== params.requestId); + const resolvedInputs = this.state.pendingUserInputs.filter((input) => input.requestId === params.requestId); + this.state.pendingUserInputs = this.state.pendingUserInputs.filter((input) => input.requestId !== params.requestId); + for (const input of resolvedInputs) clearUserInputDrafts(this.state.userInputDrafts, input); + } else if (method === "thread/compacted") { + this.addSystemMessage("Context compacted."); + } else if (method === "guardianWarning") { + this.state.displayItems.push(createReviewResultItem(params.message)); + } else if (method === "model/rerouted" || method === "deprecationNotice") { + this.addSystemMessage(`${method}: ${jsonPreview(params)}`); + } else if (method === "mcpServer/startupStatus/updated") { + this.handleMcpStartupStatus(params); + } else if (method === "error" || method === "warning" || method === "configWarning") { + this.addSystemMessage(`${method}: ${jsonPreview(params)}`); + } + } + + handleServerRequest(request: ServerRequest): void { + if (!this.isRequestInActiveScope(request)) { + this.rejectServerRequest(request, `Rejected inactive app-server request: ${request.method}`); + return; + } + + const approval = toPendingApproval(request); + if (approval) { + this.queueApprovalRequest(approval); + return; + } + + const userInput = toPendingUserInput(request); + if (userInput) { + this.queueUserInputRequest(userInput); + return; + } + + this.rejectUnsupportedServerRequest(request); + } + + handleAppServerLog(message: string): void { + const classified = classifyAppServerLog(message); + if (classified === null) return; + if (classified.kind === "plain") { + this.addDedupedSystemMessage(classified.text); + } else if (classified.kind === "error") { + this.addDedupedSystemMessage(`app-server error: ${classified.text}`); + } + } + + resolveApproval(approval: PendingApproval, action: ApprovalAction): void { + if (!this.state.approvals.some((item) => item.requestId === approval.requestId)) return; + if (!this.actions.respondToServerRequest(approval.requestId, approvalResponse(approval, action))) { + this.addSystemMessage("Could not send approval response because Codex app-server is not connected."); + return; + } + this.state.approvals = this.state.approvals.filter((item) => item.requestId !== approval.requestId); + this.state.displayItems.push({ + id: `approval-${String(approval.requestId)}`, + kind: "approvalResult", + role: "tool", + text: `${approvalTitle(approval)}: ${action}`, + turnId: approvalTurnId(approval), + markdown: false, + }); + } + + resolveUserInput(input: PendingUserInput, answers: Record): void { + if (!this.state.pendingUserInputs.some((item) => item.requestId === input.requestId)) return; + if (!this.actions.respondToServerRequest(input.requestId, userInputResponse(input, answers))) { + this.addSystemMessage("Could not send user input because Codex app-server is not connected."); + return; + } + this.state.pendingUserInputs = this.state.pendingUserInputs.filter((item) => item.requestId !== input.requestId); + clearUserInputDrafts(this.state.userInputDrafts, input); + this.state.displayItems.push(createUserInputResultItem(input, answers, "submitted")); + } + + cancelUserInput(input: PendingUserInput): void { + if (!this.state.pendingUserInputs.some((item) => item.requestId === input.requestId)) return; + if (!this.actions.rejectServerRequest(input.requestId, -32000, "User cancelled input request.")) { + this.addSystemMessage("Could not cancel user input because Codex app-server is not connected."); + return; + } + this.state.pendingUserInputs = this.state.pendingUserInputs.filter((item) => item.requestId !== input.requestId); + clearUserInputDrafts(this.state.userInputDrafts, input); + this.state.displayItems.push(createUserInputResultItem(input, {}, "cancelled")); + } + + addSystemMessage(text: string): void { + this.state.displayItems.push(createSystemItem(text)); + } + + addDedupedSystemMessage(text: string): void { + if (this.state.reportedLogs.has(text)) return; + this.state.reportedLogs.add(text); + this.addSystemMessage(text); + } + + private queueApprovalRequest(approval: PendingApproval): void { + if (!this.state.approvals.some((existing) => existing.requestId === approval.requestId)) { + this.state.approvals.push(approval); + } + } + + private queueUserInputRequest(userInput: PendingUserInput): void { + if (!this.state.pendingUserInputs.some((existing) => existing.requestId === userInput.requestId)) { + this.state.pendingUserInputs.push(userInput); + } + } + + private rejectUnsupportedServerRequest(request: ServerRequest): void { + const message = `Rejected unsupported app-server request: ${request.method}`; + this.rejectServerRequest(request, message); + } + + private rejectServerRequest(request: ServerRequest, message: string): void { + this.addSystemMessage(message); + if (!this.actions.rejectServerRequest(request.id, -32601, message)) { + this.addSystemMessage("Could not reject app-server request because Codex app-server is not connected."); + } + } + + private isInActiveScope(notification: ServerNotification): boolean { + const threadId = notificationThreadId(notification); + if (threadId && this.state.activeThreadId && threadId !== this.state.activeThreadId) { + if (notification.method === "thread/started") return true; + return false; + } + + const turnId = notificationTurnId(notification); + if (turnId && this.state.activeTurnId && turnId !== this.state.activeTurnId) return false; + return true; + } + + private isRequestInActiveScope(request: ServerRequest): boolean { + const threadId = messageThreadId(request); + if (threadId && this.state.activeThreadId && threadId !== this.state.activeThreadId) return false; + + const turnId = messageTurnId(request); + if (turnId && this.state.activeTurnId && turnId !== this.state.activeTurnId) return false; + return true; + } + + private handleStartedItem(item: ThreadItem, turnId: string): void { + if (!item || shouldSuppressThreadItem(item) || shouldSuppressLifecycleItem(item)) return; + const displayItem = displayItemFromThreadItem(item, turnId); + if (displayItem) this.state.displayItems = upsertDisplayItem(this.state.displayItems, displayItem); + } + + private handleCompletedItem(item: ThreadItem, turnId: string): void { + if (!item || shouldSuppressThreadItem(item) || item.type === "userMessage") return; + const displayItem = displayItemFromThreadItem(item, turnId); + if (displayItem) { + this.state.displayItems = upsertDisplayItem(this.state.displayItems, displayItem); + if (displayItem.kind === "reasoning") { + this.state.displayItems = completeReasoningItems(this.state.displayItems, turnId); + } + } + } + + private reconcileCompletedTurn(turn: Turn): void { + const turnItems = displayItemsFromTurns([turn]); + if (turnItems.length === 0) return; + const serverUserTexts = new Set(turnItems.filter(isUserMessage).map((item) => item.text)); + let mergedTurnItems = this.state.displayItems + .filter((item) => item.turnId === turn.id) + .filter((item) => !isOptimisticUserMessage(item, serverUserTexts)); + for (const item of turnItems) { + mergedTurnItems = upsertDisplayItem(mergedTurnItems, item); + } + const retainedItems = this.state.displayItems + .filter((item) => item.turnId !== turn.id) + .filter((item) => !isOptimisticUserMessage(item, serverUserTexts)); + this.state.displayItems = [...retainedItems, ...mergedTurnItems]; + } + + private upsertFileChange(itemId: string, turnId: string, changes: unknown[], status: string): void { + this.state.displayItems = upsertDisplayItem(this.state.displayItems, { + id: itemId, + kind: "fileChange", + role: "tool", + text: `File change ${status}`, + turnId, + itemId, + status, + changes: normalizeFileChanges(changes), + }); + } + + private appendToolText( + itemId: string, + turnId: string, + label: string, + delta: string, + kind: Extract = "tool", + ): void { + this.state.displayItems = appendItemText(this.state.displayItems, itemId, turnId, label, delta, kind); + } + + private upsertHookRun( + run: Extract["params"]["run"], + turnId: string | null, + status: string, + ): void { + const item = hookRunDisplayItem(run, turnId, status); + if (item) this.state.displayItems = upsertDisplayItem(this.state.displayItems, item); + } + + private handleMcpStartupStatus(params: Extract["params"]): void { + if (!params?.name) return; + if (params.status === "failed") { + const key = `${params.name}:${params.error ?? ""}`; + if (this.state.reportedMcpFailures.has(key)) return; + this.state.reportedMcpFailures.add(key); + this.addSystemMessage( + `MCP server failed to start: ${params.name}\n${params.error ?? ""}\n\nThis is non-fatal for the Codex thread unless that MCP server is needed.`, + ); + } + } +} + +function notificationThreadId(notification: ServerNotification): string | null { + return messageThreadId(notification); +} + +function notificationTurnId(notification: ServerNotification): string | null { + return messageTurnId(notification); +} + +function messageThreadId(message: ServerNotification | ServerRequest): string | null { + const params = message.params as { threadId?: unknown }; + if (typeof params.threadId === "string") return params.threadId; + return null; +} + +function messageTurnId(message: ServerNotification | ServerRequest): string | null { + const params = message.params as { turnId?: unknown; turn?: { id?: unknown } }; + if (typeof params.turnId === "string") return params.turnId; + return typeof params.turn?.id === "string" ? params.turn.id : null; +} + +function approvalTurnId(approval: PendingApproval): string | undefined { + const params = approval.params as { turnId?: unknown }; + return typeof params.turnId === "string" ? params.turnId : undefined; +} + +function isUserMessage(item: DisplayItem): item is MessageDisplayItem & { role: "user" } { + return item.kind === "message" && item.role === "user"; +} + +function isOptimisticUserMessage(item: DisplayItem, serverUserTexts: Set): boolean { + return isUserMessage(item) && (item.id.startsWith("local-user-") || item.id.startsWith("local-steer-")) && serverUserTexts.has(item.text); +} diff --git a/src/panel/diagnostics.ts b/src/panel/diagnostics.ts new file mode 100644 index 00000000..9fdcce7e --- /dev/null +++ b/src/panel/diagnostics.ts @@ -0,0 +1,43 @@ +import { appServerIdentity, appServerPlatform, compatibilitySummary } from "../app-server/compatibility"; +import { CLIENT_VERSION } from "../constants"; +import type { InitializeResponse } from "../generated/app-server/InitializeResponse"; +import type { AppServerCompatibility } from "../app-server/compatibility"; + +export interface DiagnosticRow { + label: string; + value: string; + level?: "normal" | "warning" | "error"; +} + +export interface ConnectionDiagnosticsInput { + connected: boolean; + configuredCommand: string; + initializeResponse: InitializeResponse | null; + activeThreadCliVersion: string | null; + compatibility: AppServerCompatibility; +} + +export function connectionDiagnosticRows(input: ConnectionDiagnosticsInput): DiagnosticRow[] { + const rows: DiagnosticRow[] = [ + { label: "connection", value: input.connected ? "connected" : "offline" }, + { label: "configured command", value: input.configuredCommand }, + { label: "running app-server", value: appServerIdentity(input.initializeResponse) }, + { label: "panel client", value: CLIENT_VERSION }, + { label: "platform", value: appServerPlatform(input.initializeResponse) }, + { label: "codexHome", value: input.initializeResponse?.codexHome ?? "(not connected)" }, + { label: "active thread CLI", value: input.activeThreadCliVersion ?? "(none)" }, + { + label: "compatibility", + value: compatibilitySummary(input.compatibility), + level: input.compatibility.modelList === "failed" ? "error" : "normal", + }, + ]; + if (input.compatibility.modelListError) { + rows.push({ label: "model/list error", value: input.compatibility.modelListError, level: "error" }); + } + return rows; +} + +export function connectionDiagnosticLines(rows: DiagnosticRow[]): string[] { + return ["Connection diagnostics", ...rows.map((row) => `${row.label}: ${row.value}`)]; +} diff --git a/src/panel/hook-display.ts b/src/panel/hook-display.ts new file mode 100644 index 00000000..c47e36f2 --- /dev/null +++ b/src/panel/hook-display.ts @@ -0,0 +1,36 @@ +import type { ServerNotification } from "../generated/app-server/ServerNotification"; +import type { DisplayItem } from "../display/types"; + +export function hookRunDisplayItem( + run: Extract["params"]["run"], + turnId: string | null, + status: string, +): DisplayItem | null { + if (!run?.id) return null; + const entries = (run.entries ?? []).map((entry) => `${entry.kind}: ${entry.text}`).join("\n"); + const metaRows = [ + { key: "status", value: status }, + { key: "event", value: run.eventName }, + ...(run.statusMessage ? [{ key: "message", value: run.statusMessage }] : []), + ...(run.durationMs !== null && run.durationMs !== undefined ? [{ key: "duration", value: `${run.durationMs}ms` }] : []), + ]; + const details = [{ rows: metaRows }, ...(entries ? [{ title: "Hook output", body: entries }] : [])]; + return { + id: `hook-${run.id}`, + kind: "hook", + role: "tool", + text: hookSummary(run.eventName, run.statusMessage), + toolLabel: "hook", + turnId: turnId ?? undefined, + itemId: `hook-${run.id}`, + status, + details, + output: "", + }; +} + +function hookSummary(eventName: string | null | undefined, statusMessage: string | null | undefined): string { + const event = eventName?.trim() || "Hook"; + const message = statusMessage?.trim(); + return message ? `${event}: ${message}` : event; +} diff --git a/src/panel/model-runtime.ts b/src/panel/model-runtime.ts new file mode 100644 index 00000000..4700623c --- /dev/null +++ b/src/panel/model-runtime.ts @@ -0,0 +1,28 @@ +import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; +import type { Model } from "../generated/app-server/v2/Model"; + +export const REASONING_EFFORTS: ReasoningEffort[] = ["none", "minimal", "low", "medium", "high", "xhigh"]; + +export function isReasoningEffort(value: unknown): value is ReasoningEffort { + return typeof value === "string" && (REASONING_EFFORTS as string[]).includes(value); +} + +export function normalizeReasoningEffort(value: unknown): ReasoningEffort | null { + return isReasoningEffort(value) ? value : null; +} + +export function sortedAvailableModels(models: Model[]): Model[] { + return [...models] + .filter((model) => !model.hidden) + .sort((a, b) => Number(b.isDefault) - Number(a.isDefault) || a.model.localeCompare(b.model)); +} + +export function findModelByIdOrName(models: Model[], modelIdOrName: string | null | undefined): Model | null { + if (!modelIdOrName) return null; + return models.find((model) => !model.hidden && (model.model === modelIdOrName || model.id === modelIdOrName)) ?? null; +} + +export function supportedEffortsForModel(model: Model | null): ReasoningEffort[] { + const efforts = model?.supportedReasoningEfforts.map((option) => option.reasoningEffort).filter(isReasoningEffort) ?? []; + return efforts.length > 0 ? efforts : REASONING_EFFORTS; +} diff --git a/src/panel/request-state.ts b/src/panel/request-state.ts new file mode 100644 index 00000000..03d6d9c0 --- /dev/null +++ b/src/panel/request-state.ts @@ -0,0 +1,44 @@ +import type { RequestId } from "../generated/app-server/RequestId"; +import type { DisplayDetailSection, DisplayItem } from "../display/types"; +import type { PendingUserInput } from "../user-input/model"; + +export function userInputDraftKey(requestId: RequestId, questionId: string): string { + return `${String(requestId)}:${questionId}`; +} + +export function userInputOtherDraftKey(requestId: RequestId, questionId: string): string { + return `${String(requestId)}:${questionId}:other`; +} + +export function clearUserInputDrafts(drafts: Map, input: PendingUserInput): void { + for (const question of input.params.questions) { + drafts.delete(userInputDraftKey(input.requestId, question.id)); + drafts.delete(userInputOtherDraftKey(input.requestId, question.id)); + } +} + +export function createUserInputResultItem( + input: PendingUserInput, + answers: Record, + status: "submitted" | "cancelled", +): DisplayItem { + const questionCount = input.params.questions.length; + const label = questionCount === 1 ? "1 question" : `${questionCount} questions`; + const details: DisplayDetailSection[] = input.params.questions.map((question) => ({ + title: question.header || question.id, + rows: [ + { key: "question", value: question.question }, + ...(status === "submitted" ? [{ key: "answer", value: answers[question.id] ?? "" }] : []), + ], + })); + return { + id: `user-input-${status}-${String(input.requestId)}`, + kind: "userInputResult", + role: "tool", + text: status === "submitted" ? `Input submitted for ${label}.` : `Input request cancelled for ${label}.`, + turnId: input.params.turnId, + markdown: false, + state: status === "submitted" ? "completed" : "failed", + details, + }; +} diff --git a/src/panel/runtime-settings.ts b/src/panel/runtime-settings.ts new file mode 100644 index 00000000..8994a8d1 --- /dev/null +++ b/src/panel/runtime-settings.ts @@ -0,0 +1,44 @@ +import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; +import { isReasoningEffort } from "./model-runtime"; + +const DEFAULT_ALIASES = new Set(["default", "reset", "clear", "off"]); + +export function parseModelOverride(args: string): string | null | undefined { + const model = args.trim(); + if (!model) return undefined; + if (DEFAULT_ALIASES.has(model.toLowerCase())) return null; + return model; +} + +export function parseReasoningEffortOverride(args: string): ReasoningEffort | null | undefined { + const effort = args.trim().toLowerCase(); + if (!effort) return undefined; + if (DEFAULT_ALIASES.has(effort)) return null; + return isReasoningEffort(effort) ? effort : undefined; +} + +export function modelOverrideMessage(model: string | null): string { + return model === null ? "Model reset to default for subsequent turns." : `Model set to ${model} for subsequent turns.`; +} + +export function reasoningEffortOverrideMessage(effort: ReasoningEffort | null): string { + return effort === null ? "Effort reset to default for subsequent turns." : `Effort set to ${effort} for subsequent turns.`; +} + +export function compactModelLabel(model: string | null): string { + if (!model) return "default"; + const match = /^gpt-(.+)$/.exec(model); + return match?.[1] ?? model; +} + +export function compactReasoningEffortLabel(effort: ReasoningEffort | null): string { + if (!effort) return "default"; + if (effort === "minimal") return "min"; + return effort; +} + +export function compactContextLabel(percent: number | null, label: string): string { + if (percent !== null) return `${percent}%`; + if (label === "Context unknown") return "?"; + return label === "Context waiting" ? "wait" : label.replace(/^Context\s+/i, ""); +} diff --git a/src/panel/runtime-state.ts b/src/panel/runtime-state.ts new file mode 100644 index 00000000..dcf8fa83 --- /dev/null +++ b/src/panel/runtime-state.ts @@ -0,0 +1,146 @@ +import type { CollaborationMode } from "../generated/app-server/CollaborationMode"; +import type { ModeKind } from "../generated/app-server/ModeKind"; +import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; +import type { ConfigReadResponse } from "../generated/app-server/v2/ConfigReadResponse"; +import type { Model } from "../generated/app-server/v2/Model"; +import type { RateLimitSnapshot } from "../generated/app-server/v2/RateLimitSnapshot"; +import type { ThreadTokenUsage } from "../generated/app-server/v2/ThreadTokenUsage"; +import type { DisplayItem } from "../display/types"; +import { parseServiceTier, serviceTierRequestValue, type ServiceTier, type ServiceTierRequest } from "../app-server/service-tier"; +import { defaultCollaborationMode, planCollaborationMode } from "./collaboration-mode"; +import { findModelByIdOrName, isReasoningEffort, supportedEffortsForModel } from "./model-runtime"; +import { compactModelLabel, compactReasoningEffortLabel } from "./runtime-settings"; +export { sortedAvailableModels } from "./model-runtime"; + +export type RuntimeOverride = { kind: "default" } | { kind: "set"; value: T } | { kind: "resetPending" }; + +export interface RuntimeSnapshot { + effectiveConfig: ConfigReadResponse | null; + activeThreadId: string | null; + activeModel: string | null; + activeServiceTier: string | null; + requestedModel: RuntimeOverride; + requestedReasoningEffort: RuntimeOverride; + requestedCollaborationMode: ModeKind; + requestedServiceTier: ServiceTier | null; + tokenUsage: ThreadTokenUsage | null; + rateLimit: RateLimitSnapshot | null; + displayItems: DisplayItem[]; + availableModels: Model[]; +} + +export interface TurnRuntimeSettings { + collaborationMode: CollaborationMode | null; + model: string | null | undefined; + effort: ReasoningEffort | null | undefined; + warning: string | null; +} + +export function configRecord(effectiveConfig: ConfigReadResponse | null): Record { + return asRecord(effectiveConfig?.config); +} + +export function currentServiceTier(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): string | null { + return ( + snapshot.requestedServiceTier ?? + parseServiceTier(snapshot.activeServiceTier) ?? + snapshot.activeServiceTier ?? + configuredServiceTier(config) + ); +} + +export function requestedOrConfiguredServiceTier( + snapshot: RuntimeSnapshot, + config = configRecord(snapshot.effectiveConfig), +): ServiceTierRequest { + return serviceTierRequestValue(snapshot.requestedServiceTier ?? configuredServiceTier(config)); +} + +export function currentModel(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): string | null { + const model = config.model; + const configModel = typeof model === "string" && model.length > 0 ? model : null; + if (snapshot.requestedModel.kind === "set") return snapshot.requestedModel.value; + if (snapshot.requestedModel.kind === "resetPending" && configModel) return configModel; + return snapshot.activeModel ?? configModel; +} + +export function currentReasoningEffort(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): ReasoningEffort | null { + if (snapshot.requestedReasoningEffort.kind === "set") return snapshot.requestedReasoningEffort.value; + const effort = config.model_reasoning_effort; + return isReasoningEffort(effort) ? effort : null; +} + +export function requestedTurnRuntimeSettings(snapshot: RuntimeSnapshot): TurnRuntimeSettings { + const model = currentModel(snapshot); + const effort = currentReasoningEffort(snapshot); + const collaborationMode = model + ? snapshot.requestedCollaborationMode === "plan" + ? planCollaborationMode(model, effort) + : defaultCollaborationMode(model, effort) + : null; + return { + collaborationMode, + model: runtimeOverridePayload(snapshot.requestedModel), + effort: runtimeOverridePayload(snapshot.requestedReasoningEffort), + warning: model ? null : "No effective model is available. Sending without a mode override.", + }; +} + +export function supportedReasoningEfforts(snapshot: RuntimeSnapshot): ReasoningEffort[] { + const model = currentModel(snapshot); + return supportedEffortsForModel(findModelByIdOrName(snapshot.availableModels, model)); +} + +export function runtimeSummaryLabel(model: string | null, effort: ReasoningEffort | null): string { + const modelLabel = compactModelLabel(model); + if (!effort) return modelLabel; + return `${modelLabel} ${compactReasoningEffortLabel(effort)}`; +} + +export function serviceTierLabel(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): string { + return currentServiceTier(snapshot, config) ?? "(not reported)"; +} + +export function fastModeLabel(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): string { + const serviceTier = currentServiceTier(snapshot, config); + if (serviceTier === "fast") return "on"; + if (serviceTier === "standard") return "off"; + if (serviceTier) return `unknown (${serviceTier})`; + return "not reported"; +} + +export function defaultRuntimeOverride(): RuntimeOverride { + return { kind: "default" }; +} + +export function setRuntimeOverride(value: T): RuntimeOverride { + return { kind: "set", value }; +} + +export function resetRuntimeOverride(): RuntimeOverride { + return { kind: "resetPending" }; +} + +export function commitRuntimeOverride(override: RuntimeOverride): RuntimeOverride { + return override.kind === "resetPending" ? defaultRuntimeOverride() : override; +} + +export function runtimeOverridePayload(override: RuntimeOverride): T | null | undefined { + if (override.kind === "set") return override.value; + if (override.kind === "resetPending") return null; + return undefined; +} + +export function runtimeOverrideLabel(override: RuntimeOverride): string { + if (override.kind === "set") return String(override.value); + if (override.kind === "resetPending") return "(reset pending)"; + return "(default)"; +} + +function configuredServiceTier(config: Record): ServiceTier | null { + return parseServiceTier(config.service_tier); +} + +function asRecord(value: unknown): Record { + return value && typeof value === "object" ? (value as Record) : {}; +} diff --git a/src/panel/runtime-view.ts b/src/panel/runtime-view.ts new file mode 100644 index 00000000..ad309cf2 --- /dev/null +++ b/src/panel/runtime-view.ts @@ -0,0 +1,258 @@ +import type { RateLimitWindow } from "../generated/app-server/v2/RateLimitWindow"; +import type { ThreadTokenUsage } from "../generated/app-server/v2/ThreadTokenUsage"; +import { jsonPreview } from "../utils"; +import { + configRecord, + currentModel, + currentReasoningEffort, + fastModeLabel, + runtimeOverrideLabel, + serviceTierLabel, + type RuntimeSnapshot, +} from "./runtime-state"; + +export interface ContextSummary { + label: string; + title: string; + percent: number | null; + level: "ok" | "warn" | "danger"; +} + +export interface RateLimitSummary { + title: string; + level: "ok" | "warn" | "danger"; + rows: RateLimitSummaryRow[]; +} + +export interface RateLimitSummaryRow { + label: string; + value: string; + resetLabel: string | null; + title: string; + percent: number; + level: "ok" | "warn" | "danger"; +} + +export interface EffectiveConfigSection { + title: string; + rows: Array<{ key: string; value: string }>; +} + +export function contextSummary(snapshot: RuntimeSnapshot): ContextSummary | null { + const usage = snapshot.tokenUsage; + const config = configRecord(snapshot.effectiveConfig); + const contextWindow = usage?.modelContextWindow ?? toNumber(config.model_context_window); + if (!usage) { + if (!snapshot.activeThreadId) return null; + if (!snapshot.displayItems.some((item) => item.turnId)) { + return { + label: "Context 0%", + title: contextWindow + ? `Context: 0 / ${formatTokenCount(contextWindow)} (0%). No turns in this thread yet.` + : "Context: 0 tokens. No turns in this thread yet.", + percent: 0, + level: "ok", + }; + } + return { + label: "Context unknown", + title: contextWindow + ? `Context usage is not available for this thread yet. It will update after the next token usage report. Context window: ${formatTokenCount(contextWindow)} tokens.` + : "Context usage is not available for this thread yet. It will update after the next token usage report.", + percent: null, + level: "ok", + }; + } + + const used = contextUsageTokens(usage); + const percent = contextWindow ? Math.min(100, Math.round((used / contextWindow) * 100)) : null; + const level = percent !== null && percent >= 90 ? "danger" : percent !== null && percent >= 70 ? "warn" : "ok"; + const title = contextWindow + ? `Context: ${formatTokenCount(used)} / ${formatTokenCount(contextWindow)} (${percent}%). Last request: ${formatTokenCount(usage.last.inputTokens)} input, ${formatTokenCount(usage.last.outputTokens)} output, ${formatTokenCount(usage.last.reasoningOutputTokens)} reasoning. Session total: ${formatTokenCount(usage.total.totalTokens)} tokens.` + : `Context: ${formatTokenCount(used)} tokens. Last request: ${formatTokenCount(usage.last.totalTokens)} total. Session total: ${formatTokenCount(usage.total.totalTokens)} tokens.`; + return { + label: percent === null ? `${formatTokenCount(used)} tokens` : `Context ${percent}%`, + title, + percent, + level, + }; +} + +export function rateLimitSummary(snapshot: RuntimeSnapshot, nowMs = Date.now()): RateLimitSummary | null { + const rateLimit = snapshot.rateLimit; + if (!rateLimit) return null; + + const name = rateLimit.limitName ?? rateLimit.limitId ?? "Codex limit"; + const reached = rateLimit.rateLimitReachedType !== null; + const rows = [ + rateLimitWindowSummary("primary", rateLimit.primary, name, reached, rateLimit.rateLimitReachedType, nowMs), + rateLimitWindowSummary("secondary", rateLimit.secondary, name, reached, rateLimit.rateLimitReachedType, nowMs), + ].filter((row): row is RateLimitSummaryRow => row !== null); + if (rows.length === 0) return null; + + const level = rows.some((row) => row.level === "danger") ? "danger" : rows.some((row) => row.level === "warn") ? "warn" : "ok"; + return { + title: `${name}: ${rows.map((row) => `${row.label} ${row.value}`).join(", ")}`, + rows, + level, + }; +} + +export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: string): EffectiveConfigSection[] { + const config = configRecord(snapshot.effectiveConfig); + const features = asRecord(config.features); + const tools = asRecord(config.tools); + const workspaceWrite = asRecord(config.sandbox_workspace_write); + return [ + { + title: "Scope", + rows: [ + { key: "cwd", value: vaultPath }, + { key: "profile", value: stringValue(config.profile, "(default)") }, + { key: "model provider", value: stringValue(config.model_provider, "(from default)") }, + ], + }, + { + title: "Runtime", + rows: [ + { key: "model", value: currentModel(snapshot, config) ?? "(from default)" }, + { key: "model override", value: runtimeOverrideLabel(snapshot.requestedModel) }, + { key: "effort", value: currentReasoningEffort(snapshot, config) ?? "(from default)" }, + { key: "effort override", value: runtimeOverrideLabel(snapshot.requestedReasoningEffort) }, + { key: "reasoning summary", value: stringValue(config.model_reasoning_summary, "(from default)") }, + { key: "verbosity", value: stringValue(config.model_verbosity, "(from default)") }, + { key: "mode", value: snapshot.requestedCollaborationMode === "plan" ? "Plan" : "Default" }, + { key: "service tier", value: serviceTierLabel(snapshot, config) }, + { key: "fast mode", value: fastModeLabel(snapshot, config) }, + { key: "context window", value: tokenLimitLabel(config.model_context_window) }, + { key: "auto compact limit", value: tokenLimitLabel(config.model_auto_compact_token_limit) }, + ], + }, + { + title: "Policy", + rows: [ + { key: "approval", value: stringValue(config.approval_policy, "(from default)") }, + { key: "reviewer", value: stringValue(config.approvals_reviewer, "(from default)") }, + { key: "sandbox", value: stringValue(config.sandbox_mode, "(from default)") }, + { key: "workspace network", value: stringValue(workspaceWrite.network_access, "(from default)") }, + { key: "writable roots", value: writableRootsLabel(workspaceWrite.writable_roots) }, + { key: "web search", value: stringValue(config.web_search, "(from default)") }, + ], + }, + { + title: "Features", + rows: [ + { key: "hooks", value: stringValue(features.hooks, "(from default)") }, + { key: "apply patch freeform", value: stringValue(features.apply_patch_freeform, "(from default)") }, + { key: "tool web search", value: stringValue(tools.web_search, "(from default)") }, + { key: "tool view image", value: stringValue(tools.view_image, "(from default)") }, + { key: "apps", value: enabledAppsLabel(config.apps) }, + ], + }, + ]; +} + +function contextUsageTokens(usage: ThreadTokenUsage): number { + return usage.last.inputTokens > 0 ? usage.last.inputTokens : usage.last.totalTokens; +} + +function rateLimitWindowSummary( + fallbackLabel: string, + window: RateLimitWindow | null, + name: string, + reached: boolean, + reachedType: string | null, + nowMs: number, +): RateLimitSummaryRow | null { + if (!window) return null; + + const percent = Math.max(0, Math.min(100, Math.round(window.usedPercent))); + const level = reached || percent >= 90 ? "danger" : percent >= 70 ? "warn" : "ok"; + const label = window.windowDurationMins ? formatRateLimitDuration(window.windowDurationMins) : fallbackLabel; + const resetLabel = window.resetsAt ? formatRateLimitRemaining(window.resetsAt, nowMs) : null; + const resetText = window.resetsAt && resetLabel ? ` ${capitalize(resetLabel)}. Reset at ${formatRateLimitReset(window.resetsAt)}.` : ""; + const reachedText = reached ? ` ${String(reachedType)}.` : ""; + return { + label, + value: `${percent}%`, + resetLabel, + title: `${name} ${label}: ${percent}% used.${resetText}${reachedText}`, + percent, + level, + }; +} + +function formatRateLimitDuration(minutes: number): string { + if (minutes === 10_080) return "1w"; + if (minutes % 60 === 0) return `${minutes / 60}h`; + return `${minutes}m`; +} + +function formatRateLimitReset(resetsAt: number): string { + return new Date(resetsAt * 1000).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" }); +} + +function formatRateLimitRemaining(resetsAt: number, nowMs: number): string { + const remainingSeconds = Math.ceil((resetsAt * 1000 - nowMs) / 1000); + if (remainingSeconds <= 0) return "reset due"; + if (remainingSeconds < 60) return "reset in <1m"; + + const minutes = Math.ceil(remainingSeconds / 60); + if (minutes < 60) return `reset in ${minutes}m`; + + const hours = Math.floor(minutes / 60); + const remainderMinutes = minutes % 60; + if (hours < 24) return `reset in ${hours}h${remainderMinutes > 0 ? ` ${remainderMinutes}m` : ""}`; + + const days = Math.floor(hours / 24); + const remainderHours = hours % 24; + return `reset in ${days}d${remainderHours > 0 ? ` ${remainderHours}h` : ""}`; +} + +function capitalize(value: string): string { + return value.length === 0 ? value : value.charAt(0).toUpperCase() + value.slice(1); +} + +function tokenLimitLabel(value: unknown): string { + const tokens = toNumber(value); + return tokens === null ? "(from default)" : formatTokenCount(tokens); +} + +function writableRootsLabel(value: unknown): string { + if (!Array.isArray(value)) return "(from default)"; + if (value.length === 0) return "none"; + if (value.length === 1) return String(value[0]); + return `${value.length} roots`; +} + +function enabledAppsLabel(value: unknown): string { + const apps = asRecord(value); + const enabled = Object.entries(apps) + .filter(([key, app]) => key !== "_default" && asRecord(app).enabled === true) + .map(([key]) => key) + .sort(); + if (enabled.length > 0) return enabled.join(", "); + const defaultConfig = asRecord(apps._default); + return stringValue(defaultConfig.enabled, "(from default)"); +} + +function asRecord(value: unknown): Record { + return value && typeof value === "object" ? (value as Record) : {}; +} + +function stringValue(value: unknown, fallback = ""): string { + if (typeof value === "string") return value; + if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value); + if (value === null || value === undefined) return fallback; + return jsonPreview(value); +} + +function toNumber(value: unknown): number | null { + if (typeof value === "number") return Number.isFinite(value) ? value : null; + if (typeof value === "bigint") return Number(value); + return null; +} + +function formatTokenCount(value: number): string { + return new Intl.NumberFormat("en-US").format(Math.round(value)); +} diff --git a/src/panel/session-controller.ts b/src/panel/session-controller.ts new file mode 100644 index 00000000..ebd78153 --- /dev/null +++ b/src/panel/session-controller.ts @@ -0,0 +1,92 @@ +import type { AppServerClient } from "../app-server/client"; +import { requestedOrConfiguredServiceTier, type RuntimeSnapshot } from "./runtime-state"; +import type { PanelState } from "../state/panel-state"; + +export interface PanelSessionControllerHost { + state: PanelState; + vaultPath: string; + currentClient: () => AppServerClient | null; + runtimeSnapshot: () => RuntimeSnapshot; + setStatus: (status: string) => void; + addSystemMessage: (text: string) => void; + addDedupedSystemMessage: (text: string) => void; + forceMessagesToBottom: () => void; +} + +export class PanelSessionController { + constructor(private readonly host: PanelSessionControllerHost) {} + + async refreshThreadList(): Promise { + const client = this.host.currentClient(); + if (!client) return; + const response = await client.listThreads(this.host.vaultPath); + this.host.state.listedThreads = response.data; + this.host.state.threadsLoaded = true; + } + + async refreshSessionMetadata(): Promise { + const client = this.host.currentClient(); + if (!client) return; + this.host.state.effectiveConfig = await client.readEffectiveConfig(this.host.vaultPath); + await this.refreshModels(); + await this.refreshSkills(); + await this.refreshRateLimits(); + } + + async startThread(): Promise> | null> { + const client = this.host.currentClient(); + if (!client) return null; + const serviceTier = requestedOrConfiguredServiceTier(this.host.runtimeSnapshot()); + const response = await client.startThread(this.host.vaultPath, serviceTier); + this.host.state.activeThreadId = response.thread.id; + this.host.state.activeThreadCwd = response.cwd ?? response.thread.cwd ?? this.host.vaultPath; + this.host.state.activeTurnId = null; + this.host.state.activeModel = response.model ?? null; + this.host.state.activeServiceTier = response.serviceTier ?? null; + this.host.state.activeThreadCliVersion = response.thread.cliVersion ?? null; + this.host.state.tokenUsage = null; + this.host.state.historyCursor = null; + this.host.forceMessagesToBottom(); + return response; + } + + async refreshModels(): Promise { + const client = this.host.currentClient(); + if (!client) return; + try { + const response = await client.listModels(false); + this.host.state.availableModels = response.data; + this.host.state.appServerCompatibility.modelList = "ok"; + this.host.state.appServerCompatibility.modelListError = null; + } catch (error) { + this.host.state.availableModels = []; + const message = error instanceof Error ? error.message : String(error); + this.host.state.appServerCompatibility.modelList = "failed"; + this.host.state.appServerCompatibility.modelListError = message; + this.host.addDedupedSystemMessage(`Could not load Codex models: ${message}`); + } + } + + async refreshSkills(): Promise { + const client = this.host.currentClient(); + if (!client) return; + try { + const response = await client.listSkills(this.host.vaultPath); + this.host.state.availableSkills = response.data.flatMap((entry) => entry.skills).filter((skill) => skill.enabled); + } catch (error) { + this.host.state.availableSkills = []; + this.host.addDedupedSystemMessage(`Could not load Codex skills: ${error instanceof Error ? error.message : String(error)}`); + } + } + + async refreshRateLimits(): Promise { + const client = this.host.currentClient(); + if (!client) return; + try { + const response = await client.readAccountRateLimits(); + this.host.state.rateLimit = response.rateLimitsByLimitId?.codex ?? response.rateLimits ?? null; + } catch { + this.host.state.rateLimit = null; + } + } +} diff --git a/src/panel/slash-commands.ts b/src/panel/slash-commands.ts new file mode 100644 index 00000000..9b1c9f8b --- /dev/null +++ b/src/panel/slash-commands.ts @@ -0,0 +1,117 @@ +import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; +import { modelOverrideMessage, parseModelOverride, parseReasoningEffortOverride, reasoningEffortOverrideMessage } from "./runtime-settings"; + +export const SLASH_COMMANDS = [ + { command: "/compact", detail: "Compact the current conversation context." }, + { command: "/fast", detail: "Toggle fast service tier for subsequent turns." }, + { command: "/plan", detail: "Toggle Plan mode, optionally sending a message." }, + { command: "/status", detail: "Show current session and runtime status." }, + { command: "/doctor", detail: "Show Codex CLI and app-server diagnostics." }, + { command: "/model", detail: "Show or set the model for subsequent turns." }, + { command: "/effort", detail: "Show or set reasoning effort for subsequent turns." }, + { command: "/help", detail: "Show available Codex slash commands." }, +] as const; + +type SlashCommand = (typeof SLASH_COMMANDS)[number]["command"]; + +export type SlashCommandName = SlashCommand extends `/${infer Name}` ? Name : never; + +export function slashCommandHelpLines(): string[] { + return SLASH_COMMANDS.map((item) => `${item.command} - ${item.detail}`); +} + +export interface SlashCommandExecutionContext { + activeThreadId: string | null; + compactThread: (threadId: string) => Promise; + toggleFastMode: () => void; + toggleCollaborationMode: () => void; + addSystemMessage: (text: string) => void; + setStatus: (status: string) => void; + setRequestedModel: (model: string | null) => void; + setRequestedReasoningEffort: (effort: ReasoningEffort | null) => void; + statusSummaryLines: () => string[]; + connectionDiagnosticLines: () => string[]; + modelStatusLines: () => string[]; + effortStatusLines: () => string[]; +} + +export interface SlashCommandExecutionResult { + sendText?: string; +} + +export async function executeSlashCommand( + command: SlashCommandName, + args: string, + context: SlashCommandExecutionContext, +): Promise { + if (command === "compact") { + if (!context.activeThreadId) { + context.addSystemMessage("No active thread to compact."); + return; + } + try { + await context.compactThread(context.activeThreadId); + context.addSystemMessage("Compaction requested."); + context.setStatus("Compaction requested."); + } catch (error) { + context.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + return; + } + + if (command === "fast") { + context.toggleFastMode(); + return; + } + + if (command === "plan") { + context.toggleCollaborationMode(); + if (args) return { sendText: args }; + return; + } + + if (command === "status") { + context.addSystemMessage(context.statusSummaryLines().join("\n")); + return; + } + + if (command === "doctor") { + context.addSystemMessage(context.connectionDiagnosticLines().join("\n")); + return; + } + + if (command === "model") { + const requested = parseModelOverride(args); + if (requested !== undefined) { + context.setRequestedModel(requested); + context.addSystemMessage(modelOverrideMessage(requested)); + return; + } + context.addSystemMessage(context.modelStatusLines().join("\n")); + return; + } + + if (command === "effort") { + const requested = parseReasoningEffortOverride(args); + if (requested !== undefined) { + context.setRequestedReasoningEffort(requested); + context.addSystemMessage(reasoningEffortOverrideMessage(requested)); + return; + } + if (args) { + context.addSystemMessage(`Unsupported effort: ${args}`); + return; + } + context.addSystemMessage(context.effortStatusLines().join("\n")); + return; + } + + if (command === "help") { + context.addSystemMessage(slashCommandHelpLines().join("\n")); + return; + } + + if (args) { + context.addSystemMessage(`Unsupported slash command arguments: ${args}`); + } +} diff --git a/src/panel/thread-history.ts b/src/panel/thread-history.ts new file mode 100644 index 00000000..80f46cf0 --- /dev/null +++ b/src/panel/thread-history.ts @@ -0,0 +1,76 @@ +import type { AppServerClient } from "../app-server/client"; +import { displayItemsFromTurns } from "../display/model"; +import type { PanelState } from "../state/panel-state"; + +export interface ThreadHistoryLoaderHost { + state: PanelState; + currentClient: () => AppServerClient | null; + render: () => void; + addSystemMessage: (text: string) => void; + forceMessagesToBottom: () => void; + keepCurrentScrollPosition: () => void; + setThreadTurnPresence: (hadTurns: boolean) => void; +} + +export class ThreadHistoryLoader { + private generation = 0; + + constructor(private readonly host: ThreadHistoryLoaderHost) {} + + async loadLatest(threadId = this.host.state.activeThreadId): Promise { + const client = this.host.currentClient(); + if (!client || !threadId) return; + const generation = ++this.generation; + this.host.state.loadingHistory = true; + this.host.render(); + try { + const response = await client.threadTurnsList(threadId, null, 20); + if (this.isStale(generation, threadId)) return; + this.host.setThreadTurnPresence(response.data.length > 0); + this.host.state.historyCursor = response.nextCursor; + this.host.state.displayItems = displayItemsFromTurns(response.data); + this.host.forceMessagesToBottom(); + } catch (error) { + if (this.isStale(generation, threadId)) return; + this.host.addSystemMessage(error instanceof Error ? error.message : String(error)); + } finally { + if (!this.isStale(generation, threadId)) { + this.host.state.loadingHistory = false; + this.host.render(); + } + } + } + + async loadOlder(): Promise { + const client = this.host.currentClient(); + const state = this.host.state; + if (!client || !state.activeThreadId || !state.historyCursor || state.loadingHistory) return; + const threadId = state.activeThreadId; + const cursor = state.historyCursor; + const generation = ++this.generation; + state.loadingHistory = true; + this.host.render(); + try { + const response = await client.threadTurnsList(threadId, cursor, 20); + if (this.isStale(generation, threadId)) return; + state.historyCursor = response.nextCursor; + const olderItems = displayItemsFromTurns(response.data); + const existingIds = new Set(state.displayItems.map((item) => item.id)); + state.messagesPinnedToBottom = false; + this.host.keepCurrentScrollPosition(); + state.displayItems = [...olderItems.filter((item) => !existingIds.has(item.id)), ...state.displayItems]; + } catch (error) { + if (this.isStale(generation, threadId)) return; + this.host.addSystemMessage(error instanceof Error ? error.message : String(error)); + } finally { + if (!this.isStale(generation, threadId)) { + state.loadingHistory = false; + this.host.render(); + } + } + } + + private isStale(generation: number, threadId: string): boolean { + return generation !== this.generation || this.host.state.activeThreadId !== threadId; + } +} diff --git a/src/panel/thread-naming.ts b/src/panel/thread-naming.ts new file mode 100644 index 00000000..0ab9649f --- /dev/null +++ b/src/panel/thread-naming.ts @@ -0,0 +1,332 @@ +import { AppServerClient } from "../app-server/client"; +import type { DisplayItem } from "../display/types"; +import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; +import type { ServerNotification } from "../generated/app-server/ServerNotification"; +import type { JsonValue } from "../generated/app-server/serde_json/JsonValue"; +import type { Model } from "../generated/app-server/v2/Model"; +import type { SortDirection } from "../generated/app-server/v2/SortDirection"; +import type { ThreadItem } from "../generated/app-server/v2/ThreadItem"; +import type { Turn } from "../generated/app-server/v2/Turn"; +import { inputToText, truncate } from "../utils"; +import { findModelByIdOrName, supportedEffortsForModel } from "./model-runtime"; + +const NAMING_SERVICE_NAME = "codex-panel-naming"; +const NAMING_TIMEOUT_MS = 60_000; +const MAX_CONTEXT_CHARS = 4_000; +const MAX_TITLE_CHARS = 40; +const DEFAULT_CONTEXT_PAGE_LIMIT = 20; +const DEFAULT_CONTEXT_MAX_PAGES = 5; + +export const THREAD_NAMING_CONTEXT_UNAVAILABLE_MESSAGE = + "Auto-name needs completed history or visible resumed history with both user and assistant text."; + +const TITLE_OUTPUT_SCHEMA: JsonValue = { + type: "object", + properties: { + title: { + type: "string", + minLength: 1, + maxLength: MAX_TITLE_CHARS, + }, + }, + required: ["title"], + additionalProperties: false, +}; + +const TITLE_DEVELOPER_INSTRUCTIONS = [ + "You generate short Japanese titles for Codex chat history.", + "Return only a JSON object matching the requested schema.", + "Do not include Markdown, quotes around the whole response, explanations, or alternatives.", +].join("\n"); + +export interface ThreadNamingContext { + userRequest: string; + assistantResponse: string; +} + +export interface ThreadNamingRuntimeSettings { + threadNamingModel: string | null; + threadNamingEffort: ReasoningEffort | null; +} + +export interface ThreadNamingContextPage { + data: Turn[]; + nextCursor: string | null; +} + +export type ThreadNamingContextPageReader = ( + threadId: string, + cursor: string | null, + limit: number, + sortDirection: SortDirection, +) => Promise; + +export async function generateThreadTitleWithCodex( + codexPath: string, + cwd: string, + context: ThreadNamingContext, + runtimeSettings: ThreadNamingRuntimeSettings, +): Promise { + let namingThreadId: string | null = null; + let expectedTurnId: string | null = null; + let completed = false; + let timeout: ReturnType | null = null; + let rejectCompletedTurn: ((error: Error) => void) | null = null; + let handleNamingNotification: (notification: ServerNotification) => void = () => undefined; + const completedItems: ThreadItem[] = []; + + const completedTurn = new Promise((resolve, reject) => { + rejectCompletedTurn = reject; + timeout = globalThis.setTimeout(() => { + if (completed) return; + completed = true; + reject(new Error("Timed out while generating a Codex thread title.")); + }, NAMING_TIMEOUT_MS); + + const resolveIfNamingTurn = (notification: ServerNotification): void => { + if (completed) return; + if (notification.method === "item/completed") { + if (!namingThreadId || notification.params.threadId !== namingThreadId) return; + if (expectedTurnId && notification.params.turnId !== expectedTurnId) return; + completedItems.push(notification.params.item); + return; + } + if (notification.method === "turn/completed") { + if (!namingThreadId || notification.params.threadId !== namingThreadId) return; + if (expectedTurnId && notification.params.turn.id !== expectedTurnId) return; + completed = true; + resolve(turnWithCollectedItems(notification.params.turn, completedItems)); + } + }; + + handleNamingNotification = resolveIfNamingTurn; + }); + + let client!: AppServerClient; + client = new AppServerClient(codexPath, cwd, { + onNotification: (notification) => handleNamingNotification(notification), + onServerRequest: (request) => + client.rejectServerRequest(request.id, -32601, "Thread title generation does not handle server requests."), + onLog: () => undefined, + onExit: () => { + if (completed) return; + completed = true; + rejectCompletedTurn?.(new Error("Codex title generation app-server exited.")); + }, + }); + + try { + await client.connect(); + const runtime = await namingRuntimeForClient(client, runtimeSettings); + const threadResponse = await client.startEphemeralThread(cwd, NAMING_SERVICE_NAME, TITLE_DEVELOPER_INSTRUCTIONS); + namingThreadId = threadResponse.thread.id; + const turnResponse = await client.startStructuredTurn( + namingThreadId, + cwd, + namingPrompt(context), + TITLE_OUTPUT_SCHEMA, + runtime.model, + runtime.effort, + ); + expectedTurnId = turnResponse.turn.id; + const turn = turnResponse.turn.status === "completed" ? turnWithCollectedItems(turnResponse.turn, completedItems) : await completedTurn; + return titleFromNamingTurn(turn); + } finally { + completed = true; + if (timeout) globalThis.clearTimeout(timeout); + client.disconnect(); + } +} + +export function namingContextFromTurn(turn: Turn): ThreadNamingContext | null { + if (turn.status !== "completed") return null; + + const userRequest = firstUserMessage(turn.items); + const assistantResponse = lastAssistantMessage(turn.items); + if (!userRequest || !assistantResponse) return null; + + return { + userRequest: truncateForPrompt(userRequest), + assistantResponse: truncateForPrompt(assistantResponse), + }; +} + +export function namingContextFromDisplayItems(turnId: string, items: DisplayItem[]): ThreadNamingContext | null { + const turnItems = items.filter((item) => item.turnId === turnId); + const userRequest = turnItems.find((item) => item.kind === "message" && item.role === "user")?.text.trim() ?? ""; + const assistantResponse = + [...turnItems] + .reverse() + .find((item) => item.kind === "message" && item.role === "assistant") + ?.text.trim() ?? ""; + if (!userRequest || !assistantResponse) return null; + return { + userRequest: truncateForPrompt(userRequest), + assistantResponse: truncateForPrompt(assistantResponse), + }; +} + +export function firstNamingContextFromDisplayItems(items: DisplayItem[]): ThreadNamingContext | null { + const turnIds = new Set(); + for (const item of items) { + if (!item.turnId || turnIds.has(item.turnId)) continue; + turnIds.add(item.turnId); + const context = namingContextFromDisplayItems(item.turnId, items); + if (context) return context; + } + return null; +} + +export async function findThreadNamingContext(options: { + threadId: string; + readTurns: ThreadNamingContextPageReader; + fallbackDisplayItems?: DisplayItem[] | null; + pageLimit?: number; + maxPages?: number; +}): Promise { + const pageLimit = options.pageLimit ?? DEFAULT_CONTEXT_PAGE_LIMIT; + const maxPages = options.maxPages ?? DEFAULT_CONTEXT_MAX_PAGES; + let cursor: string | null = null; + + for (let page = 0; page < maxPages; page += 1) { + const response = await options.readTurns(options.threadId, cursor, pageLimit, "asc"); + for (const turn of response.data) { + const context = namingContextFromTurn(turn); + if (context) return context; + } + if (!response.nextCursor) break; + cursor = response.nextCursor; + } + + return options.fallbackDisplayItems ? firstNamingContextFromDisplayItems(options.fallbackDisplayItems) : null; +} + +export function titleFromNamingTurn(turn: Turn): string | null { + const response = lastAssistantMessage(turn.items); + if (!response) return null; + return normalizeGeneratedTitle(extractTitleFromModelText(response)); +} + +export interface NamingRuntime { + model?: string; + effort?: ReasoningEffort; +} + +export function namingRuntime(settings: ThreadNamingRuntimeSettings): NamingRuntime { + return { + ...(settings.threadNamingModel ? { model: settings.threadNamingModel } : {}), + ...(settings.threadNamingEffort ? { effort: settings.threadNamingEffort } : {}), + }; +} + +export function validatedNamingRuntime(settings: ThreadNamingRuntimeSettings, models: Model[]): NamingRuntime { + const runtime = namingRuntime(settings); + if (!runtime.model || !runtime.effort) return runtime; + + const model = findModelByIdOrName(models, runtime.model); + if (!model) return runtime; + + const supportedEfforts = new Set(supportedEffortsForModel(model)); + return supportedEfforts.has(runtime.effort) ? runtime : { model: runtime.model }; +} + +function turnWithCollectedItems(turn: Turn, items: ThreadItem[]): Turn { + if (turn.items.length > 0 || items.length === 0) return turn; + return { ...turn, items: [...items], itemsView: "full" }; +} + +async function namingRuntimeForClient(client: AppServerClient, settings: ThreadNamingRuntimeSettings): Promise { + const runtime = namingRuntime(settings); + if (!runtime.model || !runtime.effort) return runtime; + try { + const response = await client.listModels(false); + return validatedNamingRuntime(settings, response.data); + } catch { + return runtime; + } +} + +export function normalizeGeneratedTitle(value: unknown): string | null { + if (typeof value !== "string") return null; + const title = value + .trim() + .replace(/[\r\n\t]+/g, " ") + .replace(/\s+/g, " ") + .replace(/^#+\s*/, "") + .replace(/^[-*]\s*/, "") + .replace(/^["'`「『]+/, "") + .replace(/["'`」』]+$/, "") + .trim(); + if (!title) return null; + return title.length > MAX_TITLE_CHARS ? title.slice(0, MAX_TITLE_CHARS).trimEnd() : title; +} + +function firstUserMessage(items: ThreadItem[]): string | null { + for (const item of items) { + if (item.type !== "userMessage") continue; + const text = inputToText(item.content).trim(); + if (text) return text; + } + return null; +} + +function lastAssistantMessage(items: ThreadItem[]): string | null { + for (let index = items.length - 1; index >= 0; index -= 1) { + const item = items[index]; + if (item.type !== "agentMessage" && item.type !== "plan") continue; + const text = item.text.trim(); + if (text) return text; + } + return null; +} + +function namingPrompt(context: ThreadNamingContext): string { + return [ + "次のCodex会話につける履歴用タイトルを作ってください。", + "", + "条件:", + "- 日本語の短い名詞句または短い文", + "- 12〜28字程度、最大40字", + "- 依頼の対象と目的が分かる", + "- 「について」「相談」「実装してください」だけの曖昧な表現にしない", + "- Markdown、引用符、末尾句点、説明文は使わない", + "", + "ユーザーの初回依頼:", + context.userRequest, + "", + "Codexの最初の回答:", + context.assistantResponse, + ].join("\n"); +} + +function extractTitleFromModelText(text: string): unknown { + const trimmed = stripCodeFence(text.trim()); + const objectText = extractJsonObject(trimmed) ?? trimmed; + try { + const parsed = JSON.parse(objectText) as unknown; + if (parsed && typeof parsed === "object" && "title" in parsed) { + return (parsed as { title?: unknown }).title; + } + } catch { + return trimmed; + } + return trimmed; +} + +function stripCodeFence(text: string): string { + return text + .replace(/^```(?:json)?\s*/i, "") + .replace(/\s*```$/, "") + .trim(); +} + +function extractJsonObject(text: string): string | null { + const start = text.indexOf("{"); + const end = text.lastIndexOf("}"); + if (start < 0 || end <= start) return null; + return text.slice(start, end + 1); +} + +function truncateForPrompt(text: string): string { + return truncate(text.replace(/\s+/g, " ").trim(), MAX_CONTEXT_CHARS); +} diff --git a/src/panel/thread-rename.ts b/src/panel/thread-rename.ts new file mode 100644 index 00000000..4782d36b --- /dev/null +++ b/src/panel/thread-rename.ts @@ -0,0 +1,199 @@ +import type { AppServerClient } from "../app-server/client"; +import type { Thread } from "../generated/app-server/v2/Thread"; +import type { Turn } from "../generated/app-server/v2/Turn"; +import type { CodexPanelSettings } from "../settings"; +import type { PanelState } from "../state/panel-state"; +import { getThreadTitle } from "../threads"; +import { + findThreadNamingContext, + generateThreadTitleWithCodex, + namingContextFromDisplayItems, + namingContextFromTurn, + THREAD_NAMING_CONTEXT_UNAVAILABLE_MESSAGE, + type ThreadNamingContext, +} from "./thread-naming"; + +export interface ThreadRenameEditState { + draft: string; + generating: boolean; +} + +export interface ThreadRenameControllerHost { + state: PanelState; + vaultPath: string; + settings: () => CodexPanelSettings; + ensureConnected: () => Promise; + currentClient: () => AppServerClient | null; + refreshThreads: () => Promise; + render: () => void; + addSystemMessage: (text: string) => void; +} + +export class ThreadRenameController { + private activeThreadHadTurns = false; + private readonly autoNameAttemptedThreadIds = new Set(); + private readonly autoNameInFlightThreadIds = new Set(); + private renameThreadId: string | null = null; + private renameDraft = ""; + private renameAutoNameThreadId: string | null = null; + private renameAutoNameGeneration = 0; + + constructor(private readonly host: ThreadRenameControllerHost) {} + + resetThreadTurnPresence(hadTurns: boolean): void { + this.activeThreadHadTurns = hadTurns; + } + + editState(threadId: string): ThreadRenameEditState | null { + if (this.renameThreadId !== threadId) return null; + return { + draft: this.renameDraft, + generating: this.renameAutoNameThreadId === threadId, + }; + } + + start(threadId: string): void { + const thread = this.thread(threadId); + if (!thread) return; + this.renameAutoNameGeneration += 1; + this.renameAutoNameThreadId = null; + this.renameThreadId = threadId; + this.renameDraft = getThreadTitle(thread); + this.host.render(); + } + + updateDraft(threadId: string, value: string): void { + if (this.renameThreadId !== threadId) return; + this.renameDraft = value; + } + + cancel(threadId: string): void { + if (this.renameThreadId !== threadId) return; + this.clear(); + this.host.render(); + } + + async save(threadId: string, value: string): Promise { + if (this.renameThreadId !== threadId || this.renameAutoNameThreadId === threadId) return; + const title = value.trim(); + if (!title) { + this.cancel(threadId); + return; + } + + await this.host.ensureConnected(); + const client = this.host.currentClient(); + if (!client) return; + + try { + await client.setThreadName(threadId, title); + this.host.state.listedThreads = this.host.state.listedThreads.map((thread) => + thread.id === threadId ? { ...thread, name: title } : thread, + ); + this.clear(); + await this.host.refreshThreads(); + } catch (error) { + this.host.addSystemMessage(error instanceof Error ? error.message : String(error)); + } finally { + this.host.render(); + } + } + + async autoNameDraft(threadId: string): Promise { + if (this.renameThreadId !== threadId || this.renameAutoNameThreadId === threadId) return; + + await this.host.ensureConnected(); + const generation = this.renameAutoNameGeneration + 1; + const draftBeforeGeneration = this.renameDraft; + this.renameAutoNameGeneration = generation; + this.renameAutoNameThreadId = threadId; + this.host.render(); + + try { + const context = await this.resolveNamingContext(threadId); + if (!context) throw new Error(THREAD_NAMING_CONTEXT_UNAVAILABLE_MESSAGE); + const title = await this.generateTitle(context); + if (!title) throw new Error("Codex did not return a usable thread title."); + if (this.renameThreadId !== threadId || this.renameAutoNameGeneration !== generation) return; + if (this.renameDraft !== draftBeforeGeneration) return; + this.renameDraft = title; + } catch (error) { + if (this.renameThreadId === threadId && this.renameAutoNameGeneration === generation) { + this.host.addSystemMessage(error instanceof Error ? error.message : String(error)); + } + } finally { + if (this.renameThreadId === threadId && this.renameAutoNameGeneration === generation) { + this.renameAutoNameThreadId = null; + this.host.render(); + } + } + } + + maybeAutoNameThread(threadId: string, turn: Turn): void { + const hadTurnsBeforeThisCompletion = this.activeThreadHadTurns; + this.activeThreadHadTurns = true; + + if (hadTurnsBeforeThisCompletion || turn.status !== "completed") return; + if (this.threadHasName(threadId)) return; + if (this.autoNameAttemptedThreadIds.has(threadId) || this.autoNameInFlightThreadIds.has(threadId)) return; + const context = namingContextFromTurn(turn) ?? namingContextFromDisplayItems(turn.id, this.host.state.displayItems); + if (!context) return; + + this.autoNameAttemptedThreadIds.add(threadId); + this.autoNameInFlightThreadIds.add(threadId); + void this.generateAndSetName(threadId, context); + } + + private async generateAndSetName(threadId: string, context: ThreadNamingContext): Promise { + try { + const title = await this.generateTitle(context); + if (!title || this.threadHasName(threadId)) return; + + const client = this.host.currentClient(); + if (!client) return; + await client.setThreadName(threadId, title); + this.host.state.listedThreads = this.host.state.listedThreads.map((thread) => + thread.id === threadId ? { ...thread, name: title } : thread, + ); + await this.host.refreshThreads(); + } catch { + // Auto-naming is best-effort metadata. Leave the thread preview untouched on failure. + } finally { + this.autoNameInFlightThreadIds.delete(threadId); + this.host.render(); + } + } + + private async resolveNamingContext(threadId: string): Promise { + const client = this.host.currentClient(); + if (!client) return null; + return findThreadNamingContext({ + threadId, + readTurns: (id, cursor, limit, sortDirection) => client.threadTurnsList(id, cursor, limit, sortDirection), + fallbackDisplayItems: this.host.state.activeThreadId === threadId ? this.host.state.displayItems : null, + }); + } + + private async generateTitle(context: ThreadNamingContext): Promise { + const settings = this.host.settings(); + return generateThreadTitleWithCodex(settings.codexPath, this.host.vaultPath, context, { + threadNamingModel: settings.threadNamingModel, + threadNamingEffort: settings.threadNamingEffort, + }); + } + + private clear(): void { + this.renameAutoNameGeneration += 1; + this.renameThreadId = null; + this.renameDraft = ""; + this.renameAutoNameThreadId = null; + } + + private threadHasName(threadId: string): boolean { + return Boolean(this.thread(threadId)?.name?.trim()); + } + + private thread(threadId: string): Thread | undefined { + return this.host.state.listedThreads.find((item) => item.id === threadId); + } +} diff --git a/src/settings-data.ts b/src/settings-data.ts new file mode 100644 index 00000000..7409dee2 --- /dev/null +++ b/src/settings-data.ts @@ -0,0 +1,94 @@ +import type { AppServerClient } from "./app-server/client"; +import type { HookMetadata } from "./generated/app-server/v2/HookMetadata"; +import type { Model } from "./generated/app-server/v2/Model"; +import type { Thread } from "./generated/app-server/v2/Thread"; +import { errorMessage } from "./utils"; + +export interface LoadedHooks { + hooks: HookMetadata[]; + warnings: string[]; + errors: string[]; + status: string; +} + +export interface SettingsDataLoad { + models: SettledSettingsData; + hooks: SettledSettingsData; + archivedThreads: SettledSettingsData; +} + +export type SettledSettingsData = + | { + ok: true; + data: T; + status: string; + } + | { + ok: false; + status: string; + }; + +export async function loadSettingsData(client: AppServerClient, cwd: string): Promise { + const [modelsResult, hooksResult, archivedThreadsResult] = await Promise.allSettled([ + client.listModels(false), + client.listHooks(cwd), + client.listThreads(cwd, true), + ] as const); + + return { + models: + modelsResult.status === "fulfilled" + ? { + ok: true, + data: modelsResult.value.data, + status: `Loaded ${modelsResult.value.data.length} model${modelsResult.value.data.length === 1 ? "" : "s"}.`, + } + : { ok: false, status: `Could not load models: ${errorMessage(modelsResult.reason)}` }, + hooks: + hooksResult.status === "fulfilled" + ? settledHooks(hooksResult.value.data, cwd) + : { ok: false, status: `Could not load hooks: ${errorMessage(hooksResult.reason)}` }, + archivedThreads: + archivedThreadsResult.status === "fulfilled" + ? { + ok: true, + data: archivedThreadsResult.value.data, + status: `Loaded ${archivedThreadsResult.value.data.length} archived thread${archivedThreadsResult.value.data.length === 1 ? "" : "s"}.`, + } + : { ok: false, status: `Could not load archived threads: ${errorMessage(archivedThreadsResult.reason)}` }, + }; +} + +export async function loadHookData(client: AppServerClient, cwd: string): Promise { + const response = await client.listHooks(cwd); + const hooks = hooksForCwd(response.data, cwd); + return { + ...hooks, + status: hooksStatus(hooks.hooks.length), + }; +} + +function hooksForCwd(entries: Awaited>["data"], cwd: string): Omit { + const entry = entries.find((item) => item.cwd === cwd) ?? entries[0]; + return { + hooks: entry?.hooks ?? [], + warnings: entry?.warnings ?? [], + errors: (entry?.errors ?? []).map((error) => JSON.stringify(error)), + }; +} + +function hooksStatus(count: number): string { + return `Loaded ${count} hook${count === 1 ? "" : "s"}.`; +} + +function settledHooks(entries: Awaited>["data"], cwd: string): SettledSettingsData { + const hooks = hooksForCwd(entries, cwd); + return { + ok: true, + data: { + ...hooks, + status: hooksStatus(hooks.hooks.length), + }, + status: hooksStatus(hooks.hooks.length), + }; +} diff --git a/src/settings-tab.ts b/src/settings-tab.ts new file mode 100644 index 00000000..f11d89f3 --- /dev/null +++ b/src/settings-tab.ts @@ -0,0 +1,406 @@ +import { type App, Notice, PluginSettingTab, Setting } from "obsidian"; + +import type { AppServerClient } from "./app-server/client"; +import { withAppServerSession } from "./app-server/session-client"; +import { DEFAULT_CODEX_PATH } from "./constants"; +import type { ReasoningEffort } from "./generated/app-server/ReasoningEffort"; +import type { HookMetadata } from "./generated/app-server/v2/HookMetadata"; +import type { Model } from "./generated/app-server/v2/Model"; +import type { Thread } from "./generated/app-server/v2/Thread"; +import type CodexPanelPlugin from "./main"; +import { findModelByIdOrName, REASONING_EFFORTS, sortedAvailableModels, supportedEffortsForModel } from "./panel/model-runtime"; +import { loadHookData, loadSettingsData } from "./settings-data"; +import { archivedThreadDisplayTitle, fullThreadTitle } from "./threads"; +import { errorMessage, shortThreadId } from "./utils"; + +const CODEX_DEFAULT_VALUE = "__codex-default__"; + +export class CodexPanelSettingTab extends PluginSettingTab { + private settingsDataAutoLoadStarted = false; + private settingsDataLoading = false; + private archivedThreads: Thread[] = []; + private archivedThreadsLoaded = false; + private archivedThreadsLoading = false; + private archivedThreadsStatus = ""; + private hooks: HookMetadata[] = []; + private hookWarnings: string[] = []; + private hookErrors: string[] = []; + private hooksLoaded = false; + private hooksLoading = false; + private hooksStatus = ""; + private namingModels: Model[] = []; + private namingModelsLoading = false; + private namingModelsStatus = ""; + + constructor( + app: App, + private readonly plugin: CodexPanelPlugin, + ) { + super(app, plugin); + } + + display(): void { + const { containerEl } = this; + containerEl.empty(); + containerEl.addClass("codex-panel-settings"); + + const configSection = containerEl.createDiv({ cls: "codex-panel-settings__section codex-panel-settings__general-section" }); + new Setting(configSection) + .setClass("codex-panel-settings__section-heading") + .setHeading() + .setName("General") + .setDesc( + "This plugin stores only panel metadata, the app-server launch command, and optional automatic thread naming runtime overrides. Sandbox, approvals, MCP, and normal chat runtime policy are resolved from Codex config for the current vault.", + ); + + new Setting(configSection) + .setName("Settings data") + .setDesc("Refresh thread naming models, hooks, and archived threads from Codex app-server.") + .addButton((button) => { + button + .setButtonText(this.settingsDataLoading ? "Refreshing..." : "Refresh settings data") + .setDisabled(this.settingsDataLoading) + .onClick(() => void this.refreshSettingsData()); + }); + + new Setting(configSection) + .setName("Codex executable") + .setDesc("Command used to launch `codex app-server`. Use an absolute path when Obsidian cannot see your shell PATH.") + .addText((text) => { + text + .setPlaceholder(DEFAULT_CODEX_PATH) + .setValue(this.plugin.settings.codexPath) + .onChange(async (value) => { + this.plugin.settings.codexPath = value.trim() || DEFAULT_CODEX_PATH; + await this.plugin.saveSettings(); + }); + }); + + new Setting(configSection) + .setName("Thread naming model") + .setDesc("Model and reasoning effort used only for automatic thread titles.") + .addDropdown((dropdown) => { + const current = this.plugin.settings.threadNamingModel; + const options = this.namingModelOptions(); + dropdown.selectEl.ariaLabel = "Thread naming model"; + dropdown.addOption(CODEX_DEFAULT_VALUE, "Codex default"); + if (current && !options.some((model) => model.model === current || model.id === current)) { + dropdown.addOption(current, `${current} (saved)`); + } + for (const model of options) { + dropdown.addOption(model.model, model.model); + } + dropdown.setValue(current ?? CODEX_DEFAULT_VALUE).onChange(async (value) => { + this.plugin.settings.threadNamingModel = value === CODEX_DEFAULT_VALUE ? null : value; + if (!this.namingEffortSupported(this.plugin.settings.threadNamingEffort)) { + this.plugin.settings.threadNamingEffort = null; + } + await this.plugin.saveSettings(); + this.display(); + }); + }) + .addDropdown((dropdown) => { + const current = this.plugin.settings.threadNamingEffort; + const options = this.namingEffortOptions(); + dropdown.selectEl.ariaLabel = "Thread naming effort"; + dropdown.addOption(CODEX_DEFAULT_VALUE, "Codex default"); + for (const effort of options) { + dropdown.addOption(effort, effort); + } + dropdown.setValue(current && options.includes(current) ? current : CODEX_DEFAULT_VALUE).onChange(async (value) => { + this.plugin.settings.threadNamingEffort = value === CODEX_DEFAULT_VALUE ? null : (value as ReasoningEffort); + await this.plugin.saveSettings(); + }); + }); + if (this.namingModelsLoading || (this.namingModelsStatus && !this.namingModelsStatus.startsWith("Loaded "))) { + configSection.createEl("p", { + cls: "setting-item-description codex-panel-settings__section-status", + text: this.namingModelsStatus || "Loading models...", + }); + } + + const hookSection = containerEl.createDiv({ cls: "codex-panel-settings__dynamic-section codex-panel-settings__hook-section" }); + new Setting(hookSection) + .setClass("codex-panel-settings__dynamic-section-heading") + .setHeading() + .setName("Hook status") + .setDesc("Review hooks discovered by Codex app-server for the current vault root, including trust and enabled state."); + + if (this.hooksLoading) { + hookSection.createEl("p", { cls: "setting-item-description codex-panel-settings__dynamic-section-status", text: "Loading hooks..." }); + } else if (this.hooksLoaded) { + this.renderHooks(hookSection); + } else if (this.hooksStatus) { + hookSection.createEl("p", { cls: "setting-item-description codex-panel-settings__dynamic-section-status", text: this.hooksStatus }); + } + + const archivedSection = containerEl.createDiv({ + cls: "codex-panel-settings__dynamic-section codex-panel-settings__archived-section", + }); + new Setting(archivedSection) + .setClass("codex-panel-settings__dynamic-section-heading") + .setHeading() + .setName("Archived thread list") + .setDesc("Restore archived Codex threads to Chat History when they are needed again."); + + if (this.archivedThreadsLoading) { + archivedSection.createEl("p", { + cls: "setting-item-description codex-panel-settings__dynamic-section-status", + text: "Loading archived threads...", + }); + } else if (this.archivedThreadsLoaded && this.archivedThreads.length === 0) { + archivedSection.createEl("p", { + cls: "setting-item-description codex-panel-settings__dynamic-section-status", + text: "No archived threads.", + }); + } else if (this.archivedThreadsLoaded) { + this.renderArchivedThreadList(archivedSection); + } else if (this.archivedThreadsStatus) { + archivedSection.createEl("p", { + cls: "setting-item-description codex-panel-settings__dynamic-section-status", + text: this.archivedThreadsStatus, + }); + } + + this.maybeAutoLoadSettingsData(); + } + + private maybeAutoLoadSettingsData(): void { + if (this.settingsDataAutoLoadStarted || this.settingsDataLoading) return; + this.settingsDataAutoLoadStarted = true; + void this.refreshSettingsData(); + } + + private async refreshSettingsData(): Promise { + this.settingsDataLoading = true; + this.namingModelsLoading = true; + this.archivedThreadsLoading = true; + this.hooksLoading = true; + this.namingModelsStatus = "Loading models..."; + this.archivedThreadsStatus = "Loading archived threads..."; + this.hooksStatus = "Loading hooks..."; + this.display(); + + let failedCount = 0; + try { + const result = await this.withSettingsSession((client) => loadSettingsData(client, this.plugin.vaultPath)); + + if (result.models.ok) { + this.namingModels = result.models.data; + this.namingModelsStatus = result.models.status; + } else { + failedCount += 1; + this.namingModelsStatus = result.models.status; + } + + if (result.hooks.ok) { + this.hooks = result.hooks.data.hooks; + this.hookWarnings = result.hooks.data.warnings; + this.hookErrors = result.hooks.data.errors; + this.hooksLoaded = true; + this.hooksStatus = result.hooks.status; + } else { + failedCount += 1; + this.hooksStatus = result.hooks.status; + } + + if (result.archivedThreads.ok) { + this.archivedThreads = result.archivedThreads.data; + this.archivedThreadsLoaded = true; + this.archivedThreadsStatus = result.archivedThreads.status; + } else { + failedCount += 1; + this.archivedThreadsStatus = result.archivedThreads.status; + } + } catch (error) { + failedCount = 3; + const message = errorMessage(error); + this.namingModelsStatus = `Could not load models: ${message}`; + this.hooksStatus = `Could not load hooks: ${message}`; + this.archivedThreadsStatus = `Could not load archived threads: ${message}`; + } finally { + this.settingsDataLoading = false; + this.namingModelsLoading = false; + this.archivedThreadsLoading = false; + this.hooksLoading = false; + if (failedCount > 0) { + new Notice("Could not refresh all Codex settings data."); + } + this.display(); + } + } + + private async loadHooks(): Promise { + this.hooksLoading = true; + this.hooksStatus = ""; + this.display(); + try { + const hooks = await this.withSettingsSession((client) => loadHookData(client, this.plugin.vaultPath)); + this.hooks = hooks.hooks; + this.hookWarnings = hooks.warnings; + this.hookErrors = hooks.errors; + this.hooksLoaded = true; + this.hooksStatus = hooks.status; + } catch (error) { + this.hooksStatus = `Could not load hooks: ${errorMessage(error)}`; + new Notice("Could not load Codex hooks."); + } finally { + this.hooksLoading = false; + this.display(); + } + } + + private async trustHook(hook: HookMetadata): Promise { + this.hooksLoading = true; + this.hooksStatus = ""; + this.display(); + try { + await this.withSettingsSession((client) => client.trustHook(hook)); + this.hooksStatus = "Trusted hook definition."; + await this.loadHooks(); + } catch (error) { + this.hooksStatus = `Could not trust hook: ${errorMessage(error)}`; + new Notice("Could not trust Codex hook."); + this.hooksLoading = false; + this.display(); + } + } + + private async setHookEnabled(hook: HookMetadata, enabled: boolean): Promise { + this.hooksLoading = true; + this.hooksStatus = ""; + this.display(); + try { + await this.withSettingsSession((client) => client.setHookEnabled(hook, enabled)); + this.hooksStatus = enabled ? "Enabled hook." : "Disabled hook."; + await this.loadHooks(); + } catch (error) { + this.hooksStatus = `Could not update hook: ${errorMessage(error)}`; + new Notice("Could not update Codex hook."); + this.hooksLoading = false; + this.display(); + } + } + + private async restoreArchivedThread(threadId: string): Promise { + this.archivedThreadsLoading = true; + this.archivedThreadsStatus = ""; + this.display(); + try { + const response = await this.withSettingsSession((client) => client.unarchiveThread(threadId)); + this.archivedThreads = this.archivedThreads.filter((thread) => thread.id !== threadId); + this.archivedThreadsLoaded = true; + this.archivedThreadsStatus = `Restored "${archivedThreadDisplayTitle(response.thread)}".`; + this.plugin.refreshOpenThreadLists(); + } catch (error) { + this.archivedThreadsStatus = `Could not restore archived thread: ${errorMessage(error)}`; + new Notice("Could not restore archived Codex thread."); + } finally { + this.archivedThreadsLoading = false; + this.display(); + } + } + + private async withSettingsSession(operation: (client: AppServerClient) => Promise): Promise { + return withAppServerSession(this.plugin.settings.codexPath, this.plugin.vaultPath, operation); + } + + private namingModelOptions(): Model[] { + return sortedAvailableModels(this.namingModels); + } + + private namingEffortOptions(): ReasoningEffort[] { + const model = this.selectedNamingModel(); + return model ? supportedEffortsForModel(model) : REASONING_EFFORTS; + } + + private namingEffortSupported(effort: ReasoningEffort | null): boolean { + return !effort || this.namingEffortOptions().includes(effort); + } + + private selectedNamingModel(): Model | null { + return findModelByIdOrName(this.namingModels, this.plugin.settings.threadNamingModel); + } + + private renderArchivedThreadList(containerEl: HTMLElement): void { + containerEl.createEl("p", { + cls: "setting-item-description codex-panel-settings__dynamic-list-summary", + text: `Loaded ${this.archivedThreads.length} archived thread${this.archivedThreads.length === 1 ? "" : "s"} from Codex app-server.`, + }); + const list = containerEl.createDiv({ cls: "setting-items codex-panel-settings__dynamic-list codex-panel-settings__archived-list" }); + for (const thread of this.archivedThreads) { + const title = archivedThreadDisplayTitle(thread); + const setting = new Setting(list) + .setClass("codex-panel-settings__dynamic-row") + .setName(title) + .setDesc(`Updated ${formatThreadDate(thread.updatedAt)} · ${shortThreadId(thread.id)}`) + .addExtraButton((button) => { + button.setIcon("rotate-ccw").onClick(() => void this.restoreArchivedThread(thread.id)); + button.extraSettingsEl.addClass("codex-panel-settings__archived-restore"); + button.extraSettingsEl.setAttr("aria-label", `Restore ${title}`); + }); + setting.settingEl.addClass("codex-panel-settings__archived-row"); + setting.settingEl.setAttr("title", fullThreadTitle(thread)); + } + } + + private renderHooks(containerEl: HTMLElement): void { + if (this.hooks.length === 0) { + containerEl.createEl("p", { cls: "setting-item-description", text: "No hooks discovered for the current vault root." }); + } else { + containerEl.createEl("p", { + cls: "setting-item-description codex-panel-settings__dynamic-list-summary", + text: `Loaded ${this.hooks.length} hook${this.hooks.length === 1 ? "" : "s"} from Codex app-server.`, + }); + const list = containerEl.createDiv({ cls: "setting-items codex-panel-settings__dynamic-list codex-panel-settings__hook-list" }); + for (const hook of this.hooks) { + this.renderHookRow(list, hook); + } + } + + for (const warning of this.hookWarnings) { + containerEl.createEl("p", { cls: "setting-item-description codex-panel-settings__hook-warning", text: warning }); + } + for (const error of this.hookErrors) { + containerEl.createEl("p", { cls: "setting-item-description codex-panel-settings__hook-error", text: error }); + } + } + + private renderHookRow(list: HTMLElement, hook: HookMetadata): void { + const canTrust = !hook.isManaged && (hook.trustStatus === "untrusted" || hook.trustStatus === "modified"); + const setting = new Setting(list) + .setClass("codex-panel-settings__dynamic-row") + .setName(hook.statusMessage || hook.command || hook.matcher || hook.eventName) + .setDesc(`${hook.eventName} · ${hook.matcher ?? "(no matcher)"} · ${hook.trustStatus} · ${hook.enabled ? "enabled" : "disabled"}`) + .addButton((button) => { + button + .setButtonText("Trust") + .setDisabled(this.hooksLoading || !canTrust) + .onClick(() => void this.trustHook(hook)); + }) + .addButton((button) => { + button + .setButtonText(hook.enabled ? "Disable" : "Enable") + .setDisabled(this.hooksLoading || hook.isManaged) + .onClick(() => void this.setHookEnabled(hook, !hook.enabled)); + }); + setting.settingEl.addClass("codex-panel-settings__hook-row"); + setting.settingEl.setAttr("title", hook.command ?? hook.key); + setting.descEl.createDiv({ + cls: "codex-panel-settings__hook-hash", + text: hook.currentHash, + attr: { title: hook.key }, + }); + } +} + +function formatThreadDate(timestamp: number): string { + if (!Number.isFinite(timestamp) || timestamp <= 0) return "unknown"; + return new Date(timestamp * 1000).toLocaleString(undefined, { + year: "numeric", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }); +} diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 00000000..53a4b5d4 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,63 @@ +import { FileSystemAdapter, type App } from "obsidian"; + +import { DEFAULT_CODEX_PATH } from "./constants"; +import type { ReasoningEffort } from "./generated/app-server/ReasoningEffort"; +import { normalizeReasoningEffort } from "./panel/model-runtime"; + +export interface CodexPanelSettings { + codexPath: string; + threadNamingModel: string | null; + threadNamingEffort: ReasoningEffort | null; +} + +export const DEFAULT_SETTINGS: CodexPanelSettings = { + codexPath: DEFAULT_CODEX_PATH, + threadNamingModel: null, + threadNamingEffort: null, +}; + +export function normalizeSettings(data: unknown): CodexPanelSettings { + const record = asRecord(data); + return { + codexPath: stringOrDefault(record.codexPath, DEFAULT_CODEX_PATH).trim() || DEFAULT_CODEX_PATH, + threadNamingModel: threadNamingModelOrDefault(record.threadNamingModel), + threadNamingEffort: reasoningEffortOrDefault(record.threadNamingEffort), + }; +} + +export function settingsMatchNormalizedData(data: unknown, settings: CodexPanelSettings): boolean { + const record = asRecord(data); + return ( + Object.keys(record).length === 3 && + record.codexPath === settings.codexPath && + record.threadNamingModel === settings.threadNamingModel && + record.threadNamingEffort === settings.threadNamingEffort + ); +} + +function asRecord(value: unknown): Record { + return value && typeof value === "object" ? (value as Record) : {}; +} + +function stringOrDefault(value: unknown, fallback: string): string { + return typeof value === "string" ? value : fallback; +} + +function threadNamingModelOrDefault(value: unknown): string | null { + if (typeof value !== "string") return DEFAULT_SETTINGS.threadNamingModel; + const trimmed = value.trim(); + return trimmed.length > 0 ? trimmed : null; +} + +function reasoningEffortOrDefault(value: unknown): ReasoningEffort | null { + return normalizeReasoningEffort(value) ?? DEFAULT_SETTINGS.threadNamingEffort; +} + +export function getVaultPath(app: App): string { + const adapter = app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + const basePath = adapter.getBasePath(); + if (basePath.length > 0) return basePath; + } + throw new Error("This plugin requires a desktop vault with a local basePath."); +} diff --git a/src/state/panel-state.ts b/src/state/panel-state.ts new file mode 100644 index 00000000..62c17021 --- /dev/null +++ b/src/state/panel-state.ts @@ -0,0 +1,129 @@ +import type { InitializeResponse } from "../generated/app-server/InitializeResponse"; +import type { ModeKind } from "../generated/app-server/ModeKind"; +import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; +import type { ConfigReadResponse } from "../generated/app-server/v2/ConfigReadResponse"; +import type { Model } from "../generated/app-server/v2/Model"; +import type { RateLimitSnapshot } from "../generated/app-server/v2/RateLimitSnapshot"; +import type { SkillMetadata } from "../generated/app-server/v2/SkillMetadata"; +import type { Thread } from "../generated/app-server/v2/Thread"; +import type { ThreadTokenUsage } from "../generated/app-server/v2/ThreadTokenUsage"; +import type { AppServerCompatibility } from "../app-server/compatibility"; +import { createAppServerCompatibility } from "../app-server/compatibility"; +import type { PendingApproval } from "../approvals/model"; +import type { ComposerSuggestion } from "../composer/suggestions"; +import type { DisplayItem } from "../display/types"; +import type { PendingUserInput } from "../user-input/model"; +import type { ServiceTier } from "../app-server/service-tier"; +import { defaultRuntimeOverride, type RuntimeOverride } from "../panel/runtime-state"; + +export interface PanelState { + status: string; + effectiveConfig: ConfigReadResponse | null; + initializeResponse: InitializeResponse | null; + activeThreadId: string | null; + activeThreadCwd: string | null; + activeTurnId: string | null; + activeModel: string | null; + activeServiceTier: string | null; + activeThreadCliVersion: string | null; + appServerCompatibility: AppServerCompatibility; + requestedModel: RuntimeOverride; + requestedReasoningEffort: RuntimeOverride; + requestedCollaborationMode: ModeKind; + requestedServiceTier: ServiceTier | null; + tokenUsage: ThreadTokenUsage | null; + rateLimit: RateLimitSnapshot | null; + busy: boolean; + displayItems: DisplayItem[]; + approvals: PendingApproval[]; + pendingUserInputs: PendingUserInput[]; + userInputDrafts: Map; + listedThreads: Thread[]; + threadsLoaded: boolean; + historyCursor: string | null; + loadingHistory: boolean; + composerDraft: string; + runtimePicker: "model" | "effort" | null; + availableModels: Model[]; + availableSkills: SkillMetadata[]; + reportedMcpFailures: Set; + reportedLogs: Set; + composerSuggestSelected: number; + composerSuggestions: ComposerSuggestion[]; + messagesPinnedToBottom: boolean; + openDetails: Set; +} + +export function createPanelState(): PanelState { + return { + status: "Idle", + effectiveConfig: null, + initializeResponse: null, + activeThreadId: null, + activeThreadCwd: null, + activeTurnId: null, + activeModel: null, + activeServiceTier: null, + activeThreadCliVersion: null, + appServerCompatibility: createAppServerCompatibility(), + requestedModel: defaultRuntimeOverride(), + requestedReasoningEffort: defaultRuntimeOverride(), + requestedCollaborationMode: "default", + requestedServiceTier: null, + tokenUsage: null, + rateLimit: null, + busy: false, + displayItems: [], + approvals: [], + pendingUserInputs: [], + userInputDrafts: new Map(), + listedThreads: [], + threadsLoaded: false, + historyCursor: null, + loadingHistory: false, + composerDraft: "", + runtimePicker: null, + availableModels: [], + availableSkills: [], + reportedMcpFailures: new Set(), + reportedLogs: new Set(), + composerSuggestSelected: 0, + composerSuggestions: [], + messagesPinnedToBottom: true, + openDetails: new Set(), + }; +} + +export function clearActiveTurnState(state: PanelState): void { + state.activeTurnId = null; + state.busy = false; + state.approvals = []; + state.pendingUserInputs = []; + state.userInputDrafts.clear(); +} + +export function clearActiveThreadState(state: PanelState): void { + state.activeThreadId = null; + state.activeThreadCwd = null; + state.activeModel = null; + state.activeServiceTier = null; + state.activeThreadCliVersion = null; + state.tokenUsage = null; + state.historyCursor = null; + state.loadingHistory = false; + state.displayItems = []; + state.messagesPinnedToBottom = true; + clearActiveTurnState(state); +} + +export function clearConnectionScopedState(state: PanelState): void { + clearActiveTurnState(state); + state.activeModel = null; + state.activeServiceTier = null; + state.activeThreadCliVersion = null; + state.rateLimit = null; + state.listedThreads = []; + state.threadsLoaded = false; + state.availableModels = []; + state.runtimePicker = null; +} diff --git a/src/threads.ts b/src/threads.ts new file mode 100644 index 00000000..a7ed1129 --- /dev/null +++ b/src/threads.ts @@ -0,0 +1,27 @@ +import type { Thread } from "./generated/app-server/v2/Thread"; + +const MAX_THREAD_DISPLAY_TITLE_LENGTH = 96; +const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +export function getThreadTitle(thread: Thread): string { + return thread.name || thread.preview || thread.id; +} + +export function archivedThreadDisplayTitle(thread: Thread): string { + const title = fullThreadTitle(thread); + if (!title || title === thread.id || UUID_PATTERN.test(title)) return "Untitled archived thread"; + return truncateTitle(title, MAX_THREAD_DISPLAY_TITLE_LENGTH); +} + +export function fullThreadTitle(thread: Thread): string { + return normalizeTitle(getThreadTitle(thread)); +} + +function normalizeTitle(value: string): string { + return value.replace(/\s+/g, " ").trim(); +} + +function truncateTitle(value: string, maxLength: number): string { + if (value.length <= maxLength) return value; + return `${value.slice(0, maxLength - 3).trimEnd()}...`; +} diff --git a/src/user-input/model.ts b/src/user-input/model.ts new file mode 100644 index 00000000..588ff023 --- /dev/null +++ b/src/user-input/model.ts @@ -0,0 +1,39 @@ +import type { RequestId } from "../generated/app-server/RequestId"; +import type { ServerRequest } from "../generated/app-server/ServerRequest"; +import type { ToolRequestUserInputParams } from "../generated/app-server/v2/ToolRequestUserInputParams"; +import type { ToolRequestUserInputQuestion } from "../generated/app-server/v2/ToolRequestUserInputQuestion"; +import type { ToolRequestUserInputResponse } from "../generated/app-server/v2/ToolRequestUserInputResponse"; + +export type UserInputRequest = Extract; + +export interface PendingUserInput { + requestId: RequestId; + method: UserInputRequest["method"]; + params: ToolRequestUserInputParams; +} + +export function toPendingUserInput(request: ServerRequest): PendingUserInput | null { + if (request.method !== "item/tool/requestUserInput") return null; + return { + requestId: request.id, + method: request.method, + params: request.params, + }; +} + +export function userInputResponse(input: PendingUserInput, answers: Record): ToolRequestUserInputResponse { + return { + answers: Object.fromEntries( + input.params.questions.map((question) => [ + question.id, + { + answers: [answers[question.id] ?? ""], + }, + ]), + ), + }; +} + +export function questionDefaultAnswer(question: ToolRequestUserInputQuestion): string { + return question.options?.[0]?.label ?? ""; +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 00000000..b6657cac --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,36 @@ +import type { UserInput } from "./generated/app-server/v2/UserInput"; + +export function truncate(value: string, maxLength: number): string { + return value.length > maxLength ? `${value.slice(0, maxLength - 1)}...` : value; +} + +export function jsonPreview(value: unknown): string { + try { + return JSON.stringify(value, null, 2); + } catch { + return String(value); + } +} + +export function inputToText(content: UserInput[]): string { + const hasText = content.some((item) => item.type === "text" && item.text.length > 0); + return content + .map((item) => { + if (item.type === "text") return item.text; + if (item.type === "localImage") return `[local image] ${item.path}`; + if (item.type === "image") return `[image] ${item.url}`; + if (item.type === "mention") return hasText ? "" : `[@${item.name}] ${item.path}`; + if (item.type === "skill") return `[$${item.name}] ${item.path}`; + return ""; + }) + .filter(Boolean) + .join("\n"); +} + +export function errorMessage(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} + +export function shortThreadId(threadId: string): string { + return threadId.length <= 8 ? threadId : threadId.slice(0, 8); +} diff --git a/src/view/components.ts b/src/view/components.ts new file mode 100644 index 00000000..f98a5346 --- /dev/null +++ b/src/view/components.ts @@ -0,0 +1,56 @@ +import { setIcon } from "obsidian"; + +export function createIconButton(parent: HTMLElement, icon: string, label: string, className: string): HTMLButtonElement { + const button = parent.createEl("button", { + cls: `clickable-icon ${className}`, + attr: { + type: "button", + "aria-label": label, + }, + }); + setIcon(button, icon); + return button; +} + +export function createToolbarButton(parent: HTMLElement, icon: string, label: string): HTMLButtonElement { + return createIconButton(parent, icon, label, "nav-action-button codex-panel__top-control codex-panel__icon-button"); +} + +export function createRememberedDetails( + parent: HTMLElement, + openDetails: Set, + key: string, + cls: string, + summary: string, + defaultOpen = false, + onToggle?: () => void, +): HTMLDetailsElement { + const details = parent.createEl("details", { cls }); + details.open = openDetails.has(key) || defaultOpen; + details.createEl("summary", { text: summary }); + details.ontoggle = () => { + if (details.open) { + openDetails.add(key); + } else { + openDetails.delete(key); + } + onToggle?.(); + }; + return details; +} + +export function createMetaPair(list: HTMLElement, key: string, value: string): void { + list.createEl("dt", { text: key }); + list.createEl("dd", { text: value }); +} + +export function createDefinitionRow(list: HTMLElement, className: string, key: string, value: string): void { + const row = list.createDiv({ cls: className }); + row.createEl("dt", { text: key }); + row.createEl("dd", { text: value }); +} + +export function setButtonIcon(button: HTMLButtonElement, icon: string): void { + button.empty(); + setIcon(button, icon); +} diff --git a/src/view/composer.ts b/src/view/composer.ts new file mode 100644 index 00000000..46d2df3a --- /dev/null +++ b/src/view/composer.ts @@ -0,0 +1,202 @@ +import type { ComposerSuggestion } from "../composer/suggestions"; +import { createIconButton, setButtonIcon } from "./components"; + +export interface ComposerElements { + composer: HTMLTextAreaElement; + suggestions: HTMLElement; +} + +export interface ComposerCallbacks { + onInput: () => void; + onUpdateSuggestions: () => void; + onKeydown: (event: KeyboardEvent) => void; + onNewThread: () => void; + onSendOrInterrupt: () => void; + onSuggestionHover: (index: number) => void; + onSuggestionInsert: (suggestion: ComposerSuggestion) => void; +} + +export function renderComposerShell( + parent: HTMLElement, + viewId: string, + draft: string, + busy: boolean, + callbacks: ComposerCallbacks, +): ComposerElements { + parent.empty(); + const composerEl = parent.createDiv({ cls: "codex-panel__composer" }); + const composer = composerEl.createEl("textarea", { + cls: "codex-panel__input", + attr: { + placeholder: "Ask Codex to work...", + role: "combobox", + "aria-autocomplete": "list", + "aria-expanded": "false", + "aria-controls": `${viewId}-composer-suggestions`, + }, + }); + composer.value = draft; + syncComposerHeight(composer); + composer.oninput = () => { + syncComposerHeight(composer); + callbacks.onInput(); + }; + composer.onkeyup = callbacks.onUpdateSuggestions; + composer.onclick = callbacks.onUpdateSuggestions; + composer.onselect = callbacks.onUpdateSuggestions; + composer.onkeydown = callbacks.onKeydown; + + const actionsEl = composerEl.createDiv({ cls: "codex-panel__composer-actions" }); + const newThreadButton = createIconButton( + actionsEl, + "message-square-plus", + "New chat", + "codex-panel__composer-action codex-panel__new-chat", + ); + newThreadButton.disabled = busy; + newThreadButton.onclick = callbacks.onNewThread; + + const sendButton = createIconButton(actionsEl, "send", "Send", "codex-panel__composer-action codex-panel__send"); + sendButton.onclick = callbacks.onSendOrInterrupt; + + const suggestions = composerEl.createDiv({ + cls: "suggestion-container codex-panel__composer-suggestions", + attr: { + id: `${viewId}-composer-suggestions`, + role: "listbox", + }, + }); + + return { composer, suggestions }; +} + +export function syncComposerControls( + parent: HTMLElement | null, + composer: HTMLTextAreaElement | null, + busy: boolean, + canInterrupt: boolean, +): void { + const newThreadButton = parent?.querySelector(".codex-panel__new-chat"); + if (newThreadButton) newThreadButton.disabled = busy; + const sendButton = parent?.querySelector(".codex-panel__send"); + if (sendButton) { + const hasDraft = Boolean(composer?.value.trim()); + const canSteer = canInterrupt && hasDraft; + const interruptMode = canInterrupt && !hasDraft; + const label = interruptMode ? "Interrupt" : canSteer ? "Steer" : "Send"; + sendButton.disabled = busy && !canInterrupt; + sendButton.setAttr("aria-label", label); + sendButton.classList.toggle("is-interrupt", interruptMode); + sendButton.classList.toggle("is-steer", canSteer); + const mode = interruptMode ? "interrupt" : canSteer ? "steer" : "send"; + if (sendButton.dataset.codexMode !== mode) { + sendButton.dataset.codexMode = mode; + setButtonIcon(sendButton, interruptMode ? "square" : canSteer ? "corner-down-right" : "send"); + } + } + if (composer) { + composer.setAttr("placeholder", canInterrupt ? "Add steering message..." : "Ask Codex to work..."); + syncComposerHeight(composer); + } +} + +export function syncComposerHeight(composer: HTMLTextAreaElement | null): void { + if (!composer) return; + const style = getComputedStyle(composer); + const minHeight = parseCssPixels(style.minHeight, 76); + const maxHeight = composerMaxHeight(style.maxHeight); + composer.style.height = "auto"; + const nextHeight = Math.min(Math.max(composer.scrollHeight, minHeight), maxHeight); + composer.style.height = `${nextHeight}px`; + composer.style.overflowY = composer.scrollHeight > maxHeight ? "auto" : "hidden"; +} + +function parseCssPixels(value: string, fallback: number): number { + const parsed = Number.parseFloat(value); + return Number.isFinite(parsed) ? parsed : fallback; +} + +function composerMaxHeight(value: string): number { + return parseCssLengthExpression(value) ?? Math.min(208, window.innerHeight * 0.4); +} + +function parseCssLengthExpression(value: string): number | null { + const trimmed = value.trim(); + if (!trimmed || trimmed === "none") return null; + if (/^min\(/i.test(trimmed)) { + const values = Array.from(trimmed.matchAll(/(-?\d+(?:\.\d+)?)\s*(px|vh)/gi), (match) => + cssLengthToPixels(Number.parseFloat(match[1]), match[2]), + ).filter((candidate): candidate is number => Number.isFinite(candidate)); + return values.length > 0 ? Math.min(...values) : null; + } + const length = trimmed.match(/^(-?\d+(?:\.\d+)?)\s*(px|vh)$/i); + if (!length) return null; + return cssLengthToPixels(Number.parseFloat(length[1]), length[2]); +} + +function cssLengthToPixels(value: number, unit: string): number { + if (unit.toLowerCase() === "vh") return (window.innerHeight * value) / 100; + return value; +} + +export function renderComposerSuggestions( + suggestionsEl: HTMLElement | null, + composer: HTMLTextAreaElement | null, + viewId: string, + suggestions: ComposerSuggestion[], + selectedIndex: number, + callbacks: Pick, +): void { + if (!suggestionsEl) return; + suggestionsEl.empty(); + + if (suggestions.length === 0) { + composer?.setAttr("aria-expanded", "false"); + composer?.removeAttribute("aria-activedescendant"); + suggestionsEl.hide(); + return; + } + + composer?.setAttr("aria-expanded", "true"); + suggestionsEl.show(); + let selectedOption: HTMLElement | null = null; + for (const [index, suggestion] of suggestions.entries()) { + const optionId = `${viewId}-composer-suggestion-${index}`; + const option = suggestionsEl.createDiv({ + cls: `suggestion-item codex-panel__composer-suggestion ${index === selectedIndex ? "is-selected" : ""}`, + attr: { + id: optionId, + role: "option", + "aria-selected": index === selectedIndex ? "true" : "false", + tabindex: "-1", + }, + }); + if (index === selectedIndex) { + composer?.setAttr("aria-activedescendant", optionId); + selectedOption = option; + } + option.createSpan({ cls: "suggestion-title codex-panel__suggestion-label", text: suggestion.display }); + if (suggestion.detail) { + option.createSpan({ cls: "suggestion-note codex-panel__suggestion-detail", text: suggestion.detail }); + } + option.onmousemove = () => callbacks.onSuggestionHover(index); + option.onmousedown = (event) => { + event.preventDefault(); + callbacks.onSuggestionInsert(suggestion); + }; + } + if (selectedOption) scrollComposerSuggestionIntoView(suggestionsEl, selectedOption); +} + +export function scrollComposerSuggestionIntoView(container: HTMLElement, option: HTMLElement): void { + const optionTop = option.offsetTop; + const optionBottom = optionTop + option.offsetHeight; + const viewportTop = container.scrollTop; + const viewportBottom = viewportTop + container.clientHeight; + + if (optionTop < viewportTop) { + container.scrollTop = Math.max(0, optionTop); + } else if (optionBottom > viewportBottom) { + container.scrollTop = Math.max(0, optionBottom - container.clientHeight); + } +} diff --git a/src/view/config.ts b/src/view/config.ts new file mode 100644 index 00000000..c810a07a --- /dev/null +++ b/src/view/config.ts @@ -0,0 +1,14 @@ +import type { EffectiveConfigSection } from "../panel/runtime-view"; +import { createDefinitionRow } from "./components"; + +export function renderEffectiveConfig(parent: HTMLElement, sections: EffectiveConfigSection[]): void { + const panel = parent.createDiv({ cls: "codex-panel__config" }); + panel.createDiv({ cls: "codex-panel__config-title", text: "Effective Codex config" }); + const list = panel.createEl("dl", { cls: "codex-panel__config-list" }); + for (const section of sections) { + list.createDiv({ cls: "codex-panel__config-section", text: section.title }); + for (const row of section.rows) { + createDefinitionRow(list, "codex-panel__config-row", row.key, row.value); + } + } +} diff --git a/src/view/dom.ts b/src/view/dom.ts new file mode 100644 index 00000000..539960a0 --- /dev/null +++ b/src/view/dom.ts @@ -0,0 +1,45 @@ +export function shortSignature(value: string): string { + let hash = 0; + for (let index = 0; index < value.length; index += 1) { + hash = (hash * 31 + value.charCodeAt(index)) | 0; + } + return Math.abs(hash).toString(36); +} + +export function renderTextWithWikiLinks(parent: HTMLElement, text: string, openLink: (target: string) => void): void { + const wikilinkPattern = /\[\[([^\]\n]+?)\]\]/g; + let lastIndex = 0; + for (const match of text.matchAll(wikilinkPattern)) { + const index = match.index ?? 0; + if (index > lastIndex) { + parent.appendChild(document.createTextNode(text.slice(lastIndex, index))); + } + + const rawLink = match[1] ?? ""; + const separator = rawLink.indexOf("|"); + const target = (separator === -1 ? rawLink : rawLink.slice(0, separator)).trim(); + const label = (separator === -1 ? rawLink : rawLink.slice(separator + 1)).trim() || target; + + if (target.length === 0) { + parent.appendChild(document.createTextNode(match[0])); + } else { + const link = parent.createEl("a", { + cls: "internal-link codex-panel__wikilink", + text: label, + attr: { + href: target, + title: target, + }, + }); + link.onclick = (event) => { + event.preventDefault(); + openLink(target); + }; + } + lastIndex = index + match[0].length; + } + + if (lastIndex < text.length) { + parent.appendChild(document.createTextNode(text.slice(lastIndex))); + } +} diff --git a/src/view/execution-state.ts b/src/view/execution-state.ts new file mode 100644 index 00000000..e01a87c0 --- /dev/null +++ b/src/view/execution-state.ts @@ -0,0 +1,7 @@ +import type { ExecutionState } from "../display/types"; + +export function applyExecutionStateClass(parent: HTMLElement, state: ExecutionState): void { + if (!state) return; + parent.addClass("codex-panel__execution"); + parent.addClass(`codex-panel__execution--${state}`); +} diff --git a/src/view/message-stream.ts b/src/view/message-stream.ts new file mode 100644 index 00000000..96abf4dc --- /dev/null +++ b/src/view/message-stream.ts @@ -0,0 +1,228 @@ +import { displayBlocksForItems, executionState } from "../display/model"; +import { displayItemSignature } from "../display/signature"; +import type { DisplayBlock, DisplayDetailSection, DisplayItem } from "../display/types"; +import { createMetaPair, createRememberedDetails } from "./components"; +import { applyExecutionStateClass } from "./execution-state"; +import { renderToolResult } from "./tool-result"; +import { + activeAgentRunSummaryBlock, + createAgentRunSummaryElement, + renderAgentItem, + renderReasoningItem, + renderTaskProgressItem, +} from "./work-items"; + +export interface MessageRenderBlock { + key: string; + signature: string; + render: () => HTMLElement; +} + +export interface MessageStreamContext { + activeThreadId: string | null; + activeTurnId: string | null; + historyCursor: string | null; + loadingHistory: boolean; + busy: boolean; + displayItems: DisplayItem[]; + workspaceRoot?: string | null; + openDetails: Set; + onDetailsToggle?: () => void; + loadOlderTurns: () => void; + renderMarkdown: (parent: HTMLElement, text: string) => void; + renderTextWithWikiLinks: (parent: HTMLElement, text: string) => void; + pendingRequestsSignature?: string; + renderPendingRequests?: () => HTMLElement | null; +} + +export function messageRenderBlocks(context: MessageStreamContext): MessageRenderBlock[] { + const blocks: MessageRenderBlock[] = []; + + if (context.activeThreadId && context.historyCursor) { + blocks.push({ + key: "history-bar", + signature: `${context.activeThreadId}:${context.historyCursor}:${context.loadingHistory}`, + render: () => createHistoryBarElement(context.loadingHistory, context.loadOlderTurns), + }); + } + + if (context.displayItems.length === 0) { + blocks.push({ + key: "empty", + signature: "empty", + render: () => + createDiv({ + cls: "codex-panel__message codex-panel__message--system", + text: "Start a new thread or send a message.", + }), + }); + return blocks; + } + + for (const block of displayBlocksForItems(context.displayItems, context.activeTurnId, context.workspaceRoot)) { + if (block.type === "item") { + blocks.push({ + key: `item:${block.item.id}`, + signature: displayItemSignature(block.item, context), + render: () => createDisplayItemElement(block.item, context), + }); + } else { + blocks.push({ + key: `activity:${block.id}`, + signature: `${block.summary}\n${block.items.map((item) => displayItemSignature(item, context)).join("\n")}`, + render: () => createActivityGroupElement(block, context), + }); + } + } + + const agentSummary = activeAgentRunSummaryBlock(context); + if (agentSummary) { + blocks.push({ + key: `active-agents:${context.activeTurnId}`, + signature: JSON.stringify(agentSummary), + render: () => createAgentRunSummaryElement(agentSummary), + }); + } + + if (context.renderPendingRequests && context.pendingRequestsSignature) { + blocks.push({ + key: "pending-requests", + signature: context.pendingRequestsSignature, + render: () => context.renderPendingRequests?.() ?? createDiv(), + }); + } + + return blocks; +} + +function createHistoryBarElement(loadingHistory: boolean, loadOlderTurns: () => void): HTMLElement { + const historyBar = createDiv({ cls: "codex-panel__history-bar" }); + const loadOlder = historyBar.createEl("button", { + text: loadingHistory ? "Loading..." : "Load older", + }); + loadOlder.disabled = loadingHistory; + loadOlder.onclick = loadOlderTurns; + return historyBar; +} + +function createDisplayItemElement(item: DisplayItem, context: MessageStreamContext): HTMLElement { + const container = createDiv(); + renderDisplayItem(container, item, context); + return container.firstElementChild as HTMLElement; +} + +function createActivityGroupElement(group: Extract, context: MessageStreamContext): HTMLElement { + const container = createDiv(); + const details = createRememberedDetails( + container, + context.openDetails, + `turn:${group.turnId}:activity`, + "codex-panel__activity-group", + group.summary, + false, + context.onDetailsToggle, + ); + for (const item of group.items) { + renderDisplayItem(details, item, context); + } + return container.firstElementChild as HTMLElement; +} + +function renderDisplayItem(parent: HTMLElement, item: DisplayItem, context: MessageStreamContext): void { + if (item.kind === "command") { + renderToolResult(parent, item, context); + return; + } + if (item.kind === "fileChange") { + renderToolResult(parent, item, context); + return; + } + if (item.kind === "taskProgress") { + renderTaskProgressItem(parent, item); + return; + } + if (item.kind === "agent") { + renderAgentItem(parent, item, context); + return; + } + if (item.kind === "reasoning") { + renderReasoningItem(parent, item, context); + return; + } + if (item.kind === "tool" || item.kind === "hook") { + renderToolResult(parent, item, context); + return; + } + if (item.kind === "reviewResult") { + renderToolResult(parent, item, context); + return; + } + if (item.kind !== "message" && item.kind !== "system" && item.kind !== "approvalResult" && item.kind !== "userInputResult") { + return; + } + + const messageEl = parent.createDiv({ cls: messageClass(item) }); + applyExecutionStateClass(messageEl, executionState(item)); + messageEl.createDiv({ cls: "codex-panel__message-role", text: displayRoleLabel(item) }); + const content = messageEl.createDiv({ cls: `codex-panel__message-content ${item.markdown === false ? "" : "markdown-rendered"}` }); + if (item.markdown === false) { + context.renderTextWithWikiLinks(content, item.text); + } else { + context.renderMarkdown(content, item.text); + } + if (item.kind === "message" && item.editedFiles && item.editedFiles.length > 0) { + renderEditedFiles(messageEl, item.editedFiles); + } + if ("details" in item && item.details && item.details.length > 0) { + renderMessageDetails(messageEl, item.id, item.details, context); + } +} + +function renderEditedFiles(parent: HTMLElement, editedFiles: string[]): void { + const label = editedFiles.length === 1 ? "Edited 1 file" : `Edited ${editedFiles.length} files`; + const details = parent.createEl("details", { cls: "codex-panel__edited-files" }); + details.createEl("summary", { text: label }); + const list = details.createEl("ul"); + for (const file of editedFiles) { + list.createEl("li", { text: file }); + } +} + +function displayRoleLabel(item: DisplayItem): string { + if (item.kind === "approvalResult") return "Approval"; + if (item.kind === "userInputResult") return "Input"; + if (item.kind === "reviewResult") return "Review"; + if (item.role === "user") return "You"; + if (item.role === "assistant") return "Codex"; + return "System"; +} + +function messageClass(item: DisplayItem): string { + const classes = ["codex-panel__message", `codex-panel__message--${item.role}`]; + if (item.kind === "approvalResult") classes.push("codex-panel__message--approval-result"); + if (item.kind === "userInputResult") classes.push("codex-panel__message--user-input-result"); + if (item.kind === "reviewResult") classes.push("codex-panel__message--review-result"); + return classes.join(" "); +} + +function renderMessageDetails(parent: HTMLElement, itemId: string, details: DisplayDetailSection[], context: MessageStreamContext): void { + for (const [index, section] of details.entries()) { + const summary = section.title ?? "Details"; + const detailsEl = createRememberedDetails( + parent, + context.openDetails, + `${itemId}:message-detail:${index}`, + "codex-panel__output", + summary, + false, + context.onDetailsToggle, + ); + if (section.rows && section.rows.length > 0) { + const rows = detailsEl.createEl("dl", { cls: "codex-panel__meta-grid" }); + for (const row of section.rows) { + createMetaPair(rows, row.key, row.value); + } + } + if (section.body) detailsEl.createEl("pre", { text: section.body }); + } +} diff --git a/src/view/pending-request-message.ts b/src/view/pending-request-message.ts new file mode 100644 index 00000000..2efebee8 --- /dev/null +++ b/src/view/pending-request-message.ts @@ -0,0 +1,185 @@ +import { approvalDetails, approvalSummary, approvalTitle, type ApprovalAction, type PendingApproval } from "../approvals/model"; +import type { RequestId } from "../generated/app-server/RequestId"; +import type { PendingUserInput } from "../user-input/model"; +import { questionDefaultAnswer } from "../user-input/model"; +import { createMetaPair, createRememberedDetails } from "./components"; +import { createWorkMessage } from "./work-message"; + +export interface PendingRequestMessageActions { + resolveApproval: (approval: PendingApproval, action: ApprovalAction) => void; + resolveUserInput: (input: PendingUserInput) => void; + cancelUserInput: (input: PendingUserInput) => void; +} + +export interface PendingRequestMessageDrafts { + values: Map; + draftKey: (requestId: RequestId, questionId: string) => string; + otherDraftKey: (requestId: RequestId, questionId: string) => string; +} + +export function renderPendingRequestMessage( + parent: HTMLElement, + approvals: PendingApproval[], + pendingUserInputs: PendingUserInput[], + drafts: PendingRequestMessageDrafts, + openDetails: Set, + actions: PendingRequestMessageActions, +): void { + parent.empty(); + if (approvals.length === 0 && pendingUserInputs.length === 0) return; + + const message = createWorkMessage(parent, { + label: "Request", + className: "codex-panel__pending-request-message", + tone: "warning", + }); + for (const approval of approvals) { + renderApprovalCard(message, approval, openDetails, actions); + } + for (const input of pendingUserInputs) { + renderUserInputCard(message, input, drafts, actions); + } +} + +function renderApprovalCard( + parent: HTMLElement, + approval: PendingApproval, + openDetails: Set, + actions: PendingRequestMessageActions, +): void { + const card = parent.createDiv({ cls: "setting-item codex-panel__pending-request-card codex-panel__approval" }); + const info = card.createDiv({ cls: "setting-item-info codex-panel__pending-request-info" }); + info.createDiv({ cls: "setting-item-name codex-panel__pending-request-title", text: approvalTitle(approval) }); + info.createDiv({ cls: "setting-item-description codex-panel__approval-body", text: approvalSummary(approval) }); + renderApprovalDetails(info, approval, openDetails); + const controls = card.createDiv({ cls: "setting-item-control codex-panel__pending-request-actions" }); + + createActionButton(controls, "Allow", "mod-cta", () => actions.resolveApproval(approval, "accept")); + createActionButton(controls, "Allow session", "", () => actions.resolveApproval(approval, "accept-session")); + createActionButton(controls, "Deny", "mod-warning", () => actions.resolveApproval(approval, "decline")); + createActionButton(controls, "Cancel", "", () => actions.resolveApproval(approval, "cancel")); +} + +function renderApprovalDetails(parent: HTMLElement, approval: PendingApproval, openDetails: Set): void { + const details = createRememberedDetails( + parent, + openDetails, + `approval:${String(approval.requestId)}:details`, + "codex-panel__approval-details", + "Details", + ); + const rows = details.createEl("dl", { cls: "codex-panel__meta-grid" }); + for (const row of approvalDetails(approval)) { + createMetaPair(rows, row.key, row.value); + } +} + +function renderUserInputCard( + parent: HTMLElement, + input: PendingUserInput, + drafts: PendingRequestMessageDrafts, + actions: PendingRequestMessageActions, +): void { + const card = parent.createDiv({ cls: "setting-item codex-panel__pending-request-card codex-panel__user-input" }); + const info = card.createDiv({ cls: "setting-item-info codex-panel__pending-request-info" }); + info.createDiv({ cls: "setting-item-name codex-panel__pending-request-title", text: "Codex needs input" }); + info.createDiv({ + cls: "setting-item-description codex-panel__approval-body", + text: `${input.params.questions.length} question${input.params.questions.length === 1 ? "" : "s"} from Plan mode.`, + }); + renderUserInputQuestions(info, input, drafts); + + const controls = card.createDiv({ cls: "setting-item-control codex-panel__pending-request-actions" }); + createActionButton(controls, "Submit", "mod-cta", () => actions.resolveUserInput(input)); + createActionButton(controls, "Cancel", "", () => actions.cancelUserInput(input)); +} + +function renderUserInputQuestions(parent: HTMLElement, input: PendingUserInput, drafts: PendingRequestMessageDrafts): void { + for (const question of input.params.questions) { + const questionEl = parent.createDiv({ cls: "codex-panel__user-input-question" }); + if (question.header) questionEl.createDiv({ cls: "codex-panel__user-input-header", text: question.header }); + questionEl.createDiv({ cls: "codex-panel__user-input-prompt", text: question.question }); + const draftKey = drafts.draftKey(input.requestId, question.id); + const current = drafts.values.get(draftKey) ?? questionDefaultAnswer(question); + if (!drafts.values.has(draftKey)) drafts.values.set(draftKey, current); + const answerEl = questionEl.createDiv({ cls: "codex-panel__user-input-answer" }); + + if (question.options && question.options.length > 0) { + const groupName = `codex-panel-${String(input.requestId)}-${question.id}`; + for (const option of question.options) { + const label = answerEl.createEl("label", { cls: "codex-panel__user-input-option" }); + const radio = label.createEl("input", { + cls: "codex-panel__user-input-radio", + attr: { type: "radio", name: groupName, value: option.label }, + }); + radio.checked = current === option.label; + radio.onchange = () => { + if (radio.checked) drafts.values.set(draftKey, option.label); + }; + label.createSpan({ cls: "codex-panel__user-input-option-label", text: option.label }); + if (option.description) label.createSpan({ cls: "codex-panel__user-input-option-description", text: option.description }); + } + if (question.isOther) renderOtherUserInputOption(answerEl, input, question.id, groupName, current, drafts); + } else { + renderFreeformUserInput(answerEl, input, question.id, question.isSecret, current, drafts); + } + } +} + +function renderOtherUserInputOption( + parent: HTMLElement, + input: PendingUserInput, + questionId: string, + groupName: string, + current: string, + drafts: PendingRequestMessageDrafts, +): void { + const draftKey = drafts.draftKey(input.requestId, questionId); + const otherKey = drafts.otherDraftKey(input.requestId, questionId); + const otherValue = drafts.values.get(otherKey) ?? ""; + const label = parent.createEl("label", { cls: "codex-panel__user-input-option" }); + const radio = label.createEl("input", { + cls: "codex-panel__user-input-radio", + attr: { type: "radio", name: groupName, value: "__other__" }, + }); + radio.checked = current === otherValue && otherValue.length > 0; + radio.onchange = () => { + if (radio.checked) drafts.values.set(draftKey, drafts.values.get(otherKey) ?? ""); + }; + label.createSpan({ cls: "codex-panel__user-input-option-label", text: "Other" }); + const inputEl = label.createEl("input", { + cls: "codex-panel__user-input-text codex-panel__user-input-other-text", + attr: { type: "text", value: otherValue, placeholder: "Other..." }, + }); + inputEl.oninput = () => { + drafts.values.set(otherKey, inputEl.value); + radio.checked = true; + drafts.values.set(draftKey, inputEl.value); + }; +} + +function renderFreeformUserInput( + parent: HTMLElement, + input: PendingUserInput, + questionId: string, + isSecret: boolean, + current: string, + drafts: PendingRequestMessageDrafts, +): void { + const draftKey = drafts.draftKey(input.requestId, questionId); + const inputEl = parent.createEl("input", { + cls: "codex-panel__user-input-text", + attr: { type: isSecret ? "password" : "text", value: current }, + }); + inputEl.oninput = () => drafts.values.set(draftKey, inputEl.value); +} + +function createActionButton(parent: HTMLElement, label: string, className: string, onClick: () => void): HTMLButtonElement { + const button = parent.createEl("button", { + cls: `codex-panel__pending-request-button ${className}`.trim(), + text: label, + attr: { type: "button" }, + }); + button.onclick = onClick; + return button; +} diff --git a/src/view/scroll.ts b/src/view/scroll.ts new file mode 100644 index 00000000..f6c52d03 --- /dev/null +++ b/src/view/scroll.ts @@ -0,0 +1,51 @@ +export interface ScrollMetrics { + scrollHeight: number; + scrollTop: number; + clientHeight: number; +} + +export interface ScrollAnchor { + key: string; + offset: number; + fallbackTop: number; +} + +export const MESSAGE_BOTTOM_THRESHOLD = 80; +export const MESSAGE_BLOCK_KEY_ATTRIBUTE = "data-codex-panel-block-key"; + +export function isNearScrollBottom(metrics: ScrollMetrics, threshold = MESSAGE_BOTTOM_THRESHOLD): boolean { + return metrics.scrollHeight - metrics.scrollTop - metrics.clientHeight < threshold; +} + +export function bottomScrollTop(metrics: Pick): number { + return metrics.scrollHeight; +} + +export function captureScrollAnchor(container: HTMLElement): ScrollAnchor | null { + const children = Array.from(container.querySelectorAll(`:scope > [${MESSAGE_BLOCK_KEY_ATTRIBUTE}]`)); + if (children.length === 0) return null; + + const viewportTop = container.scrollTop; + const anchor = children.find((child) => child.offsetTop + child.offsetHeight >= viewportTop) ?? children[0]; + const key = anchor.getAttribute(MESSAGE_BLOCK_KEY_ATTRIBUTE); + if (!key) return null; + + return { + key, + offset: anchor.offsetTop - viewportTop, + fallbackTop: viewportTop, + }; +} + +export function restoreScrollAnchor(container: HTMLElement, anchor: ScrollAnchor | null): void { + if (!anchor) return; + + const escapedKey = anchor.key.replace(/["\\]/g, "\\$&"); + const element = container.querySelector(`:scope > [${MESSAGE_BLOCK_KEY_ATTRIBUTE}="${escapedKey}"]`); + if (element) { + container.scrollTop = element.offsetTop - anchor.offset; + return; + } + + container.scrollTop = Math.min(anchor.fallbackTop, Math.max(0, container.scrollHeight - container.clientHeight)); +} diff --git a/src/view/tool-result.ts b/src/view/tool-result.ts new file mode 100644 index 00000000..51d1c038 --- /dev/null +++ b/src/view/tool-result.ts @@ -0,0 +1,109 @@ +import { toolResultView, type ToolResultDetailSection, type ToolResultDisplayItem, type ToolResultView } from "../display/tool-view"; +import { createMetaPair } from "./components"; +import { applyExecutionStateClass } from "./execution-state"; + +export interface ToolResultRenderContext { + workspaceRoot?: string | null; + openDetails: Set; + onDetailsToggle?: () => void; + renderTextWithWikiLinks: (parent: HTMLElement, text: string) => void; +} + +export function renderToolResult(parent: HTMLElement, item: ToolResultDisplayItem, context: ToolResultRenderContext): void { + const view = toolResultView(item, context.workspaceRoot); + const { root, detailsParent } = createToolResultContainer(parent, view, context); + const messageEl = root; + applyExecutionStateClass(messageEl, view.state); + createToolResultHeader(messageEl, view); + const content = messageEl.createDiv({ cls: "codex-panel__tool-summary", attr: { title: view.summary } }); + context.renderTextWithWikiLinks(content, view.summary); + + for (const section of view.details) { + renderDetailSection(detailsParent, section); + } +} + +function createToolResultContainer( + parent: HTMLElement, + view: ToolResultView, + context: ToolResultRenderContext, +): { root: HTMLElement; detailsParent: HTMLElement } { + if (view.details.length === 0) { + const root = parent.createDiv({ cls: `${view.className} codex-panel__tool-result codex-panel__tool-result--plain` }); + return { root, detailsParent: root }; + } + const root = parent.createDiv({ cls: `${view.className} codex-panel__tool-result` }); + const details = root.createEl("details", { cls: "codex-panel__tool-result-details" }); + details.open = context.openDetails.has(view.detailsKey); + setToolResultOpenClass(root, details.open); + details.ontoggle = () => { + setToolResultOpenClass(root, details.open); + if (details.open) { + context.openDetails.add(view.detailsKey); + } else { + context.openDetails.delete(view.detailsKey); + } + context.onDetailsToggle?.(); + }; + return { root, detailsParent: details }; +} + +function setToolResultOpenClass(parent: HTMLElement, open: boolean): void { + parent.classList.toggle("is-open", open); +} + +function createToolResultHeader(parent: HTMLElement, view: ToolResultView): void { + const target = parent.querySelector(":scope > .codex-panel__tool-result-details") ?? parent; + const row = + view.details.length > 0 + ? target.createEl("summary", { cls: "codex-panel__tool-result-header" }) + : target.createDiv({ cls: "codex-panel__tool-result-header" }); + row.createSpan({ cls: "codex-panel__message-role codex-panel__tool-result-label", text: view.label }); +} + +function renderDetailSection(parent: HTMLElement, section: ToolResultDetailSection): void { + if (section.kind === "meta") { + renderMetaBlock(parent, section.title, section.rows); + return; + } + if (section.kind === "diff") { + const diffBlock = renderOutputSection(parent, section.title, "codex-panel__diff-file"); + renderDiff(diffBlock, section.diff); + return; + } + renderOutputBlock(parent, section.title, section.body); +} + +function renderMetaBlock(parent: HTMLElement, title: string | undefined, rows: Array<{ key: string; value: string }>): void { + const section = title ? renderOutputSection(parent, title, "codex-panel__output codex-panel__output--meta") : parent; + const meta = section.createEl("dl", { cls: "codex-panel__meta-grid" }); + for (const row of rows) { + createMetaPair(meta, row.key, row.value); + } +} + +function renderOutputBlock(parent: HTMLElement, title: string, body: string): void { + const section = renderOutputSection(parent, title, "codex-panel__output"); + section.createEl("pre", { text: body }); +} + +function renderOutputSection(parent: HTMLElement, title: string, className: string): HTMLElement { + const section = parent.createDiv({ cls: className }); + section.createDiv({ cls: "codex-panel__output-title", text: title }); + return section; +} + +function renderDiff(parent: HTMLElement, diff: string): void { + const pre = parent.createEl("pre", { cls: "codex-panel__diff" }); + for (const line of diff.split("\n")) { + const cls = + line.startsWith("+") && !line.startsWith("+++") + ? "added" + : line.startsWith("-") && !line.startsWith("---") + ? "removed" + : line.startsWith("@@") + ? "hunk" + : "context"; + pre.createEl("span", { cls: `codex-panel__diff-line codex-panel__diff-line--${cls}`, text: line || " " }); + } +} diff --git a/src/view/toolbar.ts b/src/view/toolbar.ts new file mode 100644 index 00000000..3ecac3f9 --- /dev/null +++ b/src/view/toolbar.ts @@ -0,0 +1,418 @@ +import { setIcon } from "obsidian"; + +import type { EffectiveConfigSection, RateLimitSummary } from "../panel/runtime-view"; +import { createToolbarButton } from "./components"; +import { renderEffectiveConfig } from "./config"; + +export type ToolbarPanelKind = "history" | "status" | "runtime"; +export type ToolbarStatusState = "offline" | "connected" | "running"; + +export interface ToolbarChoice { + label: string; + selected?: boolean; + disabled?: boolean; + title?: string; + meta?: string; + onClick: () => void; +} + +export interface ToolbarThreadRow { + title: string; + threadId: string; + selected: boolean; + disabled: boolean; + canArchive: boolean; + rename: { + draft: string; + generating: boolean; + } | null; +} + +export interface ToolbarDiagnosticRow { + label: string; + value: string; + level?: "normal" | "warning" | "error"; +} + +export interface ToolbarViewModel { + connected: boolean; + status: string; + statusState: ToolbarStatusState; + historyOpen: boolean; + statusPanelOpen: boolean; + runtimeOpen: boolean; + planActive: boolean; + fastActive: boolean; + runtimeSummary: string; + runtimeTitle: string; + runtimeAriaLabel: string; + runtimeEmphasized: boolean; + context: { level: "ok" | "warn" | "danger"; title: string; label: string; percent: number | null } | null; + rateLimit: RateLimitSummary | null; + configSections: EffectiveConfigSection[]; + openPanel: ToolbarPanelKind | null; + threads: ToolbarThreadRow[]; + modelChoices: ToolbarChoice[]; + effortChoices: ToolbarChoice[]; + connectLabel: string; + diagnostics: ToolbarDiagnosticRow[]; +} + +export interface ToolbarActions { + toggleHistory: () => void; + toggleStatusPanel: () => void; + togglePlan: () => void; + toggleFast: () => void; + toggleRuntime: () => void; + connect: () => void; + openCodexConfigFolder: () => void; + refreshThreads: () => void; + resumeThread: (threadId: string) => void; + archiveThread: (threadId: string) => void; + startRenameThread: (threadId: string) => void; + updateRenameDraft: (threadId: string, value: string) => void; + saveRenameThread: (threadId: string, value: string) => void; + cancelRenameThread: (threadId: string) => void; + autoNameThread: (threadId: string) => void; +} + +export function toolbarSignature(model: ToolbarViewModel): string { + return JSON.stringify({ + connected: model.connected, + status: model.status, + statusState: model.statusState, + historyOpen: model.historyOpen, + statusPanelOpen: model.statusPanelOpen, + runtimeOpen: model.runtimeOpen, + planActive: model.planActive, + fastActive: model.fastActive, + runtimeSummary: model.runtimeSummary, + runtimeTitle: model.runtimeTitle, + runtimeEmphasized: model.runtimeEmphasized, + context: model.context, + rateLimit: model.rateLimit, + configSections: model.configSections.map( + (section) => `${section.title}:${section.rows.map((row) => `${row.key}=${row.value}`).join(",")}`, + ), + openPanel: model.openPanel, + threads: model.threads.map( + (thread) => + `${thread.threadId}:${thread.title}:${thread.selected}:${thread.disabled}:${thread.canArchive}:${thread.rename?.draft ?? ""}:${ + thread.rename?.generating ?? false + }`, + ), + modelChoices: model.modelChoices.map((choice) => `${choice.label}:${choice.selected}:${choice.disabled}:${choice.meta ?? ""}`), + effortChoices: model.effortChoices.map((choice) => `${choice.label}:${choice.selected}:${choice.disabled}:${choice.meta ?? ""}`), + connectLabel: model.connectLabel, + diagnostics: model.diagnostics.map((row) => `${row.label}:${row.value}:${row.level ?? "normal"}`), + }); +} + +export function renderToolbar(toolbar: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { + toolbar.empty(); + const primaryRow = toolbar.createDiv({ cls: "codex-panel__toolbar-primary" }); + renderHistoryButton(primaryRow, model, actions); + const runtimeArea = primaryRow.createDiv({ cls: "codex-panel__runtime-area" }); + renderRuntimeStatus(runtimeArea, model, actions); + renderContextMeter(primaryRow, model); + renderStatusButton(primaryRow, model, actions); + renderToolbarPanel(toolbar, model, actions); +} + +function renderHistoryButton(parent: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { + const history = parent.createDiv({ cls: "codex-panel__toolbar-control codex-panel__history-menu" }); + const button = createToolbarButton(history, "history", "Chat history"); + button.addClass("codex-panel__history-toggle"); + if (model.historyOpen) button.addClass("is-active"); + button.setAttr("aria-pressed", model.historyOpen ? "true" : "false"); + button.onclick = actions.toggleHistory; +} + +function renderStatusButton(parent: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { + const label = `Status: ${model.status}; ${model.connected ? "connected" : "not connected"}`; + const button = parent.createEl("button", { + cls: `clickable-icon nav-action-button codex-panel__top-control codex-panel__status-dot codex-panel__status-dot--${model.statusState} ${model.statusPanelOpen ? "is-active" : ""}`, + attr: { + type: "button", + "aria-label": label, + "aria-expanded": model.statusPanelOpen ? "true" : "false", + }, + }); + button.onclick = actions.toggleStatusPanel; +} + +function renderRuntimeStatus(parent: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { + const row = parent.createDiv({ cls: "codex-panel__runtime-strip" }); + renderRuntimeIcon(row, "list-checks", `Plan mode: ${model.planActive ? "on" : "off"}`, model.planActive, actions.togglePlan); + renderRuntimeIcon(row, "zap", `Fast mode: ${model.fastActive ? "on" : "off"}`, model.fastActive, actions.toggleFast); + renderRuntimeModelControl(row, model, actions); +} + +function renderRuntimeIcon(parent: HTMLElement, icon: string, label: string, active: boolean, onClick: () => void): void { + const button = parent.createEl("button", { + cls: `clickable-icon nav-action-button codex-panel__top-control codex-panel__runtime-icon ${active ? "is-active" : ""}`, + attr: { + type: "button", + "aria-label": label, + "aria-pressed": active ? "true" : "false", + }, + }); + setIcon(button, icon); + button.onclick = onClick; +} + +function renderRuntimeModelControl(parent: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { + const cls = [ + "clickable-icon", + "codex-panel__runtime-model", + "codex-panel__top-control", + model.runtimeEmphasized ? "is-emphasized" : "", + model.runtimeOpen ? "is-active" : "", + ] + .filter(Boolean) + .join(" "); + const button = parent.createEl("button", { + cls, + attr: { + type: "button", + "aria-label": model.runtimeAriaLabel, + "aria-expanded": model.runtimeOpen ? "true" : "false", + }, + }); + button.createSpan({ cls: "codex-panel__runtime-model-value", text: model.runtimeSummary }); + button.onclick = actions.toggleRuntime; +} + +function renderContextMeter(parent: HTMLElement, model: ToolbarViewModel): void { + if (!model.context) return; + + const context = parent.createDiv({ + cls: `codex-panel__meter-compact codex-panel__context-compact codex-panel__meter-compact--${model.context.level}`, + }); + context.title = model.context.title; + context.createSpan({ cls: "codex-panel__meter-compact-label codex-panel__context-compact-label", text: model.context.label }); + const bar = context.createSpan({ cls: "codex-panel__meter-compact-bar codex-panel__context-compact-bar" }); + bar.createSpan({ + cls: "codex-panel__meter-compact-fill codex-panel__context-compact-fill", + attr: { + style: `width: ${model.context.percent ?? 0}%`, + }, + }); +} + +function renderToolbarPanel(toolbar: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { + if (!model.openPanel) return; + + const panel = toolbar.createDiv({ cls: `codex-panel__toolbar-panel codex-panel__toolbar-panel--${model.openPanel}` }); + if (model.openPanel === "history") { + renderThreadList(panel, model.threads, actions); + } else if (model.openPanel === "runtime") { + renderRuntimePicker(panel, model); + } else { + renderStatusPanel(panel, model, actions); + } +} + +function renderStatusPanel(parent: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { + const statusItems = parent.createDiv({ cls: "codex-panel__status-panel-items", attr: { role: "menu" } }); + createToolbarPanelItem(statusItems, model.connectLabel, { onClick: actions.connect, className: "codex-panel__status-panel-item" }); + createToolbarPanelItem(statusItems, "Refresh thread list", { + onClick: actions.refreshThreads, + className: "codex-panel__status-panel-item", + }); + createToolbarPanelItem(statusItems, "Open Codex config folder", { + onClick: actions.openCodexConfigFolder, + className: "codex-panel__status-panel-item", + }); + renderRateLimitPanel(parent, model.rateLimit); + renderConnectionDiagnostics(parent, model.diagnostics); + renderEffectiveConfig(parent, model.configSections); +} + +function renderRateLimitPanel(parent: HTMLElement, rateLimit: RateLimitSummary | null): void { + if (!rateLimit) return; + + const section = parent.createDiv({ cls: `codex-panel__limit-panel codex-panel__limit-panel--${rateLimit.level}` }); + section.createDiv({ cls: "codex-panel__limit-panel-title", text: "Usage Limit" }); + const list = section.createDiv({ cls: "codex-panel__limit-panel-list" }); + for (const row of rateLimit.rows) { + const item = list.createDiv({ + cls: `codex-panel__limit-panel-row codex-panel__limit-panel-row--${row.level}`, + attr: { title: row.title }, + }); + item.createDiv({ cls: "codex-panel__limit-panel-label", text: row.label }); + item.createDiv({ cls: "codex-panel__limit-panel-value", text: row.value }); + const meter = item.createDiv({ cls: "codex-panel__limit-panel-meter" }); + meter.createDiv({ + cls: "codex-panel__limit-panel-fill", + attr: { style: `width: ${row.percent}%` }, + }); + item.createDiv({ cls: "codex-panel__limit-panel-reset", text: row.resetLabel ?? "" }); + } +} + +function renderConnectionDiagnostics(parent: HTMLElement, rows: ToolbarDiagnosticRow[]): void { + const diagnostics = parent.createDiv({ cls: "codex-panel__connection-diagnostics" }); + diagnostics.createDiv({ cls: "codex-panel__connection-diagnostics-title", text: "Connection diagnostics" }); + const list = diagnostics.createEl("dl", { cls: "codex-panel__connection-diagnostics-list" }); + for (const row of rows) { + const item = list.createDiv({ + cls: `codex-panel__connection-diagnostics-row codex-panel__connection-diagnostics-row--${row.level ?? "normal"}`, + }); + item.createEl("dt", { text: row.label }); + item.createEl("dd", { text: row.value }); + } +} + +function renderRuntimePicker(parent: HTMLElement, model: ToolbarViewModel): void { + const picker = parent.createDiv({ cls: "codex-panel__runtime-picker", attr: { role: "listbox", "aria-label": "Runtime settings" } }); + picker.createDiv({ cls: "codex-panel__runtime-picker-label", text: "Model" }); + for (const choice of model.modelChoices) { + createToolbarPanelItem(picker, choice.label, { ...choice, className: "codex-panel__runtime-choice" }); + } + picker.createDiv({ cls: "codex-panel__runtime-picker-label", text: "Effort" }); + for (const choice of model.effortChoices) { + createToolbarPanelItem(picker, choice.label, { ...choice, className: "codex-panel__runtime-choice" }); + } +} + +function renderThreadList(parent: HTMLElement, threads: ToolbarThreadRow[], actions: ToolbarActions): void { + const threadsEl = parent.createDiv({ cls: "codex-panel__threads", attr: { role: "listbox", "aria-label": "Chat history" } }); + if (threads.length === 0) { + const empty = threadsEl.createDiv({ + cls: "menu-item codex-panel__toolbar-panel-item codex-panel__thread codex-panel__thread--empty is-disabled", + }); + empty.createSpan({ cls: "menu-item-icon codex-panel__toolbar-panel-check" }); + empty.createSpan({ cls: "menu-item-title codex-panel__toolbar-panel-label", text: "No threads" }); + return; + } + + for (const thread of threads) { + const row = threadsEl.createDiv({ + cls: `codex-panel__thread-row ${thread.rename ? "codex-panel__thread-row--renaming" : ""}`, + }); + if (thread.rename) { + renderThreadRenameRow(row, thread, actions); + continue; + } + + createToolbarPanelItem(row, thread.title, { + selected: thread.selected, + disabled: thread.disabled, + title: `${thread.title}\n${thread.threadId}`, + className: "codex-panel__thread", + onClick: () => actions.resumeThread(thread.threadId), + }); + + const rename = createToolbarButton(row, "pencil", "Rename thread"); + rename.addClass("codex-panel__thread-action"); + rename.disabled = thread.disabled; + rename.onclick = (event) => { + event.stopPropagation(); + actions.startRenameThread(thread.threadId); + }; + + if (thread.canArchive) { + const action = createToolbarButton(row, "archive", "Archive thread"); + action.addClass("codex-panel__thread-action"); + action.disabled = thread.disabled; + action.onclick = (event) => { + event.stopPropagation(); + actions.archiveThread(thread.threadId); + }; + } + } +} + +function renderThreadRenameRow(parent: HTMLElement, thread: ToolbarThreadRow, actions: ToolbarActions): void { + const form = parent.createDiv({ cls: "codex-panel__thread-rename" }); + const input = form.createEl("input", { + cls: "codex-panel__thread-rename-input", + attr: { + type: "text", + value: thread.rename?.draft ?? thread.title, + "aria-label": `Rename ${thread.title}`, + }, + }); + input.oninput = () => actions.updateRenameDraft(thread.threadId, input.value); + input.onkeydown = (event) => { + if (event.key === "Enter") { + event.preventDefault(); + if (!thread.rename?.generating) actions.saveRenameThread(thread.threadId, input.value); + } else if (event.key === "Escape") { + event.preventDefault(); + actions.cancelRenameThread(thread.threadId); + } + }; + window.setTimeout(() => { + if (document.activeElement !== input) { + input.focus(); + input.select(); + } + }, 0); + + const save = createToolbarButton(form, "check", "Save thread name"); + save.addClass("codex-panel__thread-action"); + save.disabled = thread.rename?.generating ?? false; + save.onclick = (event) => { + event.stopPropagation(); + actions.saveRenameThread(thread.threadId, input.value); + }; + + const cancel = createToolbarButton(form, "x", "Cancel rename"); + cancel.addClass("codex-panel__thread-action"); + cancel.onclick = (event) => { + event.stopPropagation(); + actions.cancelRenameThread(thread.threadId); + }; + + const autoName = createToolbarButton(form, thread.rename?.generating ? "loader" : "sparkles", "Auto-name thread"); + autoName.addClass("codex-panel__thread-action"); + autoName.disabled = thread.rename?.generating ?? false; + autoName.onclick = (event) => { + event.stopPropagation(); + actions.autoNameThread(thread.threadId); + }; +} + +function createToolbarPanelItem( + parent: HTMLElement, + label: string, + options: { + selected?: boolean; + disabled?: boolean; + title?: string; + meta?: string; + className?: string; + onClick?: () => void; + } = {}, +): HTMLElement { + const selected = Boolean(options.selected); + const disabled = Boolean(options.disabled); + const item = parent.createEl("button", { + cls: [ + "menu-item", + "tappable", + "codex-panel__toolbar-panel-item", + options.className ?? "", + selected ? "selected is-selected" : "", + disabled ? "is-disabled" : "", + ] + .filter(Boolean) + .join(" "), + attr: { + type: "button", + title: options.title ?? label, + "aria-selected": selected ? "true" : "false", + }, + }); + item.disabled = disabled; + item.onclick = () => { + if (!disabled) options.onClick?.(); + }; + const check = item.createSpan({ cls: "menu-item-icon codex-panel__toolbar-panel-check" }); + if (selected) setIcon(check, "check"); + item.createSpan({ cls: "menu-item-title codex-panel__toolbar-panel-label", text: label }); + if (options.meta) item.createSpan({ cls: "codex-panel__toolbar-panel-meta", text: options.meta }); + return item; +} diff --git a/src/view/work-items.ts b/src/view/work-items.ts new file mode 100644 index 00000000..9abdb334 --- /dev/null +++ b/src/view/work-items.ts @@ -0,0 +1,158 @@ +import { activeAgentRunSummary, executionState } from "../display/model"; +import { isReasoningActive } from "../display/signature"; +import type { AgentDisplayItem, AgentRunSummary, AgentRunSummaryAgent, DisplayItem, TaskProgressDisplayItem } from "../display/types"; +import { agentActivityMetaLabel, agentMessagePreview, agentRunSummaryLabel, taskStatusMarker } from "../display/labels"; +import type { MessageStreamContext } from "./message-stream"; +import { createMetaPair, createRememberedDetails } from "./components"; +import { createWorkMessage } from "./work-message"; +import { shortThreadId } from "../utils"; + +const AGENT_ROW_MESSAGE_PREVIEW_LIMIT = 120; + +export function activeAgentRunSummaryBlock(context: MessageStreamContext): AgentRunSummary | null { + return activeAgentRunSummary(context.displayItems, context.activeTurnId, context.busy); +} + +export function createAgentRunSummaryElement(summary: AgentRunSummary): HTMLElement { + const messageEl = createWorkMessage(createDiv(), { + label: "agents", + className: "codex-panel__agent-summary", + state: summary.failed > 0 ? "failed" : "running", + }); + messageEl.createDiv({ cls: "codex-panel__tool-summary", text: agentRunSummaryLabel(summary) }); + renderAgentSummaryRows(messageEl, summary); + return messageEl; +} + +export function renderTaskProgressItem(parent: HTMLElement, item: TaskProgressDisplayItem): void { + const messageEl = createWorkMessage(parent, { + label: "tasks", + className: "codex-panel__task-progress", + state: executionState(item), + }); + if (item.explanation) { + messageEl.createDiv({ cls: "codex-panel__tool-summary", text: item.explanation }); + } + if (item.steps.length === 0) { + messageEl.createDiv({ cls: "codex-panel__tool-summary", text: "Plan updated" }); + return; + } + const list = messageEl.createEl("ul", { cls: "codex-panel__task-list" }); + for (const step of item.steps) { + const row = list.createEl("li", { cls: `codex-panel__task-step codex-panel__task-step--${step.status}` }); + row.createSpan({ cls: "codex-panel__task-marker", text: taskStatusMarker(step.status) }); + row.createSpan({ cls: "codex-panel__task-text", text: step.step }); + } +} + +export function renderAgentItem(parent: HTMLElement, item: AgentDisplayItem, context: MessageStreamContext): void { + const messageEl = createWorkMessage(parent, { + label: "agent", + className: "codex-panel__agent-activity", + state: executionState(item), + }); + messageEl.createDiv({ cls: "codex-panel__tool-summary", text: agentSummaryText(item) }); + + const details = createRememberedDetails( + messageEl, + context.openDetails, + `${item.id}:agent-details`, + "codex-panel__output codex-panel__agent-details", + "Details", + false, + context.onDetailsToggle, + ); + const meta = details.createEl("dl", { cls: "codex-panel__meta-grid" }); + createMetaPair(meta, "tool", agentActivityMetaLabel(item.tool)); + createMetaPair(meta, "status", item.status); + createMetaPair(meta, "sender", item.senderThreadId); + if (item.receiverThreadIds.length > 0) createMetaPair(meta, "target", item.receiverThreadIds.join(", ")); + if (item.model) createMetaPair(meta, "model", item.model); + if (item.reasoningEffort) createMetaPair(meta, "effort", item.reasoningEffort); + + if (item.agents.length > 0) { + const list = messageEl.createEl("ul", { cls: "codex-panel__agent-list" }); + for (const agent of item.agents) { + const row = list.createEl("li", { cls: "codex-panel__agent-row" }); + row.title = agent.threadId; + row.createSpan({ cls: "codex-panel__agent-thread", text: shortThreadId(agent.threadId) }); + row.createSpan({ cls: "codex-panel__agent-status", text: agentStatusLabel(agent.status, agent.message) }); + } + } + + for (const agent of item.agents) { + if (!agent.message || !isLongAgentMessage(agent.message)) continue; + const details = createRememberedDetails( + messageEl, + context.openDetails, + `${item.id}:agent:${agent.threadId}:message`, + "codex-panel__output", + `Agent output ${shortThreadId(agent.threadId)}`, + false, + context.onDetailsToggle, + ); + details.createEl("pre", { text: agent.message }); + } + + if (item.prompt) { + const details = createRememberedDetails( + messageEl, + context.openDetails, + `${item.id}:prompt`, + "codex-panel__output", + "Prompt", + false, + context.onDetailsToggle, + ); + details.createEl("pre", { text: item.prompt }); + } +} + +export function renderReasoningItem(parent: HTMLElement, item: DisplayItem, context: MessageStreamContext): void { + const messageEl = parent.createDiv({ cls: "codex-panel__reasoning" }); + const active = isReasoningActive(item, context); + if (active) messageEl.addClass("is-active"); + messageEl.createDiv({ cls: "codex-panel__reasoning-role", text: active ? "reasoning" : "thought" }); + const content = messageEl.createDiv({ cls: "codex-panel__reasoning-content" }); + content.createSpan({ text: item.text || (active ? "Reasoning" : "Thought") }); + if (active) { + const dots = content.createSpan({ cls: "codex-panel__reasoning-dots" }); + dots.createSpan({ text: "." }); + dots.createSpan({ text: "." }); + dots.createSpan({ text: "." }); + } +} + +function renderAgentSummaryRows(parent: HTMLElement, summary: AgentRunSummary): void { + if (summary.agents.length === 0 && summary.additionalAgents === 0) return; + const list = parent.createEl("ul", { cls: "codex-panel__agent-list codex-panel__agent-list--summary" }); + for (const agent of summary.agents) { + const row = list.createEl("li", { cls: "codex-panel__agent-row" }); + row.title = agent.threadId; + row.createSpan({ cls: "codex-panel__agent-thread", text: shortThreadId(agent.threadId) }); + row.createSpan({ cls: "codex-panel__agent-status", text: agentSummaryStatusLabel(agent) }); + } + if (summary.additionalAgents > 0) { + const row = list.createEl("li", { cls: "codex-panel__agent-row codex-panel__agent-row--more" }); + row.createSpan({ cls: "codex-panel__agent-thread", text: "" }); + row.createSpan({ cls: "codex-panel__agent-status", text: `+${summary.additionalAgents} more` }); + } +} + +function agentSummaryText(item: AgentDisplayItem): string { + const target = item.receiverThreadIds.length === 0 ? "" : ` ${item.receiverThreadIds.map(shortThreadId).join(", ")}`; + return `${agentActivityMetaLabel(item.tool)}${target} (${item.status})`; +} + +function agentStatusLabel(status: string, message: string | null): string { + const preview = agentMessagePreview(message, AGENT_ROW_MESSAGE_PREVIEW_LIMIT); + return preview ? `${status}: ${preview}` : status; +} + +function agentSummaryStatusLabel(agent: AgentRunSummaryAgent): string { + return agent.messagePreview ? `${agent.status}: ${agent.messagePreview}` : agent.status; +} + +function isLongAgentMessage(message: string): boolean { + return message.length > AGENT_ROW_MESSAGE_PREVIEW_LIMIT || message.includes("\n"); +} diff --git a/src/view/work-message.ts b/src/view/work-message.ts new file mode 100644 index 00000000..c9cb6015 --- /dev/null +++ b/src/view/work-message.ts @@ -0,0 +1,19 @@ +import type { ExecutionState } from "../display/types"; +import { applyExecutionStateClass } from "./execution-state"; + +export interface WorkMessageOptions { + label: string; + className: string; + state?: ExecutionState; + tone?: "warning"; +} + +export function createWorkMessage(parent: HTMLElement, options: WorkMessageOptions): HTMLElement { + const messageEl = parent.createDiv({ + cls: `codex-panel__message codex-panel__message--tool codex-panel__work-message ${options.className}`, + }); + if (options.tone) messageEl.addClass(`codex-panel__work-message--${options.tone}`); + applyExecutionStateClass(messageEl, options.state ?? null); + messageEl.createDiv({ cls: "codex-panel__message-role", text: options.label }); + return messageEl; +} diff --git a/styles.css b/styles.css new file mode 100644 index 00000000..9b70f926 --- /dev/null +++ b/styles.css @@ -0,0 +1,1483 @@ +.codex-panel, +.codex-panel-settings { + --codex-panel-top-icon-size: var(--icon-s, 16px); + --codex-panel-icon-button-inline-size: calc(var(--codex-panel-top-icon-size) + var(--size-2-3, 6px) * 2); + --codex-panel-icon-button-block-size: calc(var(--codex-panel-top-icon-size) + var(--size-2-2, 4px) * 2); + --codex-panel-status-dot-size: var(--size-4-2, 8px); + --codex-panel-meter-bar-height: var(--size-2-2, 4px); + --codex-panel-meter-compact-bar-width: 30px; + --codex-panel-meter-compact-gap: 7px; + --codex-panel-meter-compact-min-width: 68px; + --codex-panel-meter-compact-max-width: 88px; + --codex-panel-composer-control-size: var(--codex-panel-icon-button-block-size); + --codex-panel-composer-default-height: calc((var(--codex-panel-composer-control-size) * 2) + var(--codex-panel-control-gap)); + --codex-panel-composer-max-height: min(208px, 40vh); + --codex-panel-composer-suggestions-max-height: min(168px, 36vh); + --codex-panel-toolbar-text-color: var(--text-muted); + --codex-panel-toolbar-text-size: var(--font-ui-small); + --codex-panel-toolbar-meta-size: var(--font-ui-smaller); + --codex-panel-toolbar-line-height: var(--line-height-tight); + --codex-panel-toolbar-menu-max-height: min(232px, 45vh); + --codex-panel-toolbar-status-max-height: min(384px, 55vh); + --codex-panel-content-font: var(--font-text); + --codex-panel-content-font-size: calc(var(--font-text-size, 16px) * 0.9375); + --codex-panel-edge-padding-x: var(--size-4-3, 12px); + --codex-panel-toolbar-padding-x: var(--size-4-2, 8px); + --codex-panel-toolbar-padding-y: var(--size-4-2, 8px); + --codex-panel-toolbar-button-gap: var(--size-2-1, 2px); + --codex-panel-message-padding-y: var(--size-4-3, 12px); + --codex-panel-composer-padding-y: var(--size-4-2, 8px); + --codex-panel-composer-padding-bottom: calc(var(--font-text-size, 16px) * 2); + --codex-panel-section-padding: var(--codex-panel-edge-padding-x); + --codex-panel-section-gap: var(--size-4-2, 8px); + --codex-panel-item-gap: var(--size-2-3, 6px); + --codex-panel-control-gap: var(--size-2-2, 4px); + --codex-panel-panel-gap: var(--size-2-1, 2px); + --codex-panel-message-indent: calc(var(--size-4-2, 8px) + var(--size-2-1, 2px)); + --codex-panel-card-radius: var(--radius-s); +} + +.codex-panel { + box-sizing: border-box; + container-type: inline-size; + display: flex; + flex-direction: column; + height: 100%; + min-width: 0; + min-height: 0; + padding: 0; + overflow: hidden; + color: var(--text-normal); +} + +.workspace-leaf-content[data-type="codex-app-server-panel"] .view-content.codex-panel { + padding: 0; + overflow: hidden; +} + +.codex-panel__toolbar { + flex: 0 0 auto; + display: flex; + flex-direction: column; + padding: var(--codex-panel-toolbar-padding-y) var(--codex-panel-toolbar-padding-x); +} + +.codex-panel__toolbar-primary { + display: grid; + grid-template-columns: auto minmax(42px, 1fr) auto auto; + gap: var(--codex-panel-toolbar-button-gap); + align-items: center; + min-width: 0; +} + +.codex-panel__meter-compact-label, +.codex-panel__context-compact-label, +.codex-panel__runtime-model-value, +.codex-panel__limit-panel-label, +.codex-panel__limit-panel-value, +.codex-panel__limit-panel-reset, +.codex-panel__toolbar-panel-label, +.codex-panel__toolbar-panel-meta, +.codex-panel__suggestion-label, +.codex-panel__suggestion-detail { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.codex-panel__toolbar-control, +.codex-panel__runtime-area { + min-width: 0; +} + +.codex-panel__history-menu { + grid-column: 1; +} + +.codex-panel__runtime-area { + grid-column: 2; +} + +.codex-panel__runtime-strip { + display: inline-flex; + align-items: center; + gap: var(--codex-panel-toolbar-button-gap); + max-width: 100%; + min-width: 0; + white-space: nowrap; +} + +.codex-panel__top-control { + box-sizing: border-box; + --icon-size: var(--codex-panel-top-icon-size); + --icon-stroke: var(--icon-s-stroke-width, 2px); + min-width: var(--codex-panel-icon-button-inline-size); + width: auto; + height: auto; + padding: var(--size-2-2, 4px) var(--size-2-3, 6px); + border: 0; + border-radius: var(--clickable-icon-radius, var(--radius-s)); +} + +.codex-panel__top-control svg { + width: var(--icon-size); + height: var(--icon-size); + stroke-width: var(--icon-stroke); +} + +.codex-panel__runtime-icon { + flex: 0 0 auto; +} + +.codex-panel__runtime-icon.is-active { + color: var(--icon-color-active); + background: var(--background-modifier-active-hover); +} + +.codex-panel__runtime-model { + display: inline-flex; + align-items: center; + flex: 0 0 auto; + min-width: 92px; + max-width: 150px; + gap: var(--codex-panel-control-gap); + padding: var(--size-2-2, 4px) var(--size-2-3, 6px); + border: 0; + appearance: none; + background: transparent !important; + box-shadow: none !important; + color: var(--text-muted); + font: inherit; + font-size: var(--codex-panel-toolbar-text-size); + line-height: var(--codex-panel-toolbar-line-height); + text-align: left; + cursor: pointer; + user-select: none; +} + +.codex-panel__runtime-model.is-emphasized { + background: var(--background-modifier-active-hover) !important; + color: var(--text-normal); +} + +.codex-panel__runtime-model:hover, +.codex-panel__runtime-model.is-active { + color: var(--text-normal); +} + +.codex-panel__runtime-model.is-active { + background: var(--background-modifier-hover) !important; +} + +.codex-panel__runtime-model:hover { + background: var(--background-modifier-hover) !important; +} + +.codex-panel__runtime-model.is-emphasized:hover, +.codex-panel__runtime-model.is-emphasized.is-active { + background: var(--background-modifier-active-hover) !important; +} + +.codex-panel__runtime-model-value { + flex: 0 1 auto; + min-width: 0; + color: var(--codex-panel-toolbar-text-color); +} + +.codex-panel__runtime-model.is-emphasized .codex-panel__runtime-model-value { + color: var(--text-accent); +} + +.codex-panel__runtime-picker { + gap: var(--codex-panel-panel-gap); +} + +.codex-panel__toolbar-panel-item { + display: flex; + align-items: center; + gap: var(--size-4-2); + width: 100%; + min-width: 0; + height: auto; + padding: var(--size-4-1) var(--size-4-2); + border: 0 !important; + border-radius: var(--radius-s); + appearance: none; + background: transparent !important; + box-shadow: none !important; + color: var(--codex-panel-toolbar-text-color); + font: inherit; + font-size: var(--codex-panel-toolbar-text-size); + line-height: var(--codex-panel-toolbar-line-height); + text-align: left; + cursor: pointer; + user-select: none; +} + +.codex-panel__toolbar-panel-check { + flex: 0 0 auto; + min-width: var(--icon-s, 16px); + color: var(--text-accent); +} + +.codex-panel__toolbar-panel-check svg { + width: var(--icon-s, 16px); + height: var(--icon-s, 16px); +} + +.codex-panel__toolbar-panel-label, +.codex-panel__toolbar-panel-meta { + min-width: 0; +} + +.codex-panel__toolbar-panel-label { + flex: 1 1 auto; + color: var(--codex-panel-toolbar-text-color); +} + +.codex-panel__toolbar-panel-meta { + flex: 0 1 auto; + color: var(--text-faint); + font-size: var(--codex-panel-toolbar-meta-size); +} + +.codex-panel__toolbar-panel-item:not(:disabled):not(.is-disabled):hover, +.codex-panel__toolbar-panel-item.selected, +.codex-panel__toolbar-panel-item.is-selected, +.codex-panel__toolbar-panel-item.is-active { + color: var(--text-normal); +} + +.codex-panel__toolbar-panel-item:not(:disabled):not(.is-disabled):hover { + background: var(--background-modifier-hover) !important; +} + +.codex-panel__toolbar-panel-item.is-disabled { + cursor: default; + opacity: 0.45; +} + +.codex-panel__toolbar-panel-item:disabled { + cursor: default; + opacity: 0.45; +} + +.codex-panel__runtime-picker-empty { + padding: 5px 8px 5px 31px; + color: var(--text-faint); + font-size: var(--codex-panel-toolbar-text-size); +} + +.codex-panel__runtime-picker-label { + padding: var(--size-4-1) var(--size-4-2) var(--size-2-1) calc(var(--icon-s, 16px) + var(--size-4-2) * 2); + color: var(--text-faint); + font-size: var(--codex-panel-toolbar-meta-size); + font-weight: var(--font-medium); +} + +.codex-panel-settings__section, +.codex-panel-settings__dynamic-section { + margin-top: var(--size-4-6); +} + +.codex-panel-settings__section-heading.setting-item-heading, +.codex-panel-settings__dynamic-section-heading.setting-item-heading { + margin-bottom: var(--size-4-2); +} + +.codex-panel-settings__dynamic-section-status { + padding: 0 var(--size-4-4); +} + +.codex-panel-settings__section-status { + padding: 0 var(--size-4-4); +} + +.codex-panel-settings__dynamic-list-summary { + padding: 0 var(--size-4-4); + margin: 0 0 var(--size-2-3); +} + +.codex-panel-settings__dynamic-list { + margin: var(--size-2-3) 0 0; + overflow: auto; + border: var(--border-width) solid var(--background-modifier-border); + border-radius: var(--radius-s); + background: var(--background-primary); +} + +.codex-panel-settings__archived-list { + max-height: min(360px, 45vh); +} + +.codex-panel-settings__hook-list { + max-height: min(420px, 50vh); +} + +.codex-panel-settings__dynamic-row.setting-item { + margin: 0; + padding: var(--size-4-2) var(--size-4-3); + border: 0; + border-top: var(--border-width) solid var(--background-modifier-border); + border-radius: 0; + background: transparent; +} + +.codex-panel-settings__dynamic-row.setting-item:first-child { + border-top: 0; +} + +.codex-panel-settings__dynamic-row .setting-item-info { + min-width: 0; +} + +.codex-panel-settings__dynamic-row .setting-item-name, +.codex-panel-settings__dynamic-row .setting-item-description { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.codex-panel-settings__hook-hash { + overflow: hidden; + color: var(--text-faint); + font-size: var(--font-ui-smaller); + text-overflow: ellipsis; + white-space: nowrap; +} + +.codex-panel-settings__hook-hash { + padding-top: var(--size-2-1); + font-family: var(--font-monospace); +} + +.codex-panel-settings__hook-warning { + color: var(--text-warning); +} + +.codex-panel-settings__hook-error { + color: var(--text-error); +} + +.codex-panel__status-dot { + grid-column: 4; + cursor: pointer; +} + +.codex-panel__status-dot::before { + content: ""; + width: var(--codex-panel-status-dot-size); + height: var(--codex-panel-status-dot-size); + border-radius: 999px; + background: var(--text-faint); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-faint) 12%, transparent); +} + +.codex-panel__status-dot--connected::before { + background: var(--text-success); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-success) 12%, transparent); +} + +.codex-panel__status-dot--running::before { + background: var(--interactive-accent); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--interactive-accent) 14%, transparent); +} + +.codex-panel__meter-compact { + display: flex; + align-items: center; + justify-content: flex-end; + gap: var(--codex-panel-meter-compact-gap); + height: var(--codex-panel-icon-button-block-size); + min-width: var(--codex-panel-meter-compact-min-width); + max-width: var(--codex-panel-meter-compact-max-width); + padding: 0 2px; + color: var(--codex-panel-toolbar-text-color); + font-size: var(--codex-panel-toolbar-text-size); + line-height: var(--codex-panel-toolbar-line-height); +} + +.codex-panel__context-compact { + grid-column: 3; +} + +.codex-panel__meter-compact-bar { + flex: 0 0 var(--codex-panel-meter-compact-bar-width); + height: var(--codex-panel-meter-bar-height); + overflow: hidden; + border-radius: 999px; + background: var(--background-modifier-border); +} + +.codex-panel__meter-compact-fill { + display: block; + height: 100%; + border-radius: inherit; + background: var(--text-success); +} + +.codex-panel__meter-compact--warn .codex-panel__meter-compact-fill { + background: var(--text-warning); +} + +.codex-panel__meter-compact--danger .codex-panel__meter-compact-fill { + background: var(--text-error); +} + +.codex-panel__toolbar-panel { + display: flex; + flex-direction: column; + gap: var(--codex-panel-panel-gap); + max-height: var(--codex-panel-toolbar-menu-max-height); + overflow: auto; + margin-top: var(--codex-panel-item-gap); + padding: var(--codex-panel-item-gap) 0 0; + border-top: 1px solid var(--background-modifier-border); + background: transparent; +} + +.codex-panel__toolbar-panel--status { + max-height: var(--codex-panel-toolbar-status-max-height); +} + +.codex-panel__status-panel-items { + display: flex; + flex-direction: column; + gap: var(--codex-panel-panel-gap); +} + +.codex-panel__limit-panel { + margin-top: 2px; + padding-top: 6px; + border-top: 1px solid var(--background-modifier-border-hover); +} + +.codex-panel__limit-panel-title { + padding: 0 8px 4px; + color: var(--text-faint); + font-size: var(--font-ui-smaller); + font-weight: var(--font-semibold); + line-height: var(--line-height-tight); +} + +.codex-panel__limit-panel-list { + display: flex; + flex-direction: column; + gap: 2px; +} + +.codex-panel__limit-panel-row { + display: grid; + grid-template-columns: 2.5em 4ch minmax(64px, 1fr) 9.5em; + gap: var(--codex-panel-control-gap); + align-items: center; + padding: 4px 8px; +} + +.codex-panel__limit-panel-label, +.codex-panel__limit-panel-value, +.codex-panel__limit-panel-reset { + min-width: 0; + color: var(--text-muted); + font-size: var(--font-ui-smaller); + line-height: var(--line-height-tight); +} + +.codex-panel__limit-panel-value { + text-align: right; + font-variant-numeric: tabular-nums; +} + +.codex-panel__limit-panel-reset { + color: var(--text-faint); +} + +.codex-panel__limit-panel-meter { + height: var(--codex-panel-meter-bar-height); + margin: 0 3px; + overflow: hidden; + border-radius: 999px; + background: var(--background-modifier-border); +} + +.codex-panel__limit-panel-fill { + display: block; + height: 100%; + border-radius: inherit; + background: var(--text-success); +} + +.codex-panel__limit-panel-row--warn .codex-panel__limit-panel-fill { + background: var(--text-warning); +} + +.codex-panel__limit-panel-row--danger .codex-panel__limit-panel-fill { + background: var(--text-error); +} + +.codex-panel__limit-panel-row--warn .codex-panel__limit-panel-value { + color: var(--text-warning); +} + +.codex-panel__limit-panel-row--danger .codex-panel__limit-panel-value { + color: var(--text-error); +} + +.codex-panel__connection-diagnostics { + margin-top: 2px; + padding-top: 6px; + border-top: 1px solid var(--background-modifier-border-hover); +} + +.codex-panel__connection-diagnostics-title { + padding: 0 8px 4px; + color: var(--text-faint); + font-size: var(--font-ui-smaller); + font-weight: var(--font-semibold); + line-height: var(--line-height-tight); +} + +.codex-panel__connection-diagnostics-list { + margin: 0; +} + +.codex-panel__connection-diagnostics-row { + display: grid; + grid-template-columns: minmax(96px, 36%) minmax(0, 1fr); + gap: var(--codex-panel-section-gap); + padding: 4px 8px; +} + +.codex-panel__connection-diagnostics-row dt, +.codex-panel__connection-diagnostics-row dd { + margin: 0; + min-width: 0; + font-size: var(--font-ui-smaller); + line-height: var(--line-height-tight); +} + +.codex-panel__connection-diagnostics-row dt { + color: var(--text-faint); +} + +.codex-panel__connection-diagnostics-row dd { + color: var(--text-muted); + overflow-wrap: anywhere; + white-space: pre-wrap; +} + +.codex-panel__connection-diagnostics-row--warning dd { + color: var(--text-warning); +} + +.codex-panel__connection-diagnostics-row--error dd { + color: var(--text-error); +} + +.codex-panel__body { + flex: 1 1 auto; + display: grid; + grid-template-rows: auto 1fr auto; + min-width: 0; + min-height: 0; +} + +.codex-panel__body > *, +.codex-panel__slot { + min-width: 0; +} + +.codex-panel__slot--messages { + min-height: 0; + overflow: hidden; +} + +.codex-panel__config { + min-width: 0; + max-width: 100%; + padding: 8px; + border-bottom: 1px solid var(--background-modifier-border); + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +.codex-panel__toolbar-panel .codex-panel__config { + margin-top: 2px; + padding: 6px 0 0; + border-top: 1px solid var(--background-modifier-border-hover); + border-bottom: 0; +} + +.codex-panel__toolbar-panel .codex-panel__config-title { + padding: 0 8px 4px; + color: var(--text-faint); + font-size: var(--font-ui-smaller); + font-weight: var(--font-semibold); + line-height: var(--line-height-tight); +} + +.codex-panel__toolbar-panel .codex-panel__config-list { + margin: 0; + overflow: visible; + border: 0; + border-radius: 0; + background: transparent; +} + +.codex-panel__toolbar-panel .codex-panel__config-section { + padding: 6px 8px 2px; + border-top: 0; + color: var(--text-faint); + font-size: var(--font-ui-smaller); + font-weight: var(--font-semibold); + line-height: var(--line-height-tight); +} + +.codex-panel__toolbar-panel .codex-panel__config-section:first-child { + padding-top: 0; +} + +.codex-panel__toolbar-panel .codex-panel__config-row { + display: grid; + grid-template-columns: minmax(96px, 36%) minmax(0, 1fr); + gap: var(--codex-panel-section-gap); + padding: 4px 8px; + border-top: 0; +} + +.codex-panel__toolbar-panel .codex-panel__config-row dt, +.codex-panel__toolbar-panel .codex-panel__config-row dd { + font-size: var(--font-ui-smaller); + line-height: var(--line-height-tight); +} + +.codex-panel__config-title, +.codex-panel__approval-details summary, +.codex-panel__output summary, +.codex-panel__diff-file summary, +.codex-panel__output-title { + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +.codex-panel__approval-details summary:hover, +.codex-panel__output summary:hover, +.codex-panel__diff-file summary:hover { + color: var(--text-normal); +} + +.codex-panel__config-list { + margin: 8px 0 0; + overflow: hidden; + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-s); + background: var(--background-primary); +} + +.codex-panel__config-row { + display: grid; + grid-template-columns: minmax(82px, 34%) minmax(0, 1fr); + gap: var(--codex-panel-section-gap); + align-items: start; + padding: 5px 8px; + border-top: 1px solid var(--background-modifier-border-hover); +} + +.codex-panel__config-row:first-child { + border-top: 0; +} + +.codex-panel__config-section { + padding: 7px 8px 4px; + border-top: 1px solid var(--background-modifier-border-hover); + color: var(--text-faint); + font-size: var(--font-ui-smaller); + font-weight: var(--font-semibold); + line-height: var(--line-height-tight); +} + +.codex-panel__config-section:first-child { + border-top: 0; +} + +.codex-panel__config-row dt, +.codex-panel__config-row dd { + margin: 0; + min-width: 0; + font-size: var(--font-ui-smaller); + line-height: var(--line-height-tight); +} + +.codex-panel__config-row dt { + color: var(--text-faint); +} + +.codex-panel__config-row dd { + color: var(--text-muted); + overflow-wrap: anywhere; + white-space: pre-wrap; +} + +.codex-panel__meta-grid { + display: grid; + grid-template-columns: max-content minmax(0, 1fr); + gap: var(--codex-panel-control-gap) var(--codex-panel-section-gap); + margin: 6px 0; + font-size: var(--font-ui-smaller); +} + +.codex-panel__meta-grid dt { + color: var(--text-faint); +} + +.codex-panel__meta-grid dd { + margin: 0; + overflow-wrap: anywhere; + white-space: pre-wrap; +} + +.codex-panel__threads { + display: flex; + flex-direction: column; + gap: 1px; + min-width: 0; + max-width: 100%; + max-height: none; + overflow-x: hidden; + overflow-y: visible; +} + +.codex-panel__thread-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto; + gap: var(--codex-panel-panel-gap); + align-items: center; + min-width: 0; +} + +.codex-panel__thread-row--renaming { + grid-template-columns: minmax(0, 1fr); +} + +.codex-panel__thread-rename { + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto auto; + gap: var(--codex-panel-panel-gap); + align-items: center; + min-width: 0; +} + +.codex-panel__thread-rename-input { + width: 100%; + min-width: 0; + height: var(--nav-item-size, 28px); + margin: 0; + font-size: var(--codex-panel-toolbar-text-size); + line-height: var(--codex-panel-toolbar-line-height); +} + +.codex-panel__thread { + min-height: var(--nav-item-size, 28px); + margin: 0; + box-shadow: none; +} + +.codex-panel__thread-action { + flex: 0 0 auto; + visibility: hidden; + opacity: 0; +} + +.codex-panel__thread-row:hover .codex-panel__thread-action, +.codex-panel__thread-row:focus-within .codex-panel__thread-action, +.codex-panel__thread-action:focus-visible { + visibility: visible; + opacity: 1; +} + +.codex-panel__thread-action:disabled { + visibility: visible; + opacity: 0.4; +} + +.codex-panel__thread--empty { + color: var(--text-faint); + pointer-events: none; +} + +.codex-panel__messages { + height: 100%; + min-height: 0; + overflow-y: auto; + overflow-anchor: none; + padding: var(--codex-panel-message-padding-y) var(--codex-panel-edge-padding-x); + user-select: text; +} + +.codex-panel__history-bar { + display: flex; + justify-content: center; + margin-bottom: 10px; +} + +.codex-panel__message { + margin-bottom: 12px; + padding-left: var(--codex-panel-message-indent); + border-left: 2px solid transparent; + white-space: pre-wrap; + overflow-wrap: anywhere; + user-select: text; +} + +.codex-panel__message-role { + display: flex; + align-items: center; + gap: var(--codex-panel-item-gap); + margin-bottom: 4px; + color: var(--text-faint); + font-size: var(--font-ui-smaller); + font-weight: 600; +} + +.codex-panel__message--user { + border-left-color: var(--interactive-accent); +} + +.codex-panel__message--user .codex-panel__message-role { + color: var(--interactive-accent); +} + +.codex-panel__message--assistant { + border-left-color: var(--background-modifier-border); +} + +.codex-panel__message--system, +.codex-panel__message--tool { + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +.codex-panel__message--system { + border-left-color: var(--text-faint); +} + +.codex-panel__message--review-result { + border-left-color: var(--text-warning); +} + +.codex-panel__message--review-result .codex-panel__message-role { + color: var(--text-warning); +} + +.codex-panel__message--user-input-result { + border-left-color: var(--interactive-accent); +} + +.codex-panel__message--user-input-result .codex-panel__message-role { + color: var(--interactive-accent); +} + +.codex-panel__activity-group { + margin: 0 0 12px; + padding-left: var(--codex-panel-message-indent); + border-left: 2px solid var(--background-modifier-border); + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +.codex-panel__activity-group > summary { + cursor: pointer; + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +.codex-panel__activity-group > summary:hover { + color: var(--text-normal); +} + +.codex-panel__activity-group[open] { + padding-bottom: 2px; +} + +.codex-panel__activity-group > .codex-panel__message, +.codex-panel__activity-group > .codex-panel__work-message, +.codex-panel__activity-group > .codex-panel__reasoning { + margin-top: 8px; + margin-bottom: 8px; +} + +.codex-panel__message-content { + font-family: var(--codex-panel-content-font); + font-size: var(--codex-panel-content-font-size); + overflow-wrap: anywhere; + cursor: text; + user-select: text; +} + +.codex-panel__message-content.markdown-rendered { + white-space: normal; +} + +.codex-panel__message-content.markdown-rendered > :first-child { + margin-top: 0; +} + +.codex-panel__message-content.markdown-rendered > :last-child { + margin-bottom: 0; +} + +.codex-panel__message-content.markdown-rendered pre { + white-space: pre-wrap; +} + +.codex-panel__edited-files { + margin-top: 6px; + color: var(--text-muted); + font-size: var(--font-ui-smaller); +} + +.codex-panel__reasoning { + margin: 0 0 10px; + padding-left: var(--codex-panel-message-indent); + border-left: 2px solid var(--background-modifier-border); + color: var(--text-muted); + font-size: var(--font-ui-small); + white-space: pre-wrap; + overflow-wrap: anywhere; + cursor: text; + user-select: text; +} + +.codex-panel__reasoning-role { + margin-bottom: 3px; + color: var(--text-faint); + font-size: var(--font-ui-smaller); + font-weight: 600; +} + +.codex-panel__reasoning-content { + font-family: var(--codex-panel-content-font); + opacity: 0.92; +} + +.codex-panel__reasoning-dots span { + opacity: 0.35; +} + +.codex-panel__reasoning.is-active .codex-panel__reasoning-dots span { + animation: codex-panel-reasoning-dot 1.2s infinite ease-in-out; +} + +.codex-panel__reasoning.is-active .codex-panel__reasoning-dots span:nth-child(2) { + animation-delay: 0.18s; +} + +.codex-panel__reasoning.is-active .codex-panel__reasoning-dots span:nth-child(3) { + animation-delay: 0.36s; +} + +@keyframes codex-panel-reasoning-dot { + 0%, + 80%, + 100% { + opacity: 0.25; + } + + 40% { + opacity: 1; + } +} + +.codex-panel__wikilink { + cursor: pointer; +} + +.codex-panel__work-message { + margin-bottom: 8px; + padding-left: var(--codex-panel-message-indent); + border: 0; + border-left: 2px solid var(--background-modifier-border); + border-radius: 0; + background: transparent; +} + +.codex-panel__work-message .codex-panel__message-role, +.codex-panel__activity-group > summary { + font-size: var(--font-ui-smaller); + font-weight: 500; +} + +.codex-panel__work-message--warning { + border-left: 2px solid var(--text-warning); +} + +.codex-panel__work-message--warning > .codex-panel__message-role { + color: var(--text-warning); +} + +.codex-panel__pending-request-message { + display: flex; + flex-direction: column; + gap: var(--codex-panel-item-gap); + margin-bottom: 12px; + background: transparent; + font-family: var(--font-interface); +} + +.codex-panel__pending-request-card.setting-item { + flex-direction: column; + align-items: stretch; + gap: var(--codex-panel-control-gap); + padding: 0; + border: 0; + border-radius: 0; + background: transparent; +} + +.codex-panel__pending-request-card.setting-item + .codex-panel__pending-request-card.setting-item { + padding-top: var(--codex-panel-item-gap); + border-top: 1px solid var(--background-modifier-border-hover); +} + +.codex-panel__pending-request-info.setting-item-info { + min-width: 0; + margin-right: 0; +} + +.codex-panel__pending-request-title { + color: var(--text-normal); + font-size: var(--font-ui-small); + line-height: var(--line-height-tight); +} + +.codex-panel__approval-body { + color: var(--text-muted); + font-size: var(--font-ui-small); + line-height: var(--line-height-tight); + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +.codex-panel__approval-details { + margin-top: var(--codex-panel-control-gap); + color: var(--text-muted); + font-size: var(--font-ui-smaller); +} + +.codex-panel__pending-request-actions.setting-item-control { + justify-content: flex-start; + flex-wrap: wrap; + gap: var(--codex-panel-control-gap); +} + +.codex-panel__user-input.codex-panel__pending-request-card .codex-panel__pending-request-actions { + margin-top: var(--codex-panel-section-gap); +} + +.codex-panel__tool-item, +.codex-panel__file-change { + margin-bottom: 8px; + padding-left: var(--codex-panel-message-indent); + border: 0; + border-left: 2px solid var(--background-modifier-border); + border-radius: 0; + background: transparent; +} + +.codex-panel__tool-item .codex-panel__message-role, +.codex-panel__file-change .codex-panel__message-role, +.codex-panel__activity-group > summary { + font-size: var(--font-ui-smaller); + font-weight: 500; +} + +.codex-panel__execution--running { + border-left-color: var(--interactive-accent); +} + +.codex-panel__execution--completed { + border-left-color: var(--text-success); +} + +.codex-panel__execution--failed { + border-left-color: var(--text-error); +} + +.codex-panel__tool-item--hook:not(.codex-panel__execution) { + border-left-color: var(--text-accent); +} + +.codex-panel__tool-result-header { + display: flex; + align-items: center; + min-width: 0; + color: var(--text-muted); + cursor: pointer; +} + +.codex-panel__tool-result--plain .codex-panel__tool-result-header { + cursor: text; +} + +.codex-panel__tool-result-header .codex-panel__message-role { + margin-bottom: 0; +} + +.codex-panel__tool-result > .codex-panel__output, +.codex-panel__tool-result > .codex-panel__diff-file, +.codex-panel__tool-result > .codex-panel__meta-grid, +.codex-panel__tool-result-details > .codex-panel__output, +.codex-panel__tool-result-details > .codex-panel__diff-file, +.codex-panel__tool-result-details > .codex-panel__meta-grid { + margin-left: 0; +} + +.codex-panel__tool-summary { + white-space: pre-wrap; + overflow-wrap: anywhere; + color: var(--text-muted); + font-size: var(--font-ui-small); + cursor: text; + user-select: text; +} + +.codex-panel__tool-result > .codex-panel__tool-summary { + min-width: 0; + max-width: 100%; + margin-top: 3px; + overflow: hidden; + line-height: var(--line-height-tight); + text-overflow: ellipsis; + white-space: nowrap; +} + +.codex-panel__tool-result.is-open > .codex-panel__tool-summary { + display: none; +} + +.codex-panel__task-list, +.codex-panel__agent-list { + display: flex; + flex-direction: column; + gap: 3px; + margin: 4px 0 0; + padding: 0; + list-style: none; +} + +.codex-panel__task-step, +.codex-panel__agent-row { + display: grid; + grid-template-columns: max-content minmax(0, 1fr); + gap: var(--codex-panel-item-gap); + min-width: 0; + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +.codex-panel__task-marker, +.codex-panel__agent-thread { + color: var(--text-faint); + font-family: var(--font-monospace); + font-size: var(--font-ui-smaller); +} + +.codex-panel__task-step--completed .codex-panel__task-text { + color: var(--text-faint); +} + +.codex-panel__task-step--inProgress .codex-panel__task-marker, +.codex-panel__task-step--inProgress .codex-panel__task-text { + color: var(--text-accent); +} + +.codex-panel__agent-status { + min-width: 0; + overflow-wrap: anywhere; +} + +.codex-panel__agent-list--summary { + margin-top: 3px; +} + +.codex-panel__agent-row--more .codex-panel__agent-status { + color: var(--text-faint); +} + +.codex-panel__output, +.codex-panel__diff-file { + margin-top: 8px; +} + +.codex-panel__output-title { + line-height: var(--line-height-tight); +} + +.codex-panel__output pre, +.codex-panel__diff { + max-height: 420px; + overflow: auto; + margin: 6px 0 0; + padding: 8px; + border-radius: var(--radius-s); + background: var(--code-background, var(--background-secondary)); + color: var(--code-normal, var(--text-muted)); + font-family: var(--font-monospace); + font-size: var(--font-smaller); + cursor: text; + user-select: text; +} + +.codex-panel__output pre { + white-space: pre-wrap; +} + +.codex-panel__diff { + white-space: pre; +} + +.codex-panel__diff-line { + display: block; + min-height: 1.35em; +} + +.codex-panel__diff-line--added { + color: var(--text-success); + background: color-mix(in srgb, var(--text-success) 12%, transparent); +} + +.codex-panel__diff-line--removed { + color: var(--text-error); + background: color-mix(in srgb, var(--text-error) 12%, transparent); +} + +.codex-panel__diff-line--hunk { + color: var(--text-accent); +} + +.codex-panel__diff-line--context { + color: var(--text-muted); +} + +.codex-panel__user-input-question { + display: grid; + gap: var(--codex-panel-control-gap); + margin-top: var(--codex-panel-item-gap); +} + +.codex-panel__user-input-header { + color: var(--text-normal); + font-size: var(--font-ui-small); + font-weight: 600; +} + +.codex-panel__user-input-prompt { + color: var(--text-muted); + font-size: var(--font-ui-small); + line-height: var(--line-height-tight); +} + +.codex-panel__user-input-answer { + display: grid; + gap: var(--codex-panel-control-gap); + margin-top: calc(var(--codex-panel-section-gap) - var(--codex-panel-control-gap)); +} + +.codex-panel__user-input-option { + position: relative; + display: grid; + grid-template-columns: max-content minmax(0, 1fr); + gap: var(--codex-panel-control-gap) var(--codex-panel-item-gap); + align-items: start; + padding: 1px var(--codex-panel-control-gap); + border-radius: var(--radius-s); + color: var(--text-muted); + font-size: var(--font-ui-small); + line-height: var(--line-height-tight); + cursor: pointer; +} + +.codex-panel__user-input-radio { + grid-column: 1; + align-self: start; + margin: 2px 0 0; +} + +.codex-panel__user-input-radio:checked + .codex-panel__user-input-option-label { + color: var(--text-accent); +} + +.codex-panel__user-input-option-description { + grid-column: 2; + color: var(--text-muted); + font-size: var(--font-ui-smaller); +} + +.codex-panel__user-input-text { + width: 100%; + min-height: 26px; + min-width: 0; + font-size: var(--font-ui-small); +} + +.codex-panel__user-input-other-text { + grid-column: 2; +} + +.codex-panel__composer { + position: relative; + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: var(--codex-panel-section-gap); + padding: var(--codex-panel-composer-padding-y) var(--codex-panel-edge-padding-x) var(--codex-panel-composer-padding-bottom); + border-top: 1px solid var(--background-modifier-border); +} + +.codex-panel__input { + width: 100%; + min-height: var(--codex-panel-composer-default-height); + max-height: var(--codex-panel-composer-max-height); + overflow: hidden; + font-family: var(--codex-panel-content-font); + font-size: var(--codex-panel-content-font-size); + resize: none; +} + +.codex-panel__composer-actions { + height: 100%; + align-self: end; + display: flex; + flex-direction: column; + justify-content: flex-end; + gap: var(--codex-panel-control-gap); +} + +.codex-panel__composer-action { + box-sizing: border-box; + --icon-size: var(--codex-panel-top-icon-size); + --icon-stroke: var(--icon-s-stroke-width, 2px); + min-width: var(--codex-panel-composer-control-size); + width: auto; + height: auto; + padding: var(--size-2-2, 4px); + border-radius: var(--clickable-icon-radius, var(--radius-s)); +} + +.codex-panel__composer-action svg { + width: var(--icon-size); + height: var(--icon-size); + stroke-width: var(--icon-stroke); +} + +.codex-panel__composer-action.is-interrupt { + color: var(--icon-color-focused); +} + +.codex-panel__composer-action.is-steer { + color: var(--icon-color-active); +} + +.codex-panel__composer-action:disabled { + opacity: 0.45; +} + +.codex-panel__composer-suggestions.suggestion-container { + position: absolute; + right: var(--codex-panel-edge-padding-x); + bottom: calc(100% - 4px); + left: var(--codex-panel-edge-padding-x); + z-index: var(--layer-popover, 30); + display: flex; + flex-direction: column; + width: auto; + max-height: var(--codex-panel-composer-suggestions-max-height); + overflow-y: auto; + margin: 0; + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-s); + background: var(--background-primary); + box-shadow: var(--shadow-s); +} + +.codex-panel__composer-suggestion.suggestion-item { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: var(--codex-panel-panel-gap); + width: 100%; + line-height: 1.3; + text-align: left; +} + +.codex-panel__composer-suggestion.suggestion-item.is-selected { + background: var(--background-modifier-hover); +} + +.codex-panel__suggestion-label, +.codex-panel__suggestion-detail { + display: block; + min-width: 0; +} + +.codex-panel__suggestion-label { + color: var(--text-normal); + font-size: var(--font-ui-small); + font-weight: 600; +} + +.codex-panel__suggestion-detail { + color: var(--text-muted); + font-size: var(--font-ui-smaller); + line-height: var(--line-height-tight); +} + +.codex-panel__composer-suggestion.is-selected .codex-panel__suggestion-label { + color: var(--text-normal); +} + +@container (max-width: 280px) { + .codex-panel__toolbar-primary { + grid-template-columns: auto auto minmax(0, 1fr); + } + + .codex-panel__runtime-strip { + gap: 2px; + } + + .codex-panel__runtime-model { + min-width: 0; + max-width: 86px; + padding: 0 5px; + } + + .codex-panel__status-dot { + grid-column: 3; + } + + .codex-panel__meter-compact { + grid-column: 1 / -1; + justify-content: flex-start; + max-width: none; + min-width: 0; + height: 16px; + padding-left: calc(var(--codex-panel-icon-button-inline-size) * 2 + var(--codex-panel-control-gap) * 2); + } + + .codex-panel__meter-compact-label { + max-width: 56px; + } + + .codex-panel__meter-compact-bar { + flex-basis: 42px; + } + + .codex-panel__composer { + grid-template-columns: minmax(0, 1fr); + gap: var(--codex-panel-control-gap); + } + + .codex-panel__composer-actions { + height: auto; + flex-direction: row; + justify-content: flex-end; + } +} diff --git a/tests/app-server-client.test.ts b/tests/app-server-client.test.ts new file mode 100644 index 00000000..a4c14b2b --- /dev/null +++ b/tests/app-server-client.test.ts @@ -0,0 +1,417 @@ +import { describe, expect, it } from "vitest"; + +import { AppServerClient } from "../src/app-server/client"; +import type { AppServerTransport, AppServerTransportHandlers } from "../src/app-server/transport"; +import type { RpcOutboundMessage } from "../src/app-server/types"; +import type { InitializeResponse } from "../src/generated/app-server/InitializeResponse"; +import type { ServerRequest } from "../src/generated/app-server/ServerRequest"; + +class FakeTransport implements AppServerTransport { + readonly sent: RpcOutboundMessage[] = []; + running = false; + + constructor(private readonly handlers: AppServerTransportHandlers) {} + + start(): void { + this.running = true; + } + + send(message: RpcOutboundMessage): void { + this.sent.push(message); + } + + stop(): void { + this.running = false; + } + + isRunning(): boolean { + return this.running; + } + + emitLine(message: unknown): void { + this.handlers.onLine(JSON.stringify(message)); + } +} + +async function connectedClient(): Promise<{ client: AppServerClient; transport: FakeTransport }> { + let transport!: FakeTransport; + const client = new AppServerClient( + "/bin/codex", + "/vault", + { + onNotification: () => undefined, + onServerRequest: () => undefined, + onLog: () => undefined, + onExit: () => undefined, + }, + 500, + (handlers) => { + transport = new FakeTransport(handlers); + return transport; + }, + ); + const connecting = client.connect(); + transport.emitLine({ id: 1, result: { codexHome: "/tmp/codex" } satisfies Partial }); + await connecting; + return { client, transport }; +} + +describe("AppServerClient", () => { + it("routes responses, notifications, and server requests", async () => { + let transport: FakeTransport; + const getTransport = () => transport; + const notifications: string[] = []; + const serverRequests: ServerRequest[] = []; + const client = new AppServerClient( + "/bin/codex", + "/vault", + { + onNotification: (notification) => notifications.push(notification.method), + onServerRequest: (request) => serverRequests.push(request), + onLog: () => undefined, + onExit: () => undefined, + }, + 500, + (handlers) => { + transport = new FakeTransport(handlers); + return transport; + }, + ); + + const connecting = client.connect(); + expect(getTransport().sent[0]).toMatchObject({ id: 1, method: "initialize" }); + getTransport().emitLine({ id: 1, result: { codexHome: "/tmp/codex" } satisfies Partial }); + await expect(connecting).resolves.toMatchObject({ codexHome: "/tmp/codex" }); + expect(getTransport().sent[1]).toEqual({ method: "initialized" }); + + getTransport().emitLine({ method: "warning", params: { message: "careful" } }); + expect(notifications).toEqual(["warning"]); + + getTransport().emitLine({ + id: 99, + method: "item/commandExecution/requestApproval", + params: { + command: "npm run build", + cwd: "/vault", + threadId: "thread", + turnId: "turn", + itemId: "command", + startedAtMs: 1, + reason: null, + commandActions: [], + proposedExecpolicyAmendment: null, + proposedNetworkPolicyAmendments: [], + }, + }); + expect(serverRequests[0]?.method).toBe("item/commandExecution/requestApproval"); + }); + + it("sends typed turn steering requests", async () => { + let transport: FakeTransport; + const getTransport = () => transport; + const client = new AppServerClient( + "/bin/codex", + "/vault", + { + onNotification: () => undefined, + onServerRequest: () => undefined, + onLog: () => undefined, + onExit: () => undefined, + }, + 500, + (handlers) => { + transport = new FakeTransport(handlers); + return transport; + }, + ); + + const connecting = client.connect(); + getTransport().emitLine({ id: 1, result: { codexHome: "/tmp/codex" } satisfies Partial }); + await connecting; + + const steering = client.steerTurn("thread-1", "turn-1", "Please adjust course."); + expect(getTransport().sent[2]).toMatchObject({ + id: 2, + method: "turn/steer", + params: { + threadId: "thread-1", + expectedTurnId: "turn-1", + input: [{ type: "text", text: "Please adjust course.", text_elements: [] }], + }, + }); + getTransport().emitLine({ id: 2, result: { turnId: "turn-1" } }); + await expect(steering).resolves.toEqual({ turnId: "turn-1" }); + }); + + it("sends golden thread and turn request payloads", async () => { + const { client, transport } = await connectedClient(); + + const startingThread = client.startThread("/vault", "fast"); + expect(transport.sent[2]).toMatchObject({ + id: 2, + method: "thread/start", + params: { cwd: "/vault", serviceName: "codex-panel", serviceTier: "fast" }, + }); + transport.emitLine({ id: 2, result: { thread: { id: "thread-1", title: null }, serviceTier: "fast" } }); + await startingThread; + + const startingTurn = client.startTurn("thread-1", "/vault", "hello", null); + expect(transport.sent[3]).toMatchObject({ + id: 3, + method: "turn/start", + params: { + threadId: "thread-1", + cwd: "/vault", + serviceTier: null, + input: [{ type: "text", text: "hello", text_elements: [] }], + }, + }); + transport.emitLine({ id: 3, result: { turn: { id: "turn-1" } } }); + await startingTurn; + }); + + it("sends structured user input for turns and steering", async () => { + const { client, transport } = await connectedClient(); + const input = [ + { type: "text" as const, text: "Read [[Alpha]].", text_elements: [] }, + { type: "mention" as const, name: "Alpha", path: "thoughts/Alpha.md" }, + ]; + + const startingTurn = client.startTurn("thread-1", "/vault", input, null); + expect(transport.sent[2]).toMatchObject({ + method: "turn/start", + params: { + threadId: "thread-1", + cwd: "/vault", + input, + }, + }); + transport.emitLine({ id: 2, result: { turn: { id: "turn-1" } } }); + await startingTurn; + + const steering = client.steerTurn("thread-1", "turn-1", input); + expect(transport.sent[3]).toMatchObject({ + method: "turn/steer", + params: { + threadId: "thread-1", + expectedTurnId: "turn-1", + input, + }, + }); + transport.emitLine({ id: 3, result: { turnId: "turn-1" } }); + await steering; + }); + + it("sends explicit null service tier when fast mode is disabled", async () => { + const { client, transport } = await connectedClient(); + + const startingThread = client.startThread("/vault", null); + expect(transport.sent[2]).toMatchObject({ + id: 2, + method: "thread/start", + params: { cwd: "/vault", serviceTier: null }, + }); + transport.emitLine({ id: 2, result: { thread: { id: "thread-1", title: null }, serviceTier: null } }); + await startingThread; + }); + + it("sends collaboration mode only for Plan turns", async () => { + const { client, transport } = await connectedClient(); + + const defaultTurn = client.startTurn("thread-1", "/vault", "default", null, null); + expect(transport.sent[2]).toMatchObject({ + method: "turn/start", + params: { + threadId: "thread-1", + cwd: "/vault", + input: [{ type: "text", text: "default", text_elements: [] }], + }, + }); + expect((transport.sent[2] as { params?: Record }).params?.collaborationMode).toBeUndefined(); + transport.emitLine({ id: 2, result: { turn: { id: "turn-default" } } }); + await defaultTurn; + + const planTurn = client.startTurn("thread-1", "/vault", "plan", null, { + mode: "plan", + settings: { + model: "gpt-5.5", + reasoning_effort: "medium", + developer_instructions: null, + }, + }); + expect(transport.sent[3]).toMatchObject({ + method: "turn/start", + params: { + threadId: "thread-1", + cwd: "/vault", + input: [{ type: "text", text: "plan", text_elements: [] }], + collaborationMode: { + mode: "plan", + settings: { + model: "gpt-5.5", + reasoning_effort: "medium", + developer_instructions: null, + }, + }, + }, + }); + transport.emitLine({ id: 3, result: { turn: { id: "turn-plan" } } }); + await planTurn; + }); + + it("sends model and effort turn overrides when provided", async () => { + const { client, transport } = await connectedClient(); + + const startingTurn = client.startTurn("thread-1", "/vault", "override", null, null, "gpt-5.5", "high"); + expect(transport.sent[2]).toMatchObject({ + method: "turn/start", + params: { + threadId: "thread-1", + cwd: "/vault", + model: "gpt-5.5", + effort: "high", + input: [{ type: "text", text: "override", text_elements: [] }], + }, + }); + transport.emitLine({ id: 2, result: { turn: { id: "turn-override" } } }); + await startingTurn; + + const resetTurn = client.startTurn("thread-1", "/vault", "reset", null, null, null, null); + expect(transport.sent[3]).toMatchObject({ + method: "turn/start", + params: { + threadId: "thread-1", + cwd: "/vault", + model: null, + effort: null, + input: [{ type: "text", text: "reset", text_elements: [] }], + }, + }); + transport.emitLine({ id: 3, result: { turn: { id: "turn-reset" } } }); + await resetTurn; + }); + + it("sends model list request payloads", async () => { + const { client, transport } = await connectedClient(); + + const listing = client.listModels(); + expect(transport.sent[2]).toMatchObject({ + id: 2, + method: "model/list", + params: { includeHidden: false, limit: 100 }, + }); + transport.emitLine({ id: 2, result: { data: [], nextCursor: null } }); + await listing; + }); + + it("sends golden list and history request payloads", async () => { + const { client, transport } = await connectedClient(); + + const listing = client.listThreads("/vault", true); + expect(transport.sent[2]).toMatchObject({ + id: 2, + method: "thread/list", + params: { cwd: "/vault", limit: 20, archived: true, sortKey: "updated_at", sortDirection: "desc" }, + }); + transport.emitLine({ id: 2, result: { data: [], nextCursor: null } }); + await listing; + + const resuming = client.resumeThread("thread-1", "/vault"); + expect(transport.sent[3]).toMatchObject({ + id: 3, + method: "thread/resume", + params: { threadId: "thread-1", cwd: "/vault", excludeTurns: true, persistExtendedHistory: false }, + }); + transport.emitLine({ id: 3, result: { thread: { id: "thread-1", title: null }, cwd: "/vault" } }); + await resuming; + + const skills = client.listSkills("/vault"); + expect(transport.sent[4]).toMatchObject({ + id: 4, + method: "skills/list", + params: { cwds: ["/vault"], forceReload: false }, + }); + expect((transport.sent[4] as { params?: Record }).params?.perCwdExtraUserRoots).toBeUndefined(); + transport.emitLine({ id: 4, result: { data: [] } }); + await skills; + + const turns = client.threadTurnsList("thread-1", "cursor-1", 10); + expect(transport.sent[5]).toMatchObject({ + id: 5, + method: "thread/turns/list", + params: { threadId: "thread-1", cursor: "cursor-1", limit: 10, sortDirection: "desc", itemsView: "full" }, + }); + transport.emitLine({ id: 5, result: { data: [], nextCursor: null } }); + await turns; + + const firstTurn = client.threadTurnsList("thread-1", null, 1, "asc"); + expect(transport.sent[6]).toMatchObject({ + id: 6, + method: "thread/turns/list", + params: { threadId: "thread-1", cursor: null, limit: 1, sortDirection: "asc", itemsView: "full" }, + }); + transport.emitLine({ id: 6, result: { data: [], nextCursor: null } }); + await firstTurn; + }); + + it("sends thread naming request payloads", async () => { + const { client, transport } = await connectedClient(); + + const namingThread = client.startEphemeralThread("/vault", "naming", "Return a title."); + expect(transport.sent[2]).toMatchObject({ + id: 2, + method: "thread/start", + params: { + cwd: "/vault", + serviceName: "naming", + developerInstructions: "Return a title.", + ephemeral: true, + sandbox: "read-only", + approvalPolicy: "never", + environments: [], + }, + }); + transport.emitLine({ id: 2, result: { thread: { id: "naming-thread" } } }); + await namingThread; + + const structuredTurn = client.startStructuredTurn( + "naming-thread", + "/vault", + "title please", + { + type: "object", + properties: { title: { type: "string" } }, + required: ["title"], + }, + "gpt-5.4-mini", + "minimal", + ); + expect(transport.sent[3]).toMatchObject({ + id: 3, + method: "turn/start", + params: { + threadId: "naming-thread", + cwd: "/vault", + model: "gpt-5.4-mini", + effort: "minimal", + input: [{ type: "text", text: "title please", text_elements: [] }], + outputSchema: { + type: "object", + properties: { title: { type: "string" } }, + required: ["title"], + }, + }, + }); + transport.emitLine({ id: 3, result: { turn: { id: "naming-turn" } } }); + await structuredTurn; + + const setName = client.setThreadName("thread-1", "Codex Panel自動命名"); + expect(transport.sent[4]).toMatchObject({ + id: 4, + method: "thread/name/set", + params: { threadId: "thread-1", name: "Codex Panel自動命名" }, + }); + transport.emitLine({ id: 4, result: {} }); + await setName; + }); +}); diff --git a/tests/app-server-compatibility.test.ts b/tests/app-server-compatibility.test.ts new file mode 100644 index 00000000..ff8dcba1 --- /dev/null +++ b/tests/app-server-compatibility.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; + +import { appServerIdentity, appServerPlatform, compatibilitySummary, createAppServerCompatibility } from "../src/app-server/compatibility"; +import type { InitializeResponse } from "../src/generated/app-server/InitializeResponse"; + +describe("app-server compatibility", () => { + it("formats initialize metadata", () => { + const response = { + userAgent: "codex-cli/0.128.0", + codexHome: "/tmp/codex", + platformFamily: "unix", + platformOs: "macos", + } satisfies InitializeResponse; + + expect(appServerIdentity(response)).toBe("codex-cli/0.128.0"); + expect(appServerPlatform(response)).toBe("macos/unix"); + }); + + it("formats lightweight probe status", () => { + const compatibility = createAppServerCompatibility(); + expect(compatibilitySummary(compatibility)).toBe("model/list unknown; Plan mode uses experimental collaborationMode override"); + + compatibility.modelList = "failed"; + compatibility.modelListError = "Unsupported app-server request"; + expect(compatibilitySummary(compatibility)).toBe("model/list failed; Plan mode uses experimental collaborationMode override"); + }); +}); diff --git a/tests/app-server-logs.test.ts b/tests/app-server-logs.test.ts new file mode 100644 index 00000000..9c1e267b --- /dev/null +++ b/tests/app-server-logs.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; + +import { classifyAppServerLog } from "../src/panel/app-server-logs"; + +describe("app-server log classification", () => { + it("suppresses raw MCP token refresh stderr", () => { + expect(classifyAppServerLog('worker quit with fatal: Transport channel closed, when Auth(TokenRefreshFailed("bad"))')).toBeNull(); + }); + + it("suppresses non-JSON app-server stderr", () => { + expect( + classifyAppServerLog( + "\u001b[2m2026-05-08T01:27:27.101140Z\u001b[0m \u001b[31mERROR\u001b[0m \u001b[2mcodex_core::tools::router\u001b[0m\u001b[2m:\u001b[0m \u001b[3merror\u001b[0m\u001b[2m=\u001b[0mapply_patch verification failed", + ), + ).toBeNull(); + expect( + classifyAppServerLog( + "src/generated/app-server/: codex app-server generate-ts --out src/generated/app-serverで生成した型。手編集しない。", + ), + ).toBeNull(); + }); + + it("classifies JSON app-server errors", () => { + expect(classifyAppServerLog(JSON.stringify({ level: "ERROR", fields: { message: "boom" }, target: "codex" }))).toEqual({ + kind: "error", + text: "boom", + }); + }); + + it("suppresses structured apply_patch router verification logs", () => { + expect( + classifyAppServerLog( + JSON.stringify({ + level: "ERROR", + fields: { message: "apply_patch verification failed: Failed to find expected lines" }, + target: "codex_core::tools::router", + }), + ), + ).toBeNull(); + }); +}); diff --git a/tests/approvals.test.ts b/tests/approvals.test.ts new file mode 100644 index 00000000..c8210bbf --- /dev/null +++ b/tests/approvals.test.ts @@ -0,0 +1,102 @@ +import { describe, expect, it } from "vitest"; + +import { approvalResponse, approvalSummary, approvalTitle, toPendingApproval } from "../src/approvals/model"; +import type { ServerRequest } from "../src/generated/app-server/ServerRequest"; + +describe("approval model", () => { + it("classifies command approvals and builds v2 decisions", () => { + const request: ServerRequest = { + id: 1, + method: "item/commandExecution/requestApproval", + params: { + command: "npm run build", + cwd: "/tmp/project", + threadId: "thread", + turnId: "turn", + itemId: "item", + startedAtMs: 1, + reason: null, + commandActions: [], + proposedExecpolicyAmendment: null, + proposedNetworkPolicyAmendments: [], + }, + }; + const approval = toPendingApproval(request); + + expect(approval).not.toBeNull(); + expect(approvalTitle(approval!)).toBe("Command approval"); + expect(approvalSummary(approval!)).toBe("npm run build"); + expect(approvalResponse(approval!, "accept-session")).toEqual({ decision: "acceptForSession" }); + }); + + it("builds permission grants only for accept actions", () => { + const request: ServerRequest = { + id: 2, + method: "item/permissions/requestApproval", + params: { + cwd: "/tmp/project", + threadId: "thread", + turnId: "turn", + itemId: "item", + startedAtMs: 1, + reason: "Need network", + permissions: { network: { enabled: true }, fileSystem: null }, + }, + }; + const approval = toPendingApproval(request)!; + + expect(approvalResponse(approval, "decline")).toEqual({ permissions: {}, scope: "turn" }); + expect(approvalResponse(approval, "accept")).toEqual({ + permissions: { network: { enabled: true } }, + scope: "turn", + }); + }); + + it("builds action responses for current approval families", () => { + const requests: Array<{ request: ServerRequest; acceptSession: unknown; cancel: unknown }> = [ + { + request: { + id: 3, + method: "item/commandExecution/requestApproval", + params: { + command: "npm test", + cwd: "/tmp/project", + threadId: "thread", + turnId: "turn", + itemId: "command", + startedAtMs: 1, + reason: null, + commandActions: [], + proposedExecpolicyAmendment: null, + proposedNetworkPolicyAmendments: [], + }, + }, + acceptSession: { decision: "acceptForSession" }, + cancel: { decision: "cancel" }, + }, + { + request: { + id: 4, + method: "item/fileChange/requestApproval", + params: { + threadId: "thread", + turnId: "turn", + itemId: "file", + startedAtMs: 1, + reason: "write", + grantRoot: "/tmp/project", + }, + }, + acceptSession: { decision: "acceptForSession" }, + cancel: { decision: "cancel" }, + }, + ]; + + for (const { request, acceptSession, cancel } of requests) { + const approval = toPendingApproval(request)!; + expect(approvalResponse(approval, "accept-session")).toEqual(acceptSession); + expect(approvalResponse(approval, "cancel")).toEqual(cancel); + expect(approvalResponse(approval, "decline")).toEqual({ decision: "decline" }); + } + }); +}); diff --git a/tests/collaboration-mode.test.ts b/tests/collaboration-mode.test.ts new file mode 100644 index 00000000..f9d544d4 --- /dev/null +++ b/tests/collaboration-mode.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from "vitest"; + +import { + collaborationModeLabel, + collaborationModeToggleMessage, + defaultCollaborationMode, + nextCollaborationMode, + planCollaborationMode, +} from "../src/panel/collaboration-mode"; + +describe("collaboration mode", () => { + it("toggles between Default and Plan mode", () => { + expect(nextCollaborationMode("default")).toBe("plan"); + expect(nextCollaborationMode("plan")).toBe("default"); + expect(collaborationModeLabel("default")).toBe("Default"); + expect(collaborationModeLabel("plan")).toBe("Plan"); + }); + + it("formats slash command status messages", () => { + expect(collaborationModeToggleMessage("plan")).toBe("Plan mode on for subsequent turns."); + expect(collaborationModeToggleMessage("default")).toBe("Plan mode off for subsequent turns."); + }); + + it("builds the Plan collaboration mode payload", () => { + expect(planCollaborationMode("gpt-5.5", "high")).toEqual({ + mode: "plan", + settings: { + model: "gpt-5.5", + reasoning_effort: "high", + developer_instructions: null, + }, + }); + }); + + it("builds the Default collaboration mode payload", () => { + expect(defaultCollaborationMode("gpt-5.5", "high")).toEqual({ + mode: "default", + settings: { + model: "gpt-5.5", + reasoning_effort: "high", + developer_instructions: null, + }, + }); + }); +}); diff --git a/tests/composer-suggestions.test.ts b/tests/composer-suggestions.test.ts new file mode 100644 index 00000000..19e526a5 --- /dev/null +++ b/tests/composer-suggestions.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, it } from "vitest"; + +import { + activeComposerSuggestions, + applyComposerSuggestionInsertion, + composerSuggestionNavigationDirection, + findWikiLinkSuggestions, + nextComposerSuggestionIndex, + parseSlashCommand, +} from "../src/composer/suggestions"; + +describe("composer suggestions", () => { + const notes = [ + { basename: "Alpha", path: "thoughts/Alpha.md", mtime: 10 }, + { basename: "Alpha", path: "projects/Alpha.md", mtime: 20 }, + { basename: "Beta Note", path: "topics/Beta Note.md", mtime: 30 }, + ]; + + it("parses supported slash commands only", () => { + expect(parseSlashCommand("/status")).toEqual({ command: "status", args: "" }); + expect(parseSlashCommand("/doctor")).toEqual({ command: "doctor", args: "" }); + expect(parseSlashCommand("/fast now")).toEqual({ command: "fast", args: "now" }); + expect(parseSlashCommand("/plan")).toEqual({ command: "plan", args: "" }); + expect(parseSlashCommand("/plan OK、実装してください")).toEqual({ command: "plan", args: "OK、実装してください" }); + expect(parseSlashCommand("/model gpt-5.5")).toEqual({ command: "model", args: "gpt-5.5" }); + expect(parseSlashCommand("/effort high")).toEqual({ command: "effort", args: "high" }); + expect(parseSlashCommand("/unknown")).toBeNull(); + }); + + it("ranks wikilinks and disambiguates duplicate basenames", () => { + const suggestions = findWikiLinkSuggestions("alp", 0, notes); + expect(suggestions[0]).toMatchObject({ + display: "Alpha", + detail: "projects/Alpha.md", + replacement: "[[projects/Alpha]]", + }); + }); + + it("uses one active suggestion family at a time", () => { + expect(activeComposerSuggestions("[[bet", notes, [])[0]?.replacement).toBe("[[Beta Note]]"); + expect(activeComposerSuggestions("/pla", notes, [])[0]?.replacement).toBe("/plan"); + expect(activeComposerSuggestions("/eff", notes, [])[0]?.replacement).toBe("/effort"); + expect(activeComposerSuggestions("/sta", notes, [])[0]?.replacement).toBe("/status"); + expect(activeComposerSuggestions("/doc", notes, [])[0]?.replacement).toBe("/doctor"); + }); + + it("adds a trailing space for slash command and skill insertions only", () => { + const slash = activeComposerSuggestions("/sta", notes, [])[0]; + const skill = activeComposerSuggestions("$obs", notes, [ + { + name: "obsidian-dataview-read", + description: "Read Dataview results", + path: "/vault/___/skills/obsidian-dataview-read/SKILL.md", + scope: "local", + enabled: true, + } as never, + ])[0]; + const wikilink = activeComposerSuggestions("[[bet", notes, [])[0]; + + expect(slash).toMatchObject({ replacement: "/status", appendSpaceOnInsert: true }); + expect(skill).toMatchObject({ replacement: "$obsidian-dataview-read", appendSpaceOnInsert: true }); + expect(wikilink).toMatchObject({ replacement: "[[Beta Note]]" }); + expect(wikilink?.appendSpaceOnInsert).toBeUndefined(); + + expect(applyComposerSuggestionInsertion("/sta", 4, slash)).toEqual({ value: "/status ", cursor: 8 }); + expect(applyComposerSuggestionInsertion("/sta then", 4, slash)).toEqual({ value: "/status then", cursor: 7 }); + expect(applyComposerSuggestionInsertion("[[bet", 5, wikilink)).toEqual({ value: "[[Beta Note]]", cursor: 13 }); + }); + + it("maps arrow keys and Ctrl+n/p to suggestion navigation", () => { + expect(composerSuggestionNavigationDirection({ key: "ArrowDown", ctrlKey: false, metaKey: false, altKey: false })).toBe(1); + expect(composerSuggestionNavigationDirection({ key: "ArrowUp", ctrlKey: false, metaKey: false, altKey: false })).toBe(-1); + expect(composerSuggestionNavigationDirection({ key: "n", ctrlKey: true, metaKey: false, altKey: false })).toBe(1); + expect(composerSuggestionNavigationDirection({ key: "p", ctrlKey: true, metaKey: false, altKey: false })).toBe(-1); + expect(composerSuggestionNavigationDirection({ key: "Enter", ctrlKey: true, metaKey: false, altKey: false })).toBeNull(); + expect(nextComposerSuggestionIndex(0, 3, -1)).toBe(2); + expect(nextComposerSuggestionIndex(2, 3, 1)).toBe(0); + }); +}); diff --git a/tests/connection-manager.test.ts b/tests/connection-manager.test.ts new file mode 100644 index 00000000..4a947127 --- /dev/null +++ b/tests/connection-manager.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, it, vi } from "vitest"; + +import { AppServerClient } from "../src/app-server/client"; +import { ConnectionManager, StaleConnectionError } from "../src/app-server/connection-manager"; +import type { AppServerTransport, AppServerTransportHandlers } from "../src/app-server/transport"; +import type { RpcOutboundMessage } from "../src/app-server/types"; + +class SilentTransport implements AppServerTransport { + readonly sent: RpcOutboundMessage[] = []; + running = false; + + constructor(private readonly handlers: AppServerTransportHandlers) {} + + start(): void { + this.running = true; + } + + send(message: RpcOutboundMessage): void { + this.sent.push(message); + } + + stop(): void { + this.running = false; + } + + isRunning(): boolean { + return this.running; + } + + emitExit(): void { + this.running = false; + this.handlers.onExit(null, null); + } + + emitLine(message: unknown): void { + this.handlers.onLine(JSON.stringify(message)); + } +} + +describe("ConnectionManager", () => { + it("disconnects clients whose initialization fails", async () => { + let transport!: SilentTransport; + const manager = new ConnectionManager( + () => "/bin/codex", + "/vault", + { + onNotification: () => undefined, + onServerRequest: () => undefined, + onLog: () => undefined, + onExit: () => undefined, + }, + (codexPath, cwd, handlers) => + new AppServerClient(codexPath, cwd, handlers, 5, (transportHandlers) => { + transport = new SilentTransport(transportHandlers); + return transport; + }), + ); + + await expect(manager.connect()).rejects.toThrow("Codex app-server request timed out: initialize"); + + expect(transport.running).toBe(false); + expect(manager.currentClient()).toBeNull(); + }); + + it("marks initialization completed after disconnect as stale", async () => { + let transport!: SilentTransport; + const onExit = vi.fn(); + const manager = new ConnectionManager( + () => "/bin/codex", + "/vault", + { + onNotification: () => undefined, + onServerRequest: () => undefined, + onLog: () => undefined, + onExit, + }, + (codexPath, cwd, handlers) => + new AppServerClient(codexPath, cwd, handlers, 500, (transportHandlers) => { + transport = new SilentTransport(transportHandlers); + return transport; + }), + ); + + const connecting = manager.connect(); + manager.disconnect(); + transport.emitLine({ id: 1, result: { codexHome: "/tmp/codex" } }); + + await expect(connecting).rejects.toBeInstanceOf(StaleConnectionError); + expect(onExit).not.toHaveBeenCalled(); + expect(manager.currentClient()).toBeNull(); + }); +}); diff --git a/tests/display-model.test.ts b/tests/display-model.test.ts new file mode 100644 index 00000000..9140850a --- /dev/null +++ b/tests/display-model.test.ts @@ -0,0 +1,951 @@ +import { describe, expect, it } from "vitest"; + +import { + appendAssistantDelta, + activeAgentRunSummary, + appendItemOutput, + appendItemText, + appendPlanDelta, + appendToolOutput, + createAutoReviewResultItem, + createReviewResultItem, + displayBlocksForItems, + displayItemFromThreadItem, + displayItemsFromTurns, + executionState, + normalizeProposedPlanMarkdown, + planProgressDisplayItem, + upsertDisplayItem, +} from "../src/display/model"; +import type { DisplayItem } from "../src/display/types"; +import type { ThreadItem } from "../src/generated/app-server/v2/ThreadItem"; +import type { Turn } from "../src/generated/app-server/v2/Turn"; + +function commandItem(id: string, text: string, turnId: string): DisplayItem { + return { id, kind: "command", role: "tool", text, turnId, command: text, cwd: "/vault", status: "completed" }; +} + +function fileChangeItem(id: string, turnId: string, path = "src/main.ts"): DisplayItem { + return { + id, + kind: "fileChange", + role: "tool", + text: "File change completed", + turnId, + status: "completed", + changes: [{ kind: "update", path, diff: "" }], + }; +} + +describe("display model", () => { + it("sorts turns oldest first and converts messages", () => { + const userMessage: ThreadItem = { type: "userMessage", id: "u1", content: [{ type: "text", text: "hello", text_elements: [] }] }; + const assistantMessage: ThreadItem = { type: "agentMessage", id: "a1", text: "world", phase: null, memoryCitation: null }; + const turns: Turn[] = [ + { + id: "new", + items: [assistantMessage], + itemsView: "full", + status: "completed", + startedAt: 2, + completedAt: 3, + durationMs: 1, + error: null, + }, + { id: "old", items: [userMessage], itemsView: "full", status: "completed", startedAt: 1, completedAt: 2, durationMs: 1, error: null }, + ]; + + expect(displayItemsFromTurns(turns).map((item) => item.text)).toEqual(["hello", "world"]); + expect(displayItemFromThreadItem(userMessage)).toMatchObject({ role: "user", markdown: true }); + }); + + it("preserves reasoning text", () => { + const item: ThreadItem = { type: "reasoning", id: "r1", summary: ["summary"], content: ["detail"] }; + expect(displayItemFromThreadItem(item)?.text).toBe("summary\n\ndetail"); + }); + + it("keeps empty reasoning text empty so completed placeholders can be hidden", () => { + const item: ThreadItem = { type: "reasoning", id: "r1", summary: [], content: [] }; + expect(displayItemFromThreadItem(item)?.text).toBe(""); + }); + + it("renders completed plan items as assistant markdown", () => { + const item: ThreadItem = { type: "plan", id: "p1", text: "\n# Plan\n" }; + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + id: "p1", + kind: "message", + role: "assistant", + text: "# Plan", + markdown: true, + turnId: "t1", + }); + }); + + it("normalizes proposed plan wrappers before markdown rendering", () => { + expect(normalizeProposedPlanMarkdown("\n## Summary\n- Ship it\n")).toBe("## Summary\n- Ship it"); + }); + + it("streams plan deltas as plain assistant text until completion", () => { + const items = appendPlanDelta([], "p1", "t1", "\n# Plan"); + const updated = appendPlanDelta(items, "p1", "t1", "\n"); + expect(updated).toMatchObject([{ id: "p1", kind: "message", role: "assistant", text: "# Plan", markdown: false }]); + }); + + it("formats structured plan progress as task progress", () => { + expect( + planProgressDisplayItem("t1", "Working plan", [ + { step: "Inspect code", status: "completed" }, + { step: "Patch UI", status: "inProgress" }, + { step: "Run tests", status: "pending" }, + ]), + ).toMatchObject({ + id: "plan-progress-t1", + kind: "taskProgress", + role: "tool", + text: "Working plan\n[x] Inspect code\n[>] Patch UI\n[ ] Run tests", + explanation: "Working plan", + steps: [ + { step: "Inspect code", status: "completed" }, + { step: "Patch UI", status: "inProgress" }, + { step: "Run tests", status: "pending" }, + ], + status: "inProgress", + }); + }); + + it("formats collab agent tool calls as agent activity", () => { + const item: ThreadItem = { + type: "collabAgentToolCall", + id: "agent-1", + tool: "spawnAgent", + status: "completed", + senderThreadId: "parent-thread", + receiverThreadIds: ["child-thread"], + prompt: "Inspect the renderer.", + model: "gpt-5.5", + reasoningEffort: "high", + agentsStates: { "child-thread": { status: "completed", message: "Done" } }, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + id: "agent-1", + kind: "agent", + role: "tool", + tool: "spawnAgent", + status: "completed", + senderThreadId: "parent-thread", + receiverThreadIds: ["child-thread"], + prompt: "Inspect the renderer.", + model: "gpt-5.5", + reasoningEffort: "high", + agents: [{ threadId: "child-thread", status: "completed", message: "Done" }], + state: "completed", + }); + }); + + it("keeps spawned agents running until child state completes", () => { + const item: ThreadItem = { + type: "collabAgentToolCall", + id: "agent-1", + tool: "spawnAgent", + status: "completed", + senderThreadId: "parent-thread", + receiverThreadIds: ["child-thread"], + prompt: "Inspect the renderer.", + model: null, + reasoningEffort: null, + agentsStates: {}, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "agent", + status: "completed", + state: "running", + }); + }); + + it("summarizes command items without exposing output inline", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "npm run check", + cwd: "/vault", + processId: null, + source: "agent", + status: "failed", + commandActions: [{ type: "unknown", command: "npm" }], + aggregatedOutput: "stderr with many details", + exitCode: 1, + durationMs: 42, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + text: "npm run check (exit 1)", + output: "stderr with many details", + state: "failed", + }); + }); + + it("labels parsed read commands separately from generic commands", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "sed -n '1,20p' src/main.ts", + cwd: "/vault", + processId: null, + source: "agent", + status: "completed", + commandActions: [{ type: "read", command: "sed", name: "main.ts", path: "/vault/src/main.ts" }], + aggregatedOutput: "file contents", + exitCode: 0, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + actionLabel: "read", + text: "src/main.ts", + state: "completed", + }); + }); + + it("summarizes piped parsed read commands by file name only", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "nl -ba src/main.ts | sed -n '1,20p'", + cwd: "/vault", + processId: null, + source: "agent", + status: "completed", + commandActions: [{ type: "read", command: "nl", name: "main.ts", path: "/vault/src/main.ts" }], + aggregatedOutput: "numbered file contents", + exitCode: 0, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + actionLabel: "read", + text: "src/main.ts", + command: "nl -ba src/main.ts | sed -n '1,20p'", + cwd: "/vault", + state: "completed", + }); + }); + + it("keeps absolute read paths when they are outside the command cwd", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "sed -n '1,20p' /vault/src/main.ts", + cwd: "/vault/tests", + processId: null, + source: "agent", + status: "completed", + commandActions: [{ type: "read", command: "sed", name: "main.ts", path: "/vault/src/main.ts" }], + aggregatedOutput: "file contents", + exitCode: 0, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + actionLabel: "read", + text: "/vault/src/main.ts", + state: "completed", + }); + }); + + it("summarizes zsh login wrapper commands without changing their command classification", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "/bin/zsh -lc 'npm run check'", + cwd: "/vault", + processId: null, + source: "agent", + status: "completed", + commandActions: [{ type: "unknown", command: "zsh" }], + aggregatedOutput: "check output", + exitCode: 0, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + actionLabel: "command", + text: "npm run check", + command: "/bin/zsh -lc 'npm run check'", + cwd: "/vault", + state: "completed", + }); + }); + + it("labels parsed search commands and summarizes their query and path", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "rg 'command target' src/display", + cwd: "/vault", + processId: null, + source: "agent", + status: "completed", + commandActions: [{ type: "search", command: "rg", query: "command target", path: "src/display" }], + aggregatedOutput: "search results", + exitCode: 0, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + actionLabel: "search", + text: '"command target" in src/display', + command: "rg 'command target' src/display", + cwd: "/vault", + state: "completed", + }); + }); + + it("labels parsed file listing commands and summarizes their path", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "rg --files src/display", + cwd: "/vault", + processId: null, + source: "agent", + status: "completed", + commandActions: [{ type: "listFiles", command: "rg", path: "src/display" }], + aggregatedOutput: "src/display/model.ts", + exitCode: 0, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + actionLabel: "list files", + text: "src/display", + state: "completed", + }); + }); + + it("uses the representative command action for both label and summary", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "cd src && rg target", + cwd: "/vault", + processId: null, + source: "agent", + status: "completed", + commandActions: [ + { type: "unknown", command: "cd" }, + { type: "search", command: "rg", query: "target", path: "src" }, + ], + aggregatedOutput: "search results", + exitCode: 0, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + actionLabel: "search", + text: "target in src", + state: "completed", + }); + }); + + it("summarizes parsed workspace file listings without a path", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "rg --files", + cwd: "/vault", + processId: null, + source: "agent", + status: "completed", + commandActions: [{ type: "listFiles", command: "rg", path: null }], + aggregatedOutput: "README.md", + exitCode: 0, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + actionLabel: "list files", + text: "workspace", + state: "completed", + }); + }); + + it("does not infer failed command summaries from stdout or stderr text", () => { + const item: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "rg error src", + cwd: "/vault", + processId: null, + source: "agent", + status: "completed", + commandActions: [{ type: "unknown", command: "rg" }], + aggregatedOutput: "error appears as search result text", + exitCode: 0, + durationMs: null, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "command", + text: "rg error src", + state: "completed", + }); + }); + + it("omits running qualifiers from command, file change, and tool summaries", () => { + const command: ThreadItem = { + type: "commandExecution", + id: "cmd-1", + command: "npm run check", + cwd: "/vault", + processId: null, + source: "agent", + status: "inProgress", + commandActions: [{ type: "unknown", command: "npm" }], + aggregatedOutput: null, + exitCode: null, + durationMs: null, + }; + const fileChange: ThreadItem = { + type: "fileChange", + id: "patch-1", + status: "inProgress", + changes: [{ kind: { type: "update", move_path: null }, path: "src/main.ts", diff: "@@\n-old\n+new" }], + }; + const tool: ThreadItem = { + type: "mcpToolCall", + id: "mcp-1", + server: "github", + tool: "pull_request_read", + status: "inProgress", + arguments: { id: 123 }, + result: null, + error: null, + durationMs: null, + }; + + expect(displayItemFromThreadItem(command, "t1")).toMatchObject({ + text: "npm run check", + state: "running", + }); + expect(displayItemFromThreadItem(fileChange, "t1")).toMatchObject({ + text: "src/main.ts", + state: "running", + }); + expect(displayItemFromThreadItem(tool, "t1")).toMatchObject({ + text: "123", + toolLabel: "github.pull_request_read", + state: "running", + }); + }); + + it("summarizes MCP tool calls from structured arguments and errors", () => { + const item: ThreadItem = { + type: "mcpToolCall", + id: "mcp-1", + server: "github", + tool: "pull_request_read", + status: "failed", + arguments: { owner: "org", repo: "project", id: 123 }, + result: null, + error: { message: "Not found" }, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "tool", + text: "123 (Not found)", + toolLabel: "github.pull_request_read", + details: [ + { title: "Arguments JSON", body: expect.stringContaining('"id": 123') }, + { title: "Error JSON", body: expect.stringContaining("Not found") }, + ], + state: "failed", + }); + }); + + it("summarizes dynamic tool calls from structured arguments only", () => { + const item: ThreadItem = { + type: "dynamicToolCall", + id: "tool-1", + namespace: "web", + tool: "open", + arguments: { url: "https://example.com" }, + status: "completed", + contentItems: [{ type: "inputText", text: "ok" }], + success: true, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "tool", + text: "https://example.com", + toolLabel: "web.open", + details: [ + { title: "Arguments JSON", body: expect.stringContaining("https://example.com") }, + { title: "Result JSON", body: expect.stringContaining("ok") }, + ], + state: "completed", + }); + }); + + it("uses details as the summary when a tool target cannot be extracted", () => { + const item: ThreadItem = { + type: "dynamicToolCall", + id: "tool-1", + namespace: "multi_tool_use", + tool: "parallel", + arguments: {}, + status: "completed", + contentItems: null, + success: true, + durationMs: 10, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "tool", + text: "details", + toolLabel: "multi_tool_use.parallel", + state: "completed", + }); + }); + + it("summarizes web search actions by action type and target", () => { + const item: ThreadItem = { + type: "webSearch", + id: "search-1", + query: "fallback query", + action: { type: "search", query: "codex app-server", queries: ["obsidian codex panel"] }, + }; + + expect(displayItemFromThreadItem(item, "t1")).toMatchObject({ + kind: "tool", + text: "search: codex app-server; obsidian codex panel; fallback query", + toolLabel: "web search", + details: [ + { + title: "web search", + rows: [ + { key: "action", value: "search" }, + { key: "query", value: "codex app-server; obsidian codex panel; fallback query" }, + ], + }, + ], + }); + }); + + it("structures automatic approval review summary messages", () => { + expect( + createReviewResultItem( + "Automatic approval review approved (risk: low, authorization: unknown): Auto-review returned a low-risk allow decision.", + ), + ).toMatchObject({ + kind: "reviewResult", + text: "Auto-review approved: Auto-review returned a low-risk allow decision.", + state: "completed", + details: [ + { + title: "Review", + rows: [ + { key: "status", value: "approved" }, + { key: "risk", value: "low" }, + { key: "authorization", value: "unknown" }, + { key: "message", value: "Auto-review returned a low-risk allow decision." }, + ], + }, + ], + }); + }); + + it("structures automatic approval review actions as metadata rows", () => { + expect( + createAutoReviewResultItem({ + threadId: "thread", + turnId: "turn", + startedAtMs: 1, + completedAtMs: 2, + reviewId: "review-1", + targetItemId: "patch-1", + decisionSource: "agent", + review: { status: "approved", riskLevel: "low", userAuthorization: "medium", rationale: "Allowed by policy." }, + action: { type: "applyPatch", cwd: "/vault", files: ["/vault/src/display/model.ts", "/vault/tests/display-model.test.ts"] }, + }), + ).toMatchObject({ + kind: "reviewResult", + text: "Auto-review approved: apply patch (2 files)", + details: [ + { + title: "Review", + rows: expect.arrayContaining([ + { key: "action", value: "apply patch" }, + { key: "cwd", value: "/vault" }, + { key: "files", value: "/vault/src/display/model.ts\n/vault/tests/display-model.test.ts" }, + ]), + }, + ], + }); + }); + + it("upserts assistant deltas by item id, not by last position", () => { + const items: DisplayItem[] = [ + { id: "a1", itemId: "a1", kind: "message", role: "assistant", text: "hello" }, + { id: "tool1", itemId: "tool1", kind: "tool", role: "tool", text: "tool" }, + ]; + + const updated = appendAssistantDelta(items, "a1", "t1", " world"); + expect(updated[0].text).toBe("hello world"); + expect(updated).toHaveLength(2); + expect(items[0].text).toBe("hello"); + expect(updated).not.toBe(items); + }); + + it("appends tool text and output without mutating existing display items", () => { + const tool: DisplayItem = { id: "tool1", itemId: "tool1", kind: "tool", role: "tool", text: "plan: " }; + const command: DisplayItem = { + id: "cmd1", + itemId: "cmd1", + kind: "command", + role: "tool", + text: "Command running", + command: "npm test", + cwd: "/vault", + status: "running", + output: "one", + }; + + const withText = appendItemText([tool], "tool1", "t1", "plan", "two"); + const withOutput = appendItemOutput([command], "cmd1", "t1", "two", "command", "Command running"); + const withToolOutput = appendToolOutput([tool], "tool1", "t1", "progress", "mcp progress"); + + expect(withText[0]).toMatchObject({ text: "plan: two" }); + expect(withOutput[0]).toMatchObject({ output: "onetwo" }); + expect(withToolOutput[0]).toMatchObject({ text: "plan: ", output: "progress" }); + expect(tool.text).toBe("plan: "); + expect(command.output).toBe("one"); + }); + + it("groups completed turn activities before the final assistant message", () => { + const items: DisplayItem[] = [ + { id: "u1", kind: "message", role: "user", text: "do it", turnId: "t1" }, + { id: "r1", kind: "reasoning", role: "tool", text: "thinking", turnId: "t1" }, + commandItem("c1", "npm test", "t1"), + { id: "a1", kind: "message", role: "assistant", text: "done", turnId: "t1" }, + ]; + + const blocks = displayBlocksForItems(items, null); + expect(blocks.map((block) => block.type)).toEqual(["item", "activityGroup", "item"]); + expect(blocks[1]).toMatchObject({ summary: "Work details: command, thought" }); + }); + + it("hides empty completed reasoning items", () => { + const items: DisplayItem[] = [ + { id: "u1", kind: "message", role: "user", text: "do it", turnId: "t1" }, + { id: "r1", kind: "reasoning", role: "tool", text: "", turnId: "t1", status: "completed" }, + { id: "a1", kind: "message", role: "assistant", text: "done", turnId: "t1" }, + ]; + + const blocks = displayBlocksForItems(items, null); + expect(blocks.map((block) => (block.type === "item" ? block.item.id : block.id))).toEqual(["u1", "a1"]); + }); + + it("collapses earlier assistant responses with their turn details", () => { + const items: DisplayItem[] = [ + { id: "u1", kind: "message", role: "user", text: "do it", turnId: "t1" }, + { id: "a1", kind: "message", role: "assistant", text: "I'll inspect it.", turnId: "t1" }, + commandItem("c1", "rg issue", "t1"), + { id: "a2", kind: "message", role: "assistant", text: "I found the cause.", turnId: "t1" }, + fileChangeItem("f1", "t1"), + { id: "a3", kind: "message", role: "assistant", text: "done", turnId: "t1" }, + ]; + + const blocks = displayBlocksForItems(items, null); + expect(blocks.map((block) => block.type)).toEqual(["item", "activityGroup", "item"]); + expect(blocks[1]).toMatchObject({ + summary: "Work details: 2 responses, command, file change", + items: [{ id: "a1" }, { id: "c1" }, { id: "a2" }, { id: "f1" }], + }); + expect(blocks[2]).toMatchObject({ item: { id: "a3" } }); + }); + + it("keeps active turn activities expanded", () => { + const items: DisplayItem[] = [ + { id: "r1", kind: "reasoning", role: "tool", text: "thinking", turnId: "t1" }, + { id: "a1", kind: "message", role: "assistant", text: "done", turnId: "t1" }, + ]; + + expect(displayBlocksForItems(items, "t1").map((block) => block.type)).toEqual(["item", "item"]); + }); + + it("moves active task progress to the end of the live turn", () => { + const items: DisplayItem[] = [ + { id: "u1", kind: "message", role: "user", text: "do it", turnId: "t1" }, + { + id: "plan-progress-t1", + kind: "taskProgress", + role: "tool", + text: "[>] Patch UI", + turnId: "t1", + explanation: null, + steps: [{ step: "Patch UI", status: "inProgress" }], + status: "inProgress", + }, + { id: "a1", kind: "message", role: "assistant", text: "working", turnId: "t1" }, + ]; + + const blocks = displayBlocksForItems(items, "t1"); + + expect(blocks.map((block) => (block.type === "item" ? block.item.id : block.id))).toEqual(["u1", "a1", "plan-progress-t1"]); + }); + + it("summarizes task progress and agent activity separately from tools", () => { + const items: DisplayItem[] = [ + { id: "u1", kind: "message", role: "user", text: "do it", turnId: "t1" }, + { + id: "plan-progress-t1", + kind: "taskProgress", + role: "tool", + text: "[>] Patch UI", + turnId: "t1", + explanation: null, + steps: [{ step: "Patch UI", status: "inProgress" }], + status: "inProgress", + }, + { + id: "agent-1", + kind: "agent", + role: "tool", + text: "Spawn agent", + turnId: "t1", + tool: "spawnAgent", + status: "completed", + senderThreadId: "parent", + receiverThreadIds: ["child"], + prompt: null, + model: null, + reasoningEffort: null, + agents: [{ threadId: "child", status: "completed", message: null }], + }, + { id: "a1", kind: "message", role: "assistant", text: "done", turnId: "t1" }, + ]; + + const blocks = displayBlocksForItems(items, null); + expect(blocks[1]).toMatchObject({ summary: "Work details: task progress, agent" }); + }); + + it("summarizes active subagent states while a turn is running", () => { + const items: DisplayItem[] = [ + { + id: "agent-1", + kind: "agent", + role: "tool", + text: "Wait for agent", + turnId: "t1", + tool: "wait", + status: "running", + senderThreadId: "parent", + receiverThreadIds: ["done", "running", "failed"], + prompt: null, + model: null, + reasoningEffort: null, + agents: [ + { threadId: "done", status: "completed", message: null }, + { threadId: "running", status: "running", message: null }, + { threadId: "failed", status: "errored", message: null }, + ], + }, + ]; + + expect(activeAgentRunSummary(items, "t1", true)).toEqual({ + running: 1, + completed: 1, + failed: 1, + agents: [ + { threadId: "failed", status: "errored", messagePreview: null }, + { threadId: "running", status: "running", messagePreview: null }, + { threadId: "done", status: "completed", messagePreview: null }, + ], + additionalAgents: 0, + }); + expect(activeAgentRunSummary(items, "t1", false)).toBeNull(); + }); + + it("summarizes active subagent previews and fallback receiver states", () => { + const items: DisplayItem[] = [ + { + id: "agent-1", + kind: "agent", + role: "tool", + text: "Spawn agent", + turnId: "t1", + tool: "spawnAgent", + status: "inProgress", + senderThreadId: "parent", + receiverThreadIds: ["fallback-child"], + prompt: null, + model: null, + reasoningEffort: null, + agents: [], + }, + { + id: "agent-2", + kind: "agent", + role: "tool", + text: "Wait for agent", + turnId: "t1", + tool: "wait", + status: "running", + senderThreadId: "parent", + receiverThreadIds: ["a", "b", "c"], + prompt: null, + model: null, + reasoningEffort: null, + agents: [ + { threadId: "a", status: "running", message: "\n Inspecting renderer tests \nmore details" }, + { threadId: "b", status: "failed", message: "Could not reproduce" }, + { threadId: "c", status: "running", message: null }, + ], + }, + ]; + + expect(activeAgentRunSummary(items, "t1", true)).toMatchObject({ + running: 3, + completed: 0, + failed: 1, + agents: [ + { threadId: "b", status: "failed", messagePreview: "Could not reproduce" }, + { threadId: "a", status: "running", messagePreview: "Inspecting renderer tests" }, + { threadId: "c", status: "running", messagePreview: null }, + ], + additionalAgents: 1, + }); + }); + + it("omits active subagent summaries once every subagent is complete", () => { + const items: DisplayItem[] = [ + { + id: "agent-1", + kind: "agent", + role: "tool", + text: "Wait for agent", + turnId: "t1", + tool: "wait", + status: "completed", + senderThreadId: "parent", + receiverThreadIds: ["done"], + prompt: null, + model: null, + reasoningEffort: null, + agents: [{ threadId: "done", status: "completed", message: null }], + }, + ]; + + expect(activeAgentRunSummary(items, "t1", true)).toBeNull(); + }); + + it("adds edited files to the final assistant message", () => { + const items: DisplayItem[] = [ + fileChangeItem("f1", "t1", "src/main.ts"), + fileChangeItem("f2", "t1", "styles.css"), + { id: "a1", kind: "message", role: "assistant", text: "intermediate", turnId: "t1" }, + { id: "a2", kind: "message", role: "assistant", text: "done", turnId: "t1" }, + ]; + + const assistantBlock = displayBlocksForItems(items, null).find((block) => block.type === "item" && block.item.role === "assistant"); + expect(assistantBlock).toMatchObject({ item: { editedFiles: ["src/main.ts", "styles.css"] } }); + }); + + it("shows edited files relative to the workspace root", () => { + const items: DisplayItem[] = [ + fileChangeItem("f1", "t1", "/vault/project/src/main.ts"), + fileChangeItem("f2", "t1", "/vault/project/styles.css"), + fileChangeItem("f3", "t1", "/tmp/outside.txt"), + { id: "a1", kind: "message", role: "assistant", text: "done", turnId: "t1" }, + ]; + + const assistantBlock = displayBlocksForItems(items, null, "/vault/project").find( + (block) => block.type === "item" && block.item.role === "assistant", + ); + expect(assistantBlock).toMatchObject({ item: { editedFiles: ["/tmp/outside.txt", "src/main.ts", "styles.css"] } }); + }); + + it("detects failed command state", () => { + expect( + executionState({ + id: "c1", + kind: "command", + role: "tool", + text: "Command", + command: "npm test", + cwd: "/vault", + status: "failed", + exitCode: 1, + }), + ).toBe("failed"); + }); + + it("does not infer command failure from the command text", () => { + expect( + executionState({ + id: "c1", + kind: "command", + role: "tool", + text: "rg error src", + command: "rg error src", + cwd: "/vault", + status: "completed", + exitCode: 0, + }), + ).toBe("completed"); + }); + + it("uses structured command status before command text", () => { + expect( + executionState({ + id: "c1", + kind: "command", + role: "tool", + text: "rg error src", + command: "rg error src", + cwd: "/vault", + status: "running", + }), + ).toBe("running"); + }); + + it("does not overwrite streamed output with an empty completed item", () => { + const streamed: DisplayItem = { + id: "c1", + itemId: "c1", + kind: "command", + role: "tool", + text: "Command running", + command: "npm test", + cwd: "/vault", + status: "running", + output: "partial output", + }; + const completed: DisplayItem = { + id: "c1", + itemId: "c1", + kind: "command", + role: "tool", + text: "npm test", + command: "npm test", + cwd: "/vault", + status: "completed", + output: "", + }; + + expect(upsertDisplayItem([streamed], completed)[0]).toMatchObject({ output: "partial output", status: "completed" }); + }); +}); diff --git a/tests/mocks/obsidian.ts b/tests/mocks/obsidian.ts new file mode 100644 index 00000000..1b541053 --- /dev/null +++ b/tests/mocks/obsidian.ts @@ -0,0 +1,278 @@ +type ElementOptions = { + cls?: string | string[]; + text?: string; + attr?: Record; +}; + +declare global { + interface HTMLElement { + addClass(className: string): void; + createDiv(options?: ElementOptions): HTMLDivElement; + createEl(tag: K, options?: ElementOptions): HTMLElementTagNameMap[K]; + createSpan(options?: ElementOptions): HTMLSpanElement; + empty(): void; + setAttr(name: string, value: string): void; + } +} + +export type App = Record; + +export const notices: string[] = []; + +export class FileSystemAdapter { + constructor(readonly basePath: string) {} + + getBasePath(): string { + return this.basePath; + } +} + +export class Notice { + constructor(message: string) { + notices.push(message); + } +} + +export class PluginSettingTab { + containerEl: HTMLElement; + + constructor( + readonly app: App, + readonly plugin: unknown, + ) { + ensureElementHelpers(); + this.containerEl = document.createElement("div"); + } + + display(): void { + // Test mock placeholder. + } +} + +export class Setting { + readonly settingEl: HTMLDivElement; + readonly infoEl: HTMLDivElement; + readonly controlEl: HTMLDivElement; + descEl: HTMLDivElement; + private nameEl: HTMLDivElement | null = null; + + constructor(containerEl: HTMLElement) { + ensureElementHelpers(); + this.settingEl = containerEl.createDiv({ cls: "setting-item" }); + this.infoEl = this.settingEl.createDiv({ cls: "setting-item-info" }); + this.descEl = this.infoEl.createDiv({ cls: "setting-item-description" }); + this.controlEl = this.settingEl.createDiv({ cls: "setting-item-control" }); + } + + setName(name: string): this { + this.nameEl?.remove(); + this.nameEl = this.infoEl.createDiv({ cls: "setting-item-name", text: name }); + return this; + } + + setDesc(desc: string): this { + this.descEl.empty(); + this.descEl.textContent = desc; + return this; + } + + setClass(cls: string): this { + this.settingEl.addClass(cls); + return this; + } + + setHeading(): this { + this.settingEl.addClass("setting-item-heading"); + return this; + } + + addButton(callback: (button: ButtonComponent) => void): this { + callback(new ButtonComponent(this.controlEl)); + return this; + } + + addExtraButton(callback: (button: ExtraButtonComponent) => void): this { + callback(new ExtraButtonComponent(this.controlEl)); + return this; + } + + addDropdown(callback: (dropdown: DropdownComponent) => void): this { + callback(new DropdownComponent(this.controlEl)); + return this; + } + + addText(callback: (text: TextComponent) => void): this { + callback(new TextComponent(this.controlEl)); + return this; + } +} + +class ButtonComponent { + readonly buttonEl: HTMLButtonElement; + + constructor(parent: HTMLElement) { + this.buttonEl = parent.createEl("button", { attr: { type: "button" } }); + } + + setButtonText(text: string): this { + this.buttonEl.textContent = text; + return this; + } + + setDisabled(disabled: boolean): this { + this.buttonEl.disabled = disabled; + return this; + } + + onClick(callback: () => void): this { + this.buttonEl.onclick = callback; + return this; + } +} + +class ExtraButtonComponent { + readonly extraSettingsEl: HTMLButtonElement; + + constructor(parent: HTMLElement) { + this.extraSettingsEl = parent.createEl("button", { cls: "clickable-icon", attr: { type: "button" } }); + } + + setDisabled(disabled: boolean): this { + this.extraSettingsEl.disabled = disabled; + return this; + } + + setTooltip(tooltip: string): this { + this.extraSettingsEl.title = tooltip; + return this; + } + + setIcon(icon: string): this { + setIcon(this.extraSettingsEl, icon); + return this; + } + + onClick(callback: () => void): this { + this.extraSettingsEl.onclick = callback; + return this; + } +} + +class DropdownComponent { + readonly selectEl: HTMLSelectElement; + + constructor(parent: HTMLElement) { + this.selectEl = parent.createEl("select"); + } + + addOption(value: string, label: string): this { + const option = document.createElement("option"); + option.value = value; + option.textContent = label; + this.selectEl.append(option); + return this; + } + + setValue(value: string): this { + this.selectEl.value = value; + return this; + } + + onChange(callback: (value: string) => void | Promise): this { + this.selectEl.onchange = () => { + void callback(this.selectEl.value); + }; + return this; + } +} + +class TextComponent { + readonly inputEl: HTMLInputElement; + + constructor(parent: HTMLElement) { + this.inputEl = parent.createEl("input"); + } + + setPlaceholder(value: string): this { + this.inputEl.placeholder = value; + return this; + } + + setValue(value: string): this { + this.inputEl.value = value; + return this; + } + + onChange(callback: (value: string) => void | Promise): this { + this.inputEl.onchange = () => { + void callback(this.inputEl.value); + }; + return this; + } +} + +export function setIcon(element: HTMLElement, icon: string): void { + element.dataset.icon = icon; +} + +function ensureElementHelpers(): void { + if (typeof HTMLElement === "undefined") return; + + if (!HTMLElement.prototype.addClass) { + HTMLElement.prototype.addClass = function addClass(className: string): void { + this.classList.add(className); + }; + } + + if (!HTMLElement.prototype.empty) { + HTMLElement.prototype.empty = function empty(): void { + this.replaceChildren(); + }; + } + + if (!HTMLElement.prototype.setAttr) { + HTMLElement.prototype.setAttr = function setAttr(name: string, value: string): void { + this.setAttribute(name, value); + }; + } + + if (!HTMLElement.prototype.createEl) { + HTMLElement.prototype.createEl = function createEl( + tag: K, + options: ElementOptions = {}, + ): HTMLElementTagNameMap[K] { + const child = document.createElement(tag); + applyOptions(child, options); + this.append(child); + return child; + }; + } + + if (!HTMLElement.prototype.createDiv) { + HTMLElement.prototype.createDiv = function createDiv(options: ElementOptions = {}): HTMLDivElement { + return this.createEl("div", options); + }; + } + + if (!HTMLElement.prototype.createSpan) { + HTMLElement.prototype.createSpan = function createSpan(options: ElementOptions = {}): HTMLSpanElement { + return this.createEl("span", options); + }; + } +} + +function applyOptions(element: HTMLElement, options: ElementOptions): void { + if (Array.isArray(options.cls)) { + element.classList.add(...options.cls.filter(Boolean)); + } else if (options.cls) { + element.className = options.cls; + } + + if (options.text !== undefined) { + element.textContent = options.text; + } + + for (const [name, value] of Object.entries(options.attr ?? {})) { + element.setAttribute(name, value); + } +} diff --git a/tests/panel-controller.test.ts b/tests/panel-controller.test.ts new file mode 100644 index 00000000..52beb470 --- /dev/null +++ b/tests/panel-controller.test.ts @@ -0,0 +1,722 @@ +import { describe, expect, it, vi } from "vitest"; + +import { PanelController } from "../src/panel/controller"; +import { createPanelState } from "../src/state/panel-state"; +import type { ServerNotification } from "../src/generated/app-server/ServerNotification"; +import type { ServerRequest } from "../src/generated/app-server/ServerRequest"; +import type { Thread } from "../src/generated/app-server/v2/Thread"; +import type { Turn } from "../src/generated/app-server/v2/Turn"; + +function controllerForState( + state = createPanelState(), + actions: Partial[1]> = {}, +): PanelController { + return new PanelController(state, { + refreshThreads: vi.fn(), + maybeNameThread: vi.fn(), + respondToServerRequest: vi.fn(() => true), + rejectServerRequest: vi.fn(() => true), + ...actions, + }); +} + +describe("PanelController", () => { + it("ignores item notifications for a different active thread", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "item/agentMessage/delta", + params: { threadId: "thread-other", turnId: "turn-active", itemId: "a1", delta: "wrong" }, + } satisfies Extract); + + expect(state.displayItems).toEqual([]); + }); + + it("ignores item notifications for a different active turn", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "item/agentMessage/delta", + params: { threadId: "thread-active", turnId: "turn-other", itemId: "a1", delta: "wrong" }, + } satisfies Extract); + + expect(state.displayItems).toEqual([]); + }); + + it("applies matching streaming deltas as lightweight assistant text", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "item/agentMessage/delta", + params: { threadId: "thread-active", turnId: "turn-active", itemId: "a1", delta: "hello" }, + } satisfies Extract); + + expect(state.displayItems).toMatchObject([{ id: "a1", text: "hello", markdown: false }]); + }); + + it("marks active reasoning completed when assistant text starts", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + state.busy = true; + state.displayItems = [{ id: "r1", kind: "reasoning", role: "tool", text: "thinking", turnId: "turn-active" }]; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "item/agentMessage/delta", + params: { threadId: "thread-active", turnId: "turn-active", itemId: "a1", delta: "answer" }, + } satisfies Extract); + + expect(state.displayItems).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: "r1", kind: "reasoning", status: "completed", state: "completed" }), + expect.objectContaining({ id: "a1", kind: "message", text: "answer" }), + ]), + ); + }); + + it("streams plan deltas as plain assistant text until completion", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "item/plan/delta", + params: { threadId: "thread-active", turnId: "turn-active", itemId: "p1", delta: "\n# Plan" }, + } satisfies Extract); + + expect(state.displayItems).toMatchObject([{ id: "p1", kind: "message", role: "assistant", text: "# Plan", markdown: false }]); + }); + + it("updates structured turn plan progress", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "turn/plan/updated", + params: { + threadId: "thread-active", + turnId: "turn-active", + explanation: "Plan", + plan: [{ step: "Inspect code", status: "inProgress" }], + }, + } satisfies Extract); + + expect(state.displayItems).toMatchObject([ + { + id: "plan-progress-turn-active", + kind: "taskProgress", + text: "Plan\n[>] Inspect code", + steps: [{ step: "Inspect code", status: "inProgress" }], + status: "inProgress", + }, + ]); + }); + + it("formats hook runs as compact summaries with details", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "hook/completed", + params: { + threadId: "thread-active", + turnId: "turn-active", + run: { + id: "hook-1", + eventName: "postToolUse", + handlerType: "command", + executionMode: "sync", + scope: "turn", + sourcePath: "/vault/.codex/hooks.json", + source: "project", + displayOrder: 1n, + status: "completed", + statusMessage: "Formatted 1 file.", + startedAt: 1n, + completedAt: 2n, + durationMs: 1n, + entries: [{ kind: "feedback", text: "ok" }], + }, + }, + } satisfies Extract); + + expect(state.displayItems).toMatchObject([ + { + id: "hook-hook-1", + kind: "hook", + text: "postToolUse: Formatted 1 file.", + toolLabel: "hook", + status: "completed", + output: "", + details: [ + { + rows: [ + { key: "status", value: "completed" }, + { key: "event", value: "postToolUse" }, + { key: "message", value: "Formatted 1 file." }, + { key: "duration", value: "1ms" }, + ], + }, + { title: "Hook output", body: "feedback: ok" }, + ], + }, + ]); + }); + + it("stores account rate limit updates outside thread scope", () => { + const state = createPanelState(); + const controller = controllerForState(state); + + controller.handleNotification({ + method: "account/rateLimits/updated", + params: { + rateLimits: { + limitId: "codex", + limitName: "Codex", + primary: { usedPercent: 64, windowDurationMins: 300, resetsAt: null }, + secondary: null, + credits: null, + planType: null, + rateLimitReachedType: null, + }, + }, + } satisfies Extract); + + expect(state.rateLimit).toMatchObject({ + limitId: "codex", + primary: { usedPercent: 64 }, + }); + }); + + it("queues and resolves requestUserInput server requests", () => { + const state = createPanelState(); + const respondToServerRequest = vi.fn(() => true); + const controller = controllerForState(state, { respondToServerRequest }); + + controller.handleServerRequest({ + id: 42, + method: "item/tool/requestUserInput", + params: { + threadId: "thread-active", + turnId: "turn-active", + itemId: "input-1", + questions: [ + { + id: "scope", + header: "Scope", + question: "How broad should this be?", + isOther: true, + isSecret: false, + options: [{ label: "Narrow", description: "Small change" }], + }, + ], + }, + }); + + expect(state.pendingUserInputs).toHaveLength(1); + controller.resolveUserInput(state.pendingUserInputs[0], { scope: "Narrow" }); + expect(respondToServerRequest).toHaveBeenCalledWith(42, { answers: { scope: { answers: ["Narrow"] } } }); + expect(state.pendingUserInputs).toEqual([]); + expect(state.displayItems.at(-1)).toMatchObject({ + kind: "userInputResult", + role: "tool", + text: "Input submitted for 1 question.", + turnId: "turn-active", + details: [{ title: "Scope", rows: expect.arrayContaining([{ key: "answer", value: "Narrow" }]) }], + }); + }); + + it("rejects cancelled requestUserInput server requests", () => { + const state = createPanelState(); + const rejectServerRequest = vi.fn(() => true); + const controller = controllerForState(state, { rejectServerRequest }); + + controller.handleServerRequest({ + id: 43, + method: "item/tool/requestUserInput", + params: { + threadId: "thread-active", + turnId: "turn-active", + itemId: "input-1", + questions: [{ id: "note", header: "Note", question: "What now?", isOther: false, isSecret: false, options: null }], + }, + }); + + controller.cancelUserInput(state.pendingUserInputs[0]); + expect(rejectServerRequest).toHaveBeenCalledWith(43, -32000, "User cancelled input request."); + expect(state.pendingUserInputs).toEqual([]); + expect(state.displayItems.at(-1)).toMatchObject({ + kind: "userInputResult", + role: "tool", + text: "Input request cancelled for 1 question.", + turnId: "turn-active", + }); + }); + + it("handles known server request families and rejects unsupported requests by default", () => { + const state = createPanelState(); + const rejectServerRequest = vi.fn(() => true); + const controller = controllerForState(state, { rejectServerRequest }); + + for (const request of supportedApprovalRequests()) { + controller.handleServerRequest(request); + } + controller.handleServerRequest(userInputRequest(20)); + for (const request of unsupportedRequests()) { + controller.handleServerRequest(request); + } + + expect(state.approvals.map((approval) => approval.requestId)).toEqual([10, 11, 12]); + expect(state.pendingUserInputs.map((input) => input.requestId)).toEqual([20]); + expect(rejectServerRequest).toHaveBeenCalledTimes(4); + expect(rejectServerRequest).toHaveBeenNthCalledWith( + 1, + 21, + -32601, + "Rejected unsupported app-server request: mcpServer/elicitation/request", + ); + expect(rejectServerRequest).toHaveBeenNthCalledWith(2, 22, -32601, "Rejected unsupported app-server request: item/tool/call"); + expect(rejectServerRequest).toHaveBeenNthCalledWith( + 3, + 23, + -32601, + "Rejected unsupported app-server request: account/chatgptAuthTokens/refresh", + ); + expect(rejectServerRequest).toHaveBeenNthCalledWith( + 4, + 24, + -32601, + "Rejected unsupported app-server request: appServer/newFutureRequest", + ); + expect(state.displayItems.map((item) => item.text)).toEqual([ + "Rejected unsupported app-server request: mcpServer/elicitation/request", + "Rejected unsupported app-server request: item/tool/call", + "Rejected unsupported app-server request: account/chatgptAuthTokens/refresh", + "Rejected unsupported app-server request: appServer/newFutureRequest", + ]); + expect(state.displayItems.map((item) => item.text).join("\n")).not.toContain("do-not-render"); + }); + + it("rejects server requests scoped to a different active thread or turn", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + const rejectServerRequest = vi.fn(() => true); + const controller = controllerForState(state, { rejectServerRequest }); + + controller.handleServerRequest({ + id: 51, + method: "item/tool/requestUserInput", + params: { + threadId: "thread-other", + turnId: "turn-active", + itemId: "input", + questions: [{ id: "note", header: "Note", question: "What now?", isOther: false, isSecret: false, options: null }], + }, + }); + controller.handleServerRequest({ + id: 52, + method: "item/tool/requestUserInput", + params: { + threadId: "thread-active", + turnId: "turn-other", + itemId: "input", + questions: [{ id: "note", header: "Note", question: "What now?", isOther: false, isSecret: false, options: null }], + }, + }); + + expect(state.pendingUserInputs).toEqual([]); + expect(rejectServerRequest).toHaveBeenCalledTimes(2); + expect(rejectServerRequest).toHaveBeenNthCalledWith(1, 51, -32601, "Rejected inactive app-server request: item/tool/requestUserInput"); + expect(rejectServerRequest).toHaveBeenNthCalledWith(2, 52, -32601, "Rejected inactive app-server request: item/tool/requestUserInput"); + }); + + it("keeps pending requests when response delivery fails", () => { + const state = createPanelState(); + const respondToServerRequest = vi.fn(() => false); + const controller = controllerForState(state, { respondToServerRequest }); + + controller.handleServerRequest(userInputRequest(55)); + controller.resolveUserInput(state.pendingUserInputs[0], { note: "Later" }); + + expect(state.pendingUserInputs).toHaveLength(1); + expect(state.displayItems).toEqual([ + expect.objectContaining({ kind: "system", text: "Could not send user input because Codex app-server is not connected." }), + ]); + }); + + it("clears all active-thread scoped state when the active thread is archived", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + state.activeModel = "gpt-5.5"; + state.activeServiceTier = "fast"; + state.activeThreadCliVersion = "codex-cli 1.0.0"; + state.tokenUsage = { + last: { inputTokens: 1, cachedInputTokens: 0, outputTokens: 2, reasoningOutputTokens: 0, totalTokens: 3 }, + total: { inputTokens: 1, cachedInputTokens: 0, outputTokens: 2, reasoningOutputTokens: 0, totalTokens: 3 }, + modelContextWindow: 100, + }; + state.historyCursor = "cursor"; + state.loadingHistory = true; + state.displayItems = [{ id: "message", kind: "message", role: "assistant", text: "stale" }]; + state.busy = true; + state.approvals = [ + { + requestId: 10, + method: "item/commandExecution/requestApproval", + params: supportedApprovalRequests()[0]!.params as Extract< + ServerRequest, + { method: "item/commandExecution/requestApproval" } + >["params"], + }, + ]; + state.pendingUserInputs = [ + { + requestId: 20, + method: "item/tool/requestUserInput", + params: { + threadId: "thread-active", + turnId: "turn-active", + itemId: "input", + questions: [{ id: "note", header: "Note", question: "What now?", isOther: false, isSecret: false, options: null }], + }, + }, + ]; + state.userInputDrafts.set("20:note", "draft"); + const controller = controllerForState(state); + + controller.handleNotification({ + method: "thread/archived", + params: { threadId: "thread-active" }, + } satisfies Extract); + + expect(state.activeThreadId).toBeNull(); + expect(state.activeTurnId).toBeNull(); + expect(state.activeModel).toBeNull(); + expect(state.activeServiceTier).toBeNull(); + expect(state.activeThreadCliVersion).toBeNull(); + expect(state.tokenUsage).toBeNull(); + expect(state.historyCursor).toBeNull(); + expect(state.loadingHistory).toBe(false); + expect(state.displayItems).toEqual([]); + expect(state.busy).toBe(false); + expect(state.approvals).toEqual([]); + expect(state.pendingUserInputs).toEqual([]); + expect(state.userInputDrafts.size).toBe(0); + }); + + it("does not replace the active cwd from unrelated thread-started notifications", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeThreadCwd = "/workspace/active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "thread/started", + params: { thread: thread("thread-other", "/workspace/other") }, + } satisfies Extract); + + expect(state.activeThreadCwd).toBe("/workspace/active"); + }); + + it("records cwd from active thread-started notifications", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "thread/started", + params: { thread: thread("thread-active", "/workspace/active") }, + } satisfies Extract); + + expect(state.activeThreadCwd).toBe("/workspace/active"); + }); + + it("replaces optimistic user echoes when completed turns are reconciled", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + state.displayItems = [ + { id: "local-user-1", kind: "message", role: "user", text: "hello", turnId: "turn-active", markdown: true }, + { + id: "a1", + itemId: "a1", + kind: "message", + role: "assistant", + text: "partial", + turnId: "turn-active", + markdown: false, + }, + ]; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "turn/completed", + params: { + threadId: "thread-active", + turn: { + id: "turn-active", + status: "completed", + error: null, + startedAt: null, + completedAt: null, + durationMs: null, + itemsView: "full", + items: [ + { type: "userMessage", id: "u1", content: [{ type: "text", text: "hello", text_elements: [] }] }, + { type: "agentMessage", id: "a1", text: "done", phase: "final_answer", memoryCitation: null }, + ], + }, + }, + } satisfies Extract); + + expect(state.displayItems.filter((item) => item.kind === "message" && item.role === "user")).toEqual([ + expect.objectContaining({ id: "u1", text: "hello" }), + ]); + expect(state.displayItems).toEqual(expect.arrayContaining([expect.objectContaining({ id: "a1", text: "done", markdown: true })])); + expect(state.displayItems.some((item) => item.id === "local-user-1")).toBe(false); + }); + + it("asks the view to auto-name completed turns", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + const maybeNameThread = vi.fn(); + const controller = controllerForState(state, { maybeNameThread }); + const turn: Turn = { + id: "turn-active", + status: "completed", + error: null, + startedAt: null, + completedAt: null, + durationMs: null, + itemsView: "full", + items: [ + { type: "userMessage", id: "u1", content: [{ type: "text", text: "hello", text_elements: [] }] }, + { type: "agentMessage", id: "a1", text: "done", phase: "final_answer", memoryCitation: null }, + ], + }; + + controller.handleNotification({ + method: "turn/completed", + params: { threadId: "thread-active", turn }, + } satisfies Extract); + + expect(maybeNameThread).toHaveBeenCalledWith("thread-active", turn); + }); + + it("updates listed thread names from thread name notifications", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.listedThreads = [thread("thread-active", "/workspace/active")]; + const refreshThreads = vi.fn(); + const controller = controllerForState(state, { refreshThreads }); + + controller.handleNotification({ + method: "thread/name/updated", + params: { threadId: "thread-active", threadName: "Codex Panel自動命名" }, + } satisfies Extract); + + expect(state.listedThreads[0]?.name).toBe("Codex Panel自動命名"); + expect(refreshThreads).toHaveBeenCalled(); + }); + + it("renders guardian warnings as review results instead of system messages", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "guardianWarning", + params: { threadId: "thread-active", message: "Auto-review denied this command." }, + } satisfies Extract); + + expect(state.displayItems).toMatchObject([ + { + kind: "reviewResult", + role: "tool", + text: "Auto-review denied this command.", + markdown: false, + }, + ]); + }); + + it("renders auto approval review notifications as upserted review results", () => { + const state = createPanelState(); + state.activeThreadId = "thread-active"; + state.activeTurnId = "turn-active"; + const controller = controllerForState(state); + + controller.handleNotification({ + method: "item/autoApprovalReview/started", + params: { + threadId: "thread-active", + turnId: "turn-active", + startedAtMs: 1, + reviewId: "review-1", + targetItemId: "cmd-1", + review: { status: "inProgress", riskLevel: "low", userAuthorization: null, rationale: null }, + action: { type: "command", source: "shell", command: "npm test", cwd: "/vault" }, + }, + } satisfies Extract); + controller.handleNotification({ + method: "item/autoApprovalReview/completed", + params: { + threadId: "thread-active", + turnId: "turn-active", + startedAtMs: 1, + completedAtMs: 2, + reviewId: "review-1", + targetItemId: "cmd-1", + decisionSource: "agent", + review: { status: "approved", riskLevel: "low", userAuthorization: "medium", rationale: "Allowed by policy." }, + action: { type: "command", source: "shell", command: "npm test", cwd: "/vault" }, + }, + } satisfies Extract); + + expect(state.displayItems).toHaveLength(1); + expect(state.displayItems[0]).toMatchObject({ + id: "review-review-1", + kind: "reviewResult", + text: "Auto-review approved: npm test", + state: "completed", + }); + const reviewItem = state.displayItems[0]; + expect(reviewItem && "details" in reviewItem ? reviewItem.details?.[0] : null).toMatchObject({ + title: "Review", + rows: expect.arrayContaining([{ key: "status", value: "approved" }]), + }); + }); +}); + +function supportedApprovalRequests(): ServerRequest[] { + return [ + { + id: 10, + method: "item/commandExecution/requestApproval", + params: { + command: "npm test", + cwd: "/tmp/project", + threadId: "thread", + turnId: "turn", + itemId: "command", + startedAtMs: 1, + reason: null, + commandActions: [], + proposedExecpolicyAmendment: null, + proposedNetworkPolicyAmendments: [], + }, + }, + { + id: 11, + method: "item/fileChange/requestApproval", + params: { + threadId: "thread", + turnId: "turn", + itemId: "file", + startedAtMs: 1, + reason: "write", + grantRoot: "/tmp/project", + }, + }, + { + id: 12, + method: "item/permissions/requestApproval", + params: { + cwd: "/tmp/project", + threadId: "thread", + turnId: "turn", + itemId: "permissions", + startedAtMs: 1, + reason: "Need access", + permissions: { network: null, fileSystem: null }, + }, + }, + ]; +} + +function userInputRequest(id: number): ServerRequest { + return { + id, + method: "item/tool/requestUserInput", + params: { + threadId: "thread", + turnId: "turn", + itemId: "input", + questions: [{ id: "note", header: "Note", question: "What now?", isOther: false, isSecret: false, options: null }], + }, + }; +} + +function thread(id: string, cwd: string): Thread { + return { + id, + sessionId: id, + forkedFromId: null, + preview: "", + ephemeral: false, + modelProvider: "openai", + createdAt: 0, + updatedAt: 0, + status: { type: "active", activeFlags: [] }, + path: null, + cwd, + cliVersion: "codex", + source: "appServer", + threadSource: null, + agentNickname: null, + agentRole: null, + gitInfo: null, + name: null, + turns: [], + }; +} + +function unsupportedRequests(): ServerRequest[] { + return [ + { + id: 21, + method: "mcpServer/elicitation/request", + params: { + threadId: "thread", + turnId: "turn", + serverName: "server", + mode: "form", + _meta: null, + message: "Need input", + requestedSchema: { type: "object", properties: {} }, + }, + }, + { + id: 22, + method: "item/tool/call", + params: { threadId: "thread", turnId: "turn", callId: "call", namespace: null, tool: "tool", arguments: {} }, + }, + { + id: 23, + method: "account/chatgptAuthTokens/refresh", + params: { reason: "unauthorized", previousAccountId: null }, + }, + { + id: 24, + method: "appServer/newFutureRequest", + params: { secret: "do-not-render" }, + } as unknown as ServerRequest, + ]; +} diff --git a/tests/runtime-settings.test.ts b/tests/runtime-settings.test.ts new file mode 100644 index 00000000..783e0c03 --- /dev/null +++ b/tests/runtime-settings.test.ts @@ -0,0 +1,241 @@ +import { describe, expect, it } from "vitest"; + +import { + compactContextLabel, + compactModelLabel, + compactReasoningEffortLabel, + modelOverrideMessage, + parseModelOverride, + parseReasoningEffortOverride, + reasoningEffortOverrideMessage, +} from "../src/panel/runtime-settings"; +import { + currentModel, + currentReasoningEffort, + fastModeLabel, + requestedOrConfiguredServiceTier, + requestedTurnRuntimeSettings, + resetRuntimeOverride, + setRuntimeOverride, + serviceTierLabel, + type RuntimeSnapshot, +} from "../src/panel/runtime-state"; +import { contextSummary, rateLimitSummary } from "../src/panel/runtime-view"; + +describe("runtime settings", () => { + it("parses model overrides", () => { + expect(parseModelOverride("gpt-5.5")).toBe("gpt-5.5"); + expect(parseModelOverride(" default ")).toBeNull(); + expect(parseModelOverride("")).toBeUndefined(); + }); + + it("parses reasoning effort overrides", () => { + expect(parseReasoningEffortOverride("high")).toBe("high"); + expect(parseReasoningEffortOverride("default")).toBeNull(); + expect(parseReasoningEffortOverride("extreme")).toBeUndefined(); + }); + + it("formats runtime override messages", () => { + expect(modelOverrideMessage("gpt-5.5")).toBe("Model set to gpt-5.5 for subsequent turns."); + expect(modelOverrideMessage(null)).toBe("Model reset to default for subsequent turns."); + expect(reasoningEffortOverrideMessage("low")).toBe("Effort set to low for subsequent turns."); + expect(reasoningEffortOverrideMessage(null)).toBe("Effort reset to default for subsequent turns."); + }); + + it("formats compact runtime labels", () => { + expect(compactModelLabel("gpt-5.5")).toBe("5.5"); + expect(compactModelLabel("custom-model")).toBe("custom-model"); + expect(compactModelLabel(null)).toBe("default"); + expect(compactReasoningEffortLabel("minimal")).toBe("min"); + expect(compactReasoningEffortLabel("high")).toBe("high"); + expect(compactReasoningEffortLabel(null)).toBe("default"); + }); + + it("formats compact context labels", () => { + expect(compactContextLabel(42, "Context 42%")).toBe("42%"); + expect(compactContextLabel(null, "Context waiting")).toBe("wait"); + expect(compactContextLabel(null, "Context unknown")).toBe("?"); + expect(compactContextLabel(null, "1.2K tokens")).toBe("1.2K tokens"); + }); + + it("keeps runtime defaults, resets, and turn payload semantics distinct", () => { + const snapshot = runtimeSnapshot({ + requestedModel: resetRuntimeOverride(), + requestedReasoningEffort: resetRuntimeOverride(), + }); + + expect(currentModel(snapshot)).toBe("gpt-5.5"); + expect(currentReasoningEffort(snapshot)).toBe("high"); + expect(requestedTurnRuntimeSettings(snapshot)).toMatchObject({ + model: null, + effort: null, + collaborationMode: { + mode: "default", + settings: { model: "gpt-5.5", reasoning_effort: "high" }, + }, + }); + }); + + it("serializes explicit runtime overrides as turn payload values", () => { + const snapshot = runtimeSnapshot({ + requestedModel: setRuntimeOverride("gpt-5.4"), + requestedReasoningEffort: setRuntimeOverride("low"), + }); + + expect(currentModel(snapshot)).toBe("gpt-5.4"); + expect(currentReasoningEffort(snapshot)).toBe("low"); + expect(requestedTurnRuntimeSettings(snapshot)).toMatchObject({ + model: "gpt-5.4", + effort: "low", + }); + }); + + it("summarizes service tier and context meter state from one runtime snapshot", () => { + const snapshot = runtimeSnapshot({ requestedServiceTier: "fast", activeThreadId: "thread" }); + + expect(serviceTierLabel(snapshot)).toBe("fast"); + expect(fastModeLabel(snapshot)).toBe("on"); + expect(contextSummary(snapshot)).toMatchObject({ + label: "Context 0%", + percent: 0, + level: "ok", + }); + expect( + contextSummary( + runtimeSnapshot({ + activeThreadId: "thread", + displayItems: [{ id: "u1", kind: "message", role: "user", text: "hi", turnId: "t1" }], + }), + ), + ).toMatchObject({ + label: "Context unknown", + percent: null, + }); + }); + + it("serializes explicit fast off as a null service tier request", () => { + const snapshot = runtimeSnapshot({ + effectiveConfig: { config: { service_tier: "fast" } } as unknown as RuntimeSnapshot["effectiveConfig"], + requestedServiceTier: "standard", + }); + + expect(serviceTierLabel(snapshot)).toBe("standard"); + expect(fastModeLabel(snapshot)).toBe("off"); + expect(requestedOrConfiguredServiceTier(snapshot)).toBeNull(); + }); + + it("omits service tier when neither config nor override selects one", () => { + expect( + requestedOrConfiguredServiceTier( + runtimeSnapshot({ effectiveConfig: { config: {} } as unknown as RuntimeSnapshot["effectiveConfig"] }), + ), + ).toBeUndefined(); + }); + + it("summarizes Codex usage limits independently from context usage", () => { + expect( + rateLimitSummary( + runtimeSnapshot({ + rateLimit: { + limitId: "codex", + limitName: "Codex", + primary: { usedPercent: 72.4, windowDurationMins: 300, resetsAt: 1_800_000_000 }, + secondary: null, + credits: null, + planType: null, + rateLimitReachedType: null, + }, + }), + 1_799_991_600_000, + ), + ).toMatchObject({ + rows: [{ label: "5h", value: "72%", resetLabel: "reset in 2h 20m", percent: 72 }], + level: "warn", + }); + + expect( + rateLimitSummary( + runtimeSnapshot({ + rateLimit: { + limitId: "codex", + limitName: null, + primary: { usedPercent: 95, windowDurationMins: null, resetsAt: null }, + secondary: null, + credits: null, + planType: null, + rateLimitReachedType: "rate_limit_reached", + }, + }), + ), + ).toMatchObject({ + rows: [{ percent: 95, resetLabel: null }], + level: "danger", + }); + + expect( + rateLimitSummary( + runtimeSnapshot({ + rateLimit: { + limitId: "codex", + limitName: "Codex", + primary: { usedPercent: 15, windowDurationMins: 300, resetsAt: null }, + secondary: { usedPercent: 38, windowDurationMins: 10_080, resetsAt: null }, + credits: null, + planType: null, + rateLimitReachedType: null, + }, + }), + ), + ).toMatchObject({ + rows: [ + { label: "5h", value: "15%" }, + { label: "1w", value: "38%" }, + ], + level: "ok", + }); + + expect( + rateLimitSummary( + runtimeSnapshot({ + rateLimit: { + limitId: "codex", + limitName: "Codex", + primary: { usedPercent: 10, windowDurationMins: 300, resetsAt: 1_800_000_000 }, + secondary: null, + credits: null, + planType: null, + rateLimitReachedType: null, + }, + }), + 1_800_000_001_000, + ), + ).toMatchObject({ + rows: [{ resetLabel: "reset due" }], + }); + }); +}); + +function runtimeSnapshot(overrides: Partial = {}): RuntimeSnapshot { + return { + effectiveConfig: { + config: { + model: "gpt-5.5", + model_reasoning_effort: "high", + service_tier: "flex", + model_context_window: 100_000, + }, + } as unknown as RuntimeSnapshot["effectiveConfig"], + activeThreadId: null, + activeModel: null, + activeServiceTier: null, + requestedModel: { kind: "default" }, + requestedReasoningEffort: { kind: "default" }, + requestedCollaborationMode: "default", + requestedServiceTier: null, + tokenUsage: null, + rateLimit: null, + displayItems: [], + availableModels: [], + ...overrides, + }; +} diff --git a/tests/service-tier.test.ts b/tests/service-tier.test.ts new file mode 100644 index 00000000..39465ff2 --- /dev/null +++ b/tests/service-tier.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; + +import { parseServiceTier, serviceTierRequestValue } from "../src/app-server/service-tier"; + +describe("service tier", () => { + it("accepts the service tiers supported by the panel UI", () => { + expect(parseServiceTier("fast")).toBe("fast"); + expect(parseServiceTier("standard")).toBe("standard"); + expect(parseServiceTier("default")).toBe("standard"); + expect(parseServiceTier("flex")).toBe("standard"); + }); + + it("normalizes Codex app-server priority responses to fast mode", () => { + expect(parseServiceTier("priority")).toBe("fast"); + }); + + it("ignores unknown config values", () => { + expect(parseServiceTier("auto")).toBeNull(); + expect(parseServiceTier(null)).toBeNull(); + }); + + it("serializes standard mode as explicit null instead of flex", () => { + expect(serviceTierRequestValue("fast")).toBe("fast"); + expect(serviceTierRequestValue("standard")).toBeNull(); + expect(serviceTierRequestValue(null)).toBeUndefined(); + }); +}); diff --git a/tests/settings-tab.test.ts b/tests/settings-tab.test.ts new file mode 100644 index 00000000..0ff188d7 --- /dev/null +++ b/tests/settings-tab.test.ts @@ -0,0 +1,308 @@ +import { createRequire } from "node:module"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import type { Thread } from "../src/generated/app-server/v2/Thread"; +import type { HookMetadata } from "../src/generated/app-server/v2/HookMetadata"; +import type { Model } from "../src/generated/app-server/v2/Model"; +import type { ReasoningEffort } from "../src/generated/app-server/ReasoningEffort"; +import { findModelByIdOrName, sortedAvailableModels, supportedEffortsForModel } from "../src/panel/model-runtime"; +import { CodexPanelSettingTab } from "../src/settings-tab"; +import { archivedThreadDisplayTitle } from "../src/threads"; +import { notices } from "./mocks/obsidian"; + +const require = createRequire(import.meta.url); +const { JSDOM } = require("jsdom") as { + JSDOM: new (html: string) => { window: Window & typeof globalThis }; +}; + +const { withAppServerSessionMock } = vi.hoisted(() => ({ + withAppServerSessionMock: vi.fn(), +})); + +vi.mock("../src/app-server/session-client", () => ({ + withAppServerSession: withAppServerSessionMock, +})); + +describe("settings tab", () => { + beforeEach(() => { + const dom = new JSDOM(""); + vi.stubGlobal("document", dom.window.document); + vi.stubGlobal("HTMLElement", dom.window.HTMLElement); + vi.stubGlobal("HTMLButtonElement", dom.window.HTMLButtonElement); + vi.stubGlobal("HTMLDivElement", dom.window.HTMLDivElement); + vi.stubGlobal("HTMLInputElement", dom.window.HTMLInputElement); + vi.stubGlobal("HTMLSelectElement", dom.window.HTMLSelectElement); + withAppServerSessionMock.mockReset(); + notices.length = 0; + }); + + it("uses a placeholder for threads without a useful title", () => { + expect(archivedThreadDisplayTitle(thread({ name: null, preview: "" }))).toBe("Untitled archived thread"); + expect(archivedThreadDisplayTitle(thread({ name: "019e0182-cb70-7a72-ab48-8bc9d0b0d781" }))).toBe("Untitled archived thread"); + }); + + it("normalizes and truncates archived thread titles", () => { + expect(archivedThreadDisplayTitle(thread({ preview: "A title\nwith extra\tspace" }))).toBe("A title with extra space"); + + const title = archivedThreadDisplayTitle(thread({ preview: "x".repeat(120) })); + expect(title).toHaveLength(96); + expect(title.endsWith("...")).toBe(true); + }); + + it("sorts naming model choices by default flag and model id", () => { + expect( + sortedAvailableModels([model("z-model"), model("a-model"), model("b-default", true), model("hidden", false, true)]).map( + (item) => item.model, + ), + ).toEqual(["b-default", "a-model", "z-model"]); + }); + + it("finds selected models and falls back to all efforts when none are reported", () => { + const selected = model("gpt-5.4", false, false, ["low", "high"]); + expect(findModelByIdOrName([selected], "gpt-5.4")).toBe(selected); + expect(findModelByIdOrName([selected], selected.id)).toBe(selected); + expect(supportedEffortsForModel(selected)).toEqual(["low", "high"]); + expect(supportedEffortsForModel(model("custom", false, false, []))).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"]); + }); + + it("auto-loads settings data once and keeps one global refresh button", async () => { + const client = settingsClient(); + withAppServerSessionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => + operation(client), + ); + const tab = newSettingsTab(); + + tab.display(); + await flushPromises(); + + expect(withAppServerSessionMock).toHaveBeenCalledTimes(1); + expect(client.listModels).toHaveBeenCalledTimes(1); + expect(client.listHooks).toHaveBeenCalledTimes(1); + expect(client.listThreads).toHaveBeenCalledWith("/vault", true); + + tab.display(); + await flushPromises(); + + expect(withAppServerSessionMock).toHaveBeenCalledTimes(1); + expect(buttonTexts(tab)).toContain("Refresh settings data"); + expect(buttonTexts(tab)).not.toContain("Load models"); + expect(buttonTexts(tab)).not.toContain("Load hooks"); + expect(buttonTexts(tab)).not.toContain("Load archive list"); + expect(tab.containerEl.querySelector("h2")).toBeNull(); + expect(settingNames(tab)).toEqual([ + "General", + "Settings data", + "Codex executable", + "Thread naming model", + "Hook status", + "Archived thread list", + ]); + }); + + it("refreshes models, hooks, and archived threads from the global refresh button", async () => { + const firstClient = settingsClient({ models: [model("gpt-5.4")], threads: [thread({ id: "thread-old", preview: "Old" })] }); + const secondClient = settingsClient({ + models: [model("gpt-5.5")], + threads: [thread({ id: "thread-new", preview: "New" })], + }); + withAppServerSessionMock + .mockImplementationOnce((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => + operation(firstClient), + ) + .mockImplementationOnce((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => + operation(secondClient), + ); + const tab = newSettingsTab(); + + tab.display(); + await flushPromises(); + clickButton(tab, "Refresh settings data"); + await flushPromises(); + + expect(withAppServerSessionMock).toHaveBeenCalledTimes(2); + expect(tab.containerEl.textContent).toContain("gpt-5.5"); + expect(tab.containerEl.textContent).toContain("New"); + expect(tab.containerEl.textContent).not.toContain("Old"); + }); + + it("keeps successful sections when one settings data request fails", async () => { + const client = settingsClient({ + models: [model("gpt-5.4")], + hooksError: new Error("hooks unavailable"), + threads: [thread({ preview: "Archived thread" })], + }); + withAppServerSessionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => + operation(client), + ); + const tab = newSettingsTab(); + + tab.display(); + await flushPromises(); + + expect(tab.containerEl.textContent).not.toContain("Loaded 1 model."); + expect(tab.containerEl.textContent).toContain("Could not load hooks: hooks unavailable"); + expect(tab.containerEl.textContent).toContain("Archived thread"); + expect(notices).toEqual(["Could not refresh all Codex settings data."]); + }); + + it("renders archived threads and hooks as dynamic setting rows", async () => { + const client = settingsClient({ + hooks: [hook({ key: "hook-1", command: "node hook.js", currentHash: "abc123", trustStatus: "untrusted" })], + threads: [thread({ id: "thread-archived", preview: "Archived thread" })], + }); + withAppServerSessionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => + operation(client), + ); + const tab = newSettingsTab(); + + tab.display(); + await flushPromises(); + + expect(tab.containerEl.textContent).toContain("Loaded 1 hook from Codex app-server."); + expect(tab.containerEl.textContent).toContain("Loaded 1 archived thread from Codex app-server."); + expect(tab.containerEl.querySelector(".codex-panel-settings__hook-section .setting-item-heading")?.textContent).toContain( + "Hook status", + ); + expect(tab.containerEl.querySelector(".codex-panel-settings__archived-section .setting-item-heading")?.textContent).toContain( + "Archived thread list", + ); + expect(tab.containerEl.querySelectorAll(".codex-panel-settings__hook-list .setting-item")).toHaveLength(1); + expect(tab.containerEl.querySelectorAll(".codex-panel-settings__archived-list .setting-item")).toHaveLength(1); + expect(tab.containerEl.querySelector(".codex-panel-settings__hook-list")?.textContent).toContain("abc123"); + expect(tab.containerEl.querySelector(".codex-panel-settings__archived-list")?.textContent).toContain("Archived thread"); + }); +}); + +function thread(overrides: Partial = {}): Thread { + return { + id: "019e0182-cb70-7a72-ab48-8bc9d0b0d781", + sessionId: "019e0182-cb70-7a72-ab48-8bc9d0b0d781", + forkedFromId: null, + preview: "Preview", + ephemeral: false, + modelProvider: "openai", + createdAt: 1, + updatedAt: 1, + status: { type: "idle" }, + path: null, + cwd: "/tmp", + cliVersion: "codex-cli 0.0.0", + source: "appServer", + threadSource: null, + agentNickname: null, + agentRole: null, + gitInfo: null, + name: null, + turns: [], + ...overrides, + }; +} + +function model(modelId: string, isDefault = false, hidden = false, efforts: ReasoningEffort[] = ["medium"]): Model { + return { + id: `${modelId}-id`, + model: modelId, + upgrade: null, + upgradeInfo: null, + availabilityNux: null, + displayName: `Display ${modelId}`, + description: "", + isDefault, + hidden, + supportedReasoningEfforts: efforts.map((reasoningEffort) => ({ reasoningEffort, description: reasoningEffort })), + defaultReasoningEffort: "medium", + inputModalities: ["text"], + supportsPersonality: false, + additionalSpeedTiers: [], + serviceTiers: [], + } satisfies Model; +} + +function hook(overrides: Partial = {}): HookMetadata { + return { + key: "hook-key", + eventName: "postToolUse", + handlerType: "command", + matcher: "apply_patch", + command: "node hook.js", + timeoutSec: 10n, + statusMessage: null, + sourcePath: "/vault/.codex/hooks.json", + source: "project", + pluginId: null, + displayOrder: 0n, + enabled: true, + isManaged: false, + currentHash: "hash", + trustStatus: "trusted", + ...overrides, + }; +} + +function settingsClient(options: { models?: Model[]; hooks?: HookMetadata[]; hooksError?: Error; threads?: Thread[] } = {}) { + return { + listModels: vi.fn().mockResolvedValue({ data: options.models ?? [model("gpt-5.4")] }), + listHooks: vi.fn().mockImplementation(() => { + if (options.hooksError) return Promise.reject(options.hooksError); + return Promise.resolve({ + data: [ + { + cwd: "/vault", + hooks: options.hooks ?? [], + warnings: [], + errors: [], + }, + ], + }); + }), + listThreads: vi.fn().mockResolvedValue({ data: options.threads ?? [thread({ preview: "Archived" })] }), + }; +} + +function newSettingsTab(): CodexPanelSettingTab { + return new CodexPanelSettingTab( + {} as never, + { + settings: { + codexPath: "codex", + threadNamingModel: null, + threadNamingEffort: null, + }, + vaultPath: "/vault", + saveSettings: vi.fn().mockResolvedValue(undefined), + refreshOpenThreadLists: vi.fn(), + } as never, + ); +} + +async function flushPromises(): Promise { + await new Promise((resolve) => setImmediate(resolve)); + await new Promise((resolve) => setImmediate(resolve)); +} + +function settingNames(tab: CodexPanelSettingTab): string[] { + return Array.from(tab.containerEl.children).flatMap((element) => { + if (element.classList.contains("setting-item")) { + return [element.querySelector(".setting-item-name")?.textContent ?? ""]; + } + if (element.classList.contains("codex-panel-settings__general-section")) { + return Array.from(element.querySelectorAll(":scope > .setting-item")).map( + (setting) => setting.querySelector(".setting-item-name")?.textContent ?? "", + ); + } + if (element.classList.contains("codex-panel-settings__dynamic-section")) { + return [element.querySelector(":scope > .setting-item-heading .setting-item-name")?.textContent ?? ""]; + } + return []; + }); +} + +function buttonTexts(tab: CodexPanelSettingTab): string[] { + return Array.from(tab.containerEl.querySelectorAll("button")).map((element) => element.textContent ?? ""); +} + +function clickButton(tab: CodexPanelSettingTab, text: string): void { + const button = Array.from(tab.containerEl.querySelectorAll("button")).find((element) => element.textContent === text); + if (!button) throw new Error(`Could not find button: ${text}`); + button.click(); +} diff --git a/tests/settings.test.ts b/tests/settings.test.ts new file mode 100644 index 00000000..cb1805ea --- /dev/null +++ b/tests/settings.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from "vitest"; +import { FileSystemAdapter, type App } from "obsidian"; + +import { DEFAULT_SETTINGS, getVaultPath, normalizeSettings, settingsMatchNormalizedData } from "../src/settings"; + +describe("settings", () => { + it("normalizes empty data", () => { + expect(normalizeSettings(null)).toEqual(DEFAULT_SETTINGS); + }); + + it("drops unknown keys", () => { + expect( + normalizeSettings({ + codexPath: "/usr/local/bin/codex", + threadNamingModel: "gpt-5.4-mini", + threadNamingEffort: "low", + extraPanelState: { threadId: "thread-1" }, + }), + ).toEqual({ + codexPath: "/usr/local/bin/codex", + threadNamingModel: "gpt-5.4-mini", + threadNamingEffort: "low", + }); + }); + + it("uses the default codex path when the stored path is empty", () => { + expect(normalizeSettings({ codexPath: " " }).codexPath).toBe(DEFAULT_SETTINGS.codexPath); + }); + + it("detects when normalized settings need to be written back", () => { + const settings = normalizeSettings({ + codexPath: "/usr/local/bin/codex", + threadNamingModel: "gpt-5.4-mini", + threadNamingEffort: "low", + }); + expect(settingsMatchNormalizedData({ ...settings }, settings)).toBe(true); + expect(settingsMatchNormalizedData({ ...settings, extraPanelState: {} }, settings)).toBe(false); + expect(settingsMatchNormalizedData({ ...settings, codexPath: " " }, settings)).toBe(false); + }); + + it("normalizes current naming runtime settings", () => { + expect(normalizeSettings({ threadNamingModel: " gpt-5.4-mini ", threadNamingEffort: "low" })).toMatchObject({ + threadNamingModel: "gpt-5.4-mini", + threadNamingEffort: "low", + }); + expect(normalizeSettings({ threadNamingModel: 1, threadNamingEffort: "invalid" })).toMatchObject({ + threadNamingModel: DEFAULT_SETTINGS.threadNamingModel, + threadNamingEffort: DEFAULT_SETTINGS.threadNamingEffort, + }); + }); + + it("requires a desktop filesystem vault path", () => { + const adapter = Object.create(FileSystemAdapter.prototype) as FileSystemAdapter; + Object.defineProperty(adapter, "getBasePath", { value: () => "/vault" }); + + expect(getVaultPath({ vault: { adapter } } as unknown as App)).toBe("/vault"); + expect(() => getVaultPath({ vault: { adapter: {} } } as unknown as App)).toThrow( + "This plugin requires a desktop vault with a local basePath.", + ); + }); +}); diff --git a/tests/slash-commands.test.ts b/tests/slash-commands.test.ts new file mode 100644 index 00000000..660f7377 --- /dev/null +++ b/tests/slash-commands.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it, vi } from "vitest"; + +import { executeSlashCommand, slashCommandHelpLines, type SlashCommandExecutionContext } from "../src/panel/slash-commands"; + +function context(overrides: Partial = {}): SlashCommandExecutionContext { + return { + activeThreadId: "thread-1", + compactThread: vi.fn().mockResolvedValue(undefined), + toggleFastMode: vi.fn(), + toggleCollaborationMode: vi.fn(), + addSystemMessage: vi.fn(), + setStatus: vi.fn(), + setRequestedModel: vi.fn(), + setRequestedReasoningEffort: vi.fn(), + statusSummaryLines: () => ["status"], + connectionDiagnosticLines: () => ["doctor"], + modelStatusLines: () => ["model"], + effortStatusLines: () => ["effort"], + ...overrides, + }; +} + +describe("slash commands", () => { + it("toggles Plan mode without sending text for bare /plan", async () => { + const ctx = context(); + + const result = await executeSlashCommand("plan", "", ctx); + + expect(ctx.toggleCollaborationMode).toHaveBeenCalledOnce(); + expect(result).toBeUndefined(); + }); + + it("returns message text after toggling Plan mode for /plan arguments", async () => { + const ctx = context(); + + const result = await executeSlashCommand("plan", "OK、実装してください", ctx); + + expect(ctx.toggleCollaborationMode).toHaveBeenCalledOnce(); + expect(result).toEqual({ sendText: "OK、実装してください" }); + }); + + it("keeps /compact behavior unchanged", async () => { + const ctx = context(); + + await executeSlashCommand("compact", "ignored for now", ctx); + + expect(ctx.compactThread).toHaveBeenCalledWith("thread-1"); + expect(ctx.addSystemMessage).toHaveBeenCalledWith("Compaction requested."); + }); + + it("documents that /plan can take a message", () => { + expect(slashCommandHelpLines().find((line) => line.startsWith("/plan"))).toBe( + "/plan - Toggle Plan mode, optionally sending a message.", + ); + }); +}); diff --git a/tests/thread-naming.test.ts b/tests/thread-naming.test.ts new file mode 100644 index 00000000..81951ed4 --- /dev/null +++ b/tests/thread-naming.test.ts @@ -0,0 +1,211 @@ +import { describe, expect, it } from "vitest"; + +import { + findThreadNamingContext, + firstNamingContextFromDisplayItems, + namingRuntime, + namingContextFromDisplayItems, + namingContextFromTurn, + normalizeGeneratedTitle, + titleFromNamingTurn, + validatedNamingRuntime, +} from "../src/panel/thread-naming"; +import type { Model } from "../src/generated/app-server/v2/Model"; +import type { Turn } from "../src/generated/app-server/v2/Turn"; + +describe("thread naming", () => { + it("extracts the first user request and final assistant response from a completed turn", () => { + expect( + namingContextFromTurn( + turn([ + { type: "userMessage", id: "u1", content: [{ type: "text", text: "Codex Panelに自動命名を付けたい", text_elements: [] }] }, + { type: "agentMessage", id: "a1", text: "実装方針をまとめました。", phase: "final_answer", memoryCitation: null }, + ]), + ), + ).toEqual({ + userRequest: "Codex Panelに自動命名を付けたい", + assistantResponse: "実装方針をまとめました。", + }); + }); + + it("does not build naming context for failed or incomplete turns", () => { + expect( + namingContextFromTurn( + turn([{ type: "userMessage", id: "u1", content: [{ type: "text", text: "hello", text_elements: [] }] }], { + status: "failed", + }), + ), + ).toBeNull(); + }); + + it("extracts naming context from streamed display items when completed turn items are not loaded", () => { + expect( + namingContextFromDisplayItems("turn", [ + { id: "u1", kind: "message", role: "user", text: "自動命名を直したい", turnId: "turn", markdown: true }, + { id: "a1", kind: "message", role: "assistant", text: "原因を直しました。", turnId: "turn", markdown: false }, + ]), + ).toEqual({ + userRequest: "自動命名を直したい", + assistantResponse: "原因を直しました。", + }); + }); + + it("uses the first usable displayed turn as a resumed-history fallback", () => { + expect( + firstNamingContextFromDisplayItems([ + { id: "u1", kind: "message", role: "user", text: "本文だけのturn", turnId: "turn-1", markdown: true }, + { id: "u2", kind: "message", role: "user", text: "履歴から命名したい", turnId: "turn-2", markdown: true }, + { id: "a2", kind: "message", role: "assistant", text: "表示済み履歴から候補を作ります。", turnId: "turn-2", markdown: false }, + { id: "u3", kind: "message", role: "user", text: "後続turn", turnId: "turn-3", markdown: true }, + { id: "a3", kind: "message", role: "assistant", text: "後続応答", turnId: "turn-3", markdown: false }, + ]), + ).toEqual({ + userRequest: "履歴から命名したい", + assistantResponse: "表示済み履歴から候補を作ります。", + }); + }); + + it("scans older thread pages until it finds a usable naming context", async () => { + const calls: Array<{ cursor: string | null; limit: number; sortDirection: string }> = []; + const context = await findThreadNamingContext({ + threadId: "thread", + pageLimit: 2, + maxPages: 3, + readTurns: async (_threadId, cursor, limit, sortDirection) => { + calls.push({ cursor, limit, sortDirection }); + if (cursor === null) { + return { + data: [ + turn([{ type: "userMessage", id: "u1", content: [{ type: "text", text: "本文だけ", text_elements: [] }] }], { + id: "turn-1", + }), + ], + nextCursor: "cursor-2", + }; + } + return { + data: [ + turn( + [ + { type: "userMessage", id: "u2", content: [{ type: "text", text: "古い履歴から命名したい", text_elements: [] }] }, + { type: "agentMessage", id: "a2", text: "古いturnを使って候補を作ります。", phase: "final_answer", memoryCitation: null }, + ], + { id: "turn-2" }, + ), + ], + nextCursor: null, + }; + }, + }); + + expect(context).toEqual({ + userRequest: "古い履歴から命名したい", + assistantResponse: "古いturnを使って候補を作ります。", + }); + expect(calls).toEqual([ + { cursor: null, limit: 2, sortDirection: "asc" }, + { cursor: "cursor-2", limit: 2, sortDirection: "asc" }, + ]); + }); + + it("falls back to visible display items after bounded history scanning", async () => { + await expect( + findThreadNamingContext({ + threadId: "thread", + pageLimit: 1, + maxPages: 1, + readTurns: async () => ({ data: [], nextCursor: "ignored" }), + fallbackDisplayItems: [ + { id: "u1", kind: "message", role: "user", text: "表示済み履歴から命名したい", turnId: "visible", markdown: true }, + { id: "a1", kind: "message", role: "assistant", text: "表示済み履歴を使います。", turnId: "visible", markdown: false }, + ], + }), + ).resolves.toEqual({ + userRequest: "表示済み履歴から命名したい", + assistantResponse: "表示済み履歴を使います。", + }); + }); + + it("parses structured title responses", () => { + expect( + titleFromNamingTurn( + turn([ + { + type: "agentMessage", + id: "a1", + text: '```json\n{"title":"Codex Panelの自動命名"}\n```', + phase: "final_answer", + memoryCitation: null, + }, + ]), + ), + ).toBe("Codex Panelの自動命名"); + }); + + it("normalizes generated titles", () => { + expect(normalizeGeneratedTitle(' ## "Codex Panelの自動命名"\n')).toBe("Codex Panelの自動命名"); + expect(normalizeGeneratedTitle("")).toBeNull(); + expect(normalizeGeneratedTitle("x".repeat(80))).toHaveLength(40); + }); + + it("uses explicit naming runtime settings", () => { + expect(namingRuntime({ threadNamingModel: "gpt-5.4-mini", threadNamingEffort: "minimal" })).toEqual({ + model: "gpt-5.4-mini", + effort: "minimal", + }); + }); + + it("omits naming runtime overrides that are set to Codex default", () => { + expect(namingRuntime({ threadNamingModel: null, threadNamingEffort: null })).toEqual({}); + }); + + it("omits an explicit naming effort when the selected model does not support it", () => { + expect( + validatedNamingRuntime({ threadNamingModel: "gpt-5.4-mini", threadNamingEffort: "minimal" }, [ + model("gpt-5.4-mini", ["low", "medium", "high", "xhigh"]), + ]), + ).toEqual({ model: "gpt-5.4-mini" }); + }); + + it("keeps an explicit naming effort when the selected model supports it", () => { + expect( + validatedNamingRuntime({ threadNamingModel: "gpt-5.4-mini", threadNamingEffort: "low" }, [ + model("gpt-5.4-mini", ["low", "medium", "high", "xhigh"]), + ]), + ).toEqual({ model: "gpt-5.4-mini", effort: "low" }); + }); +}); + +function turn(items: Turn["items"], overrides: Partial = {}): Turn { + return { + id: "turn", + items, + itemsView: "full", + status: "completed", + error: null, + startedAt: 1, + completedAt: 2, + durationMs: 1, + ...overrides, + }; +} + +function model(name: string, efforts: Model["supportedReasoningEfforts"][number]["reasoningEffort"][]): Model { + return { + id: name, + model: name, + upgrade: null, + upgradeInfo: null, + availabilityNux: null, + displayName: name, + description: "", + hidden: false, + supportedReasoningEfforts: efforts.map((reasoningEffort) => ({ reasoningEffort, description: "" })), + defaultReasoningEffort: efforts[0] ?? "low", + inputModalities: ["text"], + supportsPersonality: false, + additionalSpeedTiers: [], + serviceTiers: [], + isDefault: false, + }; +} diff --git a/tests/user-input.test.ts b/tests/user-input.test.ts new file mode 100644 index 00000000..449f37ec --- /dev/null +++ b/tests/user-input.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from "vitest"; + +import { questionDefaultAnswer, toPendingUserInput, userInputResponse } from "../src/user-input/model"; +import type { ServerRequest } from "../src/generated/app-server/ServerRequest"; + +describe("user input model", () => { + it("classifies requestUserInput and builds answers", () => { + const request: ServerRequest = { + id: 7, + method: "item/tool/requestUserInput", + params: { + threadId: "thread", + turnId: "turn", + itemId: "item", + questions: [ + { + id: "direction", + header: "Direction", + question: "Which way?", + isOther: true, + isSecret: false, + options: [{ label: "Recommended", description: "Use the default path" }], + }, + ], + }, + }; + + const input = toPendingUserInput(request); + expect(input).toMatchObject({ requestId: 7, method: "item/tool/requestUserInput" }); + expect(questionDefaultAnswer(request.params.questions[0])).toBe("Recommended"); + expect(userInputResponse(input!, { direction: "Recommended" })).toEqual({ + answers: { direction: { answers: ["Recommended"] } }, + }); + }); +}); diff --git a/tests/view-dom.test.ts b/tests/view-dom.test.ts new file mode 100644 index 00000000..3b4ba647 --- /dev/null +++ b/tests/view-dom.test.ts @@ -0,0 +1,55 @@ +// @vitest-environment jsdom + +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { renderTextWithWikiLinks, shortSignature } from "../src/view/dom"; + +declare global { + interface HTMLElement { + createEl( + tag: K, + options?: { cls?: string; text?: string; attr?: Record }, + ): HTMLElementTagNameMap[K]; + } +} + +beforeEach(() => { + HTMLElement.prototype.createEl = function createEl( + this: HTMLElement, + tag: K, + options: { cls?: string; text?: string; attr?: Record } = {}, + ): HTMLElementTagNameMap[K] { + const element = document.createElement(tag); + if (options.cls) element.className = options.cls; + if (options.text) element.textContent = options.text; + for (const [key, value] of Object.entries(options.attr ?? {})) { + element.setAttribute(key, value); + } + this.appendChild(element); + return element; + }; +}); + +describe("view DOM helpers", () => { + it("uses short signatures instead of embedding full text in data attributes", () => { + const long = "x".repeat(20_000); + expect(shortSignature(long).length).toBeLessThan(12); + expect(shortSignature(long)).toBe(shortSignature(long)); + expect(shortSignature(`${long}y`)).not.toBe(shortSignature(long)); + }); + + it("renders wiki links and opens the target on click", () => { + const parent = document.createElement("div"); + const openLink = vi.fn(); + + renderTextWithWikiLinks(parent, "See [[Target Note|label]] and [[Other]].", openLink); + + const links = parent.querySelectorAll("a.internal-link"); + expect(links).toHaveLength(2); + expect(links[0].textContent).toBe("label"); + expect(links[0].getAttribute("href")).toBe("Target Note"); + + links[0].click(); + expect(openLink).toHaveBeenCalledWith("Target Note"); + }); +}); diff --git a/tests/view-renderers.test.ts b/tests/view-renderers.test.ts new file mode 100644 index 00000000..ba03b2d3 --- /dev/null +++ b/tests/view-renderers.test.ts @@ -0,0 +1,1169 @@ +// @vitest-environment jsdom + +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import type { PendingUserInput } from "../src/user-input/model"; +import { + renderComposerShell, + renderComposerSuggestions, + scrollComposerSuggestionIntoView, + syncComposerControls, + syncComposerHeight, +} from "../src/view/composer"; +import { renderPendingRequestMessage } from "../src/view/pending-request-message"; +import { renderToolbar, toolbarSignature, type ToolbarViewModel } from "../src/view/toolbar"; +import { displayItemSignature } from "../src/display/signature"; +import { messageRenderBlocks } from "../src/view/message-stream"; + +declare global { + function createDiv(options?: { cls?: string; text?: string; attr?: Record }): HTMLDivElement; + + interface HTMLElement { + addClass(className: string): void; + createDiv(options?: { cls?: string; text?: string; attr?: Record }): HTMLDivElement; + createEl( + tag: K, + options?: { cls?: string; text?: string; attr?: Record }, + ): HTMLElementTagNameMap[K]; + createSpan(options?: { cls?: string; text?: string; attr?: Record }): HTMLSpanElement; + empty(): void; + hide(): void; + setAttr(name: string, value: string): void; + show(): void; + } +} + +beforeEach(() => { + HTMLElement.prototype.addClass = function addClass(this: HTMLElement, className: string): void { + this.classList.add(...className.split(/\s+/).filter(Boolean)); + }; + HTMLElement.prototype.createEl = function createEl( + this: HTMLElement, + tag: K, + options: { cls?: string; text?: string; attr?: Record } = {}, + ): HTMLElementTagNameMap[K] { + const element = document.createElement(tag); + if (options.cls) element.className = options.cls; + if (options.text) element.textContent = options.text; + for (const [key, value] of Object.entries(options.attr ?? {})) { + element.setAttribute(key, value); + } + this.appendChild(element); + return element; + }; + HTMLElement.prototype.createDiv = function createDiv( + this: HTMLElement, + options: { cls?: string; text?: string; attr?: Record } = {}, + ): HTMLDivElement { + return this.createEl("div", options); + }; + HTMLElement.prototype.createSpan = function createSpan( + this: HTMLElement, + options: { cls?: string; text?: string; attr?: Record } = {}, + ): HTMLSpanElement { + return this.createEl("span", options); + }; + HTMLElement.prototype.empty = function empty(this: HTMLElement): void { + this.replaceChildren(); + }; + HTMLElement.prototype.hide = function hide(this: HTMLElement): void { + this.style.display = "none"; + }; + HTMLElement.prototype.show = function show(this: HTMLElement): void { + this.style.display = ""; + }; + HTMLElement.prototype.setAttr = function setAttr(this: HTMLElement, name: string, value: string): void { + this.setAttribute(name, value); + }; + HTMLElement.prototype.scrollIntoView = vi.fn(); + globalThis.createDiv = ((options: { cls?: string; text?: string; attr?: Record } = {}) => + document.body.createDiv(options)) as typeof globalThis.createDiv; +}); + +function topLevelDetailsSummaries(element: HTMLElement): Array { + const candidates = [element, ...element.children]; + return candidates + .filter((child): child is HTMLDetailsElement => child instanceof HTMLDetailsElement) + .map((details) => details.querySelector("summary")?.textContent ?? null); +} + +function composerSuggestionScrollFixture(metrics: { clientHeight: number; optionHeight: number; optionTop: number; scrollTop: number }): { + container: HTMLElement; + option: HTMLElement; +} { + const container = document.createElement("div"); + const option = container.createDiv(); + + Object.defineProperty(container, "clientHeight", { value: metrics.clientHeight, configurable: true }); + Object.defineProperty(option, "offsetHeight", { value: metrics.optionHeight, configurable: true }); + Object.defineProperty(option, "offsetTop", { value: metrics.optionTop, configurable: true }); + container.scrollTop = metrics.scrollTop; + + return { container, option }; +} + +describe("view renderers", () => { + it("invalidates message blocks when markdown rendering mode changes", () => { + const context = { busy: false, activeTurnId: null, displayItems: [] }; + const streamed = { + id: "a1", + itemId: "a1", + kind: "message", + role: "assistant", + text: "**done**", + markdown: false, + } as const; + const completed = { ...streamed, markdown: true }; + + expect(displayItemSignature(streamed, context)).not.toBe(displayItemSignature(completed, context)); + }); + + it("renders review result items as compact auto-review tool rows", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: null, + historyCursor: null, + loadingHistory: false, + busy: false, + displayItems: [{ id: "review-1", kind: "reviewResult", role: "tool", text: "Auto-review denied this command.", markdown: false }], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.classList.contains("codex-panel__message--review-result")).toBe(true); + expect(element.classList.contains("codex-panel__tool-result--plain")).toBe(true); + expect(element.querySelector(".codex-panel__message-role")?.textContent).toBe("auto-review"); + expect(element.textContent).toContain("Auto-review denied this command."); + expect(element.querySelector("details")).toBeNull(); + }); + + it("renders review result details inside one auto-review details block", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: false, + displayItems: [ + { + id: "review-1", + kind: "reviewResult", + role: "tool", + text: "Auto-review approved: npm test", + turnId: "turn", + markdown: false, + state: "completed", + details: [ + { + title: "Review", + rows: [ + { key: "status", value: "approved" }, + { key: "action", value: "apply patch" }, + { key: "files", value: "src/display/tool-view.ts\nsrc/view/message-stream.ts" }, + ], + }, + ], + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.classList.contains("codex-panel__execution--completed")).toBe(true); + expect(topLevelDetailsSummaries(element)).toEqual(["auto-review"]); + expect([...element.querySelectorAll("details summary")].map((summary) => summary.textContent)).toEqual(["auto-review"]); + expect(element.textContent).not.toContain("Details"); + expect(element.textContent).not.toContain("▶Review"); + expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain("statusapproved"); + expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain("actionapply patch"); + expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain( + "filessrc/display/tool-view.ts\nsrc/view/message-stream.ts", + ); + expect([...element.querySelectorAll(".codex-panel__output-title")].map((title) => title.textContent)).toEqual([]); + }); + + it("renders command items as a compact summary with output behind details", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "cmd-1", + kind: "command", + role: "tool", + text: "npm run check (exit 1)", + turnId: "turn", + command: "npm run check", + cwd: "/vault", + status: "failed", + exitCode: 1, + output: "stderr details", + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.querySelector(".codex-panel__tool-summary")?.textContent).toBe("npm run check (exit 1)"); + expect(element.querySelector(".codex-panel__tool-summary")?.getAttribute("title")).toBe("npm run check (exit 1)"); + expect(topLevelDetailsSummaries(element)).toEqual(["command"]); + expect([...element.querySelectorAll("details summary")].map((summary) => summary.textContent)).toEqual(["command"]); + expect(element.textContent).not.toContain("Details"); + expect([...element.querySelectorAll(".codex-panel__output-title")].map((title) => title.textContent)).toEqual(["Output"]); + expect(element.querySelector(".codex-panel__output pre")?.textContent).toBe("stderr details"); + expect(element.querySelector("details")?.hasAttribute("open")).toBe(false); + }); + + it("uses read as the command header for parsed file reads", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "cmd-1", + kind: "command", + role: "tool", + actionLabel: "read", + text: "sed /vault/src/main.ts", + turnId: "turn", + command: "sed -n '1,20p' src/main.ts", + cwd: "/vault", + status: "completed", + exitCode: 0, + output: "contents", + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect([...element.querySelectorAll("details summary")].map((summary) => summary.textContent)).toEqual(["read"]); + }); + + it("renders file diffs inside a single file change details block", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + workspaceRoot: "/vault/project", + displayItems: [ + { + id: "patch-1", + kind: "fileChange", + role: "tool", + text: "/vault/project/src/main.ts", + turnId: "turn", + status: "completed", + changes: [{ kind: "update", path: "/vault/project/src/main.ts", diff: "@@\n-old\n+new" }], + output: "patch applied", + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.querySelector(".codex-panel__tool-summary")?.textContent).toBe("src/main.ts"); + expect(topLevelDetailsSummaries(element)).toEqual(["file change"]); + expect([...element.querySelectorAll("details summary")].map((summary) => summary.textContent)).toEqual(["file change"]); + expect(element.textContent).not.toContain("Details"); + expect([...element.querySelectorAll(".codex-panel__output-title")].map((title) => title.textContent)).toEqual([ + "update src/main.ts", + "Patch output", + ]); + }); + + it("renders generic tool details as visible sections inside one details block", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "tool-1", + kind: "tool", + role: "tool", + text: "123", + toolLabel: "github.pull_request_read", + turnId: "turn", + status: "completed", + details: [ + { title: "Arguments JSON", body: '{\n "id": 123\n}' }, + { title: "Result JSON", body: '{\n "ok": true\n}' }, + ], + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.querySelector(".codex-panel__tool-summary")?.textContent).toBe("123"); + expect(topLevelDetailsSummaries(element)).toEqual(["github.pull_request_read"]); + expect([...element.querySelectorAll("details summary")].map((summary) => summary.textContent)).toEqual(["github.pull_request_read"]); + expect(element.textContent).not.toContain("Details"); + expect([...element.querySelectorAll(".codex-panel__output-title")].map((title) => title.textContent)).toEqual([ + "Arguments JSON", + "Result JSON", + ]); + }); + + it("keeps the tool summary as a separate row when details are open", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "cmd-1", + kind: "command", + role: "tool", + text: "npm run check", + turnId: "turn", + command: "npm run check", + cwd: "/vault", + status: "completed", + exitCode: 0, + output: "ok", + }, + ], + openDetails: new Set(["cmd-1:command-details"]), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.classList.contains("is-open")).toBe(true); + expect(element.querySelector("details")?.hasAttribute("open")).toBe(true); + expect(topLevelDetailsSummaries(element)).toEqual(["command"]); + expect(element.querySelector(":scope > .codex-panel__tool-summary")?.textContent).toBe("npm run check"); + }); + + it("renders generic tools without details as two compact rows", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "tool-plain", + kind: "tool", + role: "tool", + text: "https://example.com", + toolLabel: "web search", + turnId: "turn", + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.classList.contains("codex-panel__tool-result--plain")).toBe(true); + expect(element.querySelector(".codex-panel__tool-result-header")?.textContent).toBe("web search"); + expect(element.querySelector(".codex-panel__tool-summary")?.textContent).toBe("https://example.com"); + }); + + it("renders hook metadata as rows inside one details block", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "hook-1", + kind: "hook", + role: "tool", + text: "postToolUse: Formatted 1 file.", + toolLabel: "hook", + turnId: "turn", + status: "completed", + details: [ + { + rows: [ + { key: "status", value: "completed" }, + { key: "event", value: "postToolUse" }, + { key: "message", value: "Formatted 1 file." }, + { key: "duration", value: "1ms" }, + ], + }, + { title: "Hook output", body: "feedback: ok" }, + ], + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(topLevelDetailsSummaries(element)).toEqual(["hook"]); + expect([...element.querySelectorAll("details summary")].map((summary) => summary.textContent)).toEqual(["hook"]); + expect(element.querySelector(".codex-panel__tool-summary")?.textContent).toBe("postToolUse: Formatted 1 file."); + expect(element.textContent).not.toContain("Details"); + expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain("statuscompleted"); + expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain("eventpostToolUse"); + expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain("messageFormatted 1 file."); + expect([...element.querySelectorAll(".codex-panel__output-title")].map((title) => title.textContent)).toEqual(["Hook output"]); + expect(element.querySelector(".codex-panel__output pre")?.textContent).toBe("feedback: ok"); + }); + + it("renders task progress items as a dedicated task list", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "plan-progress-turn", + kind: "taskProgress", + role: "tool", + text: "Plan\n[>] Patch UI", + turnId: "turn", + explanation: "Plan", + steps: [ + { step: "Inspect code", status: "completed" }, + { step: "Patch UI", status: "inProgress" }, + ], + status: "inProgress", + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.classList.contains("codex-panel__work-message")).toBe(true); + expect(element.classList.contains("codex-panel__task-progress")).toBe(true); + expect(element.querySelector(".codex-panel__message-role")?.textContent).toBe("tasks"); + expect(element.textContent).toContain("[x]Inspect code"); + expect(element.textContent).toContain("[>]Patch UI"); + }); + + it("renders agent activity with target state and prompt details", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "agent-1", + kind: "agent", + role: "tool", + text: "Spawn agent", + turnId: "turn", + tool: "spawnAgent", + status: "completed", + senderThreadId: "parent", + receiverThreadIds: ["child"], + prompt: "Inspect the renderer.", + model: "gpt-5.5", + reasoningEffort: "high", + agents: [{ threadId: "child", status: "completed", message: "Done" }], + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.classList.contains("codex-panel__work-message")).toBe(true); + expect(element.classList.contains("codex-panel__agent-activity")).toBe(true); + expect(element.querySelector(".codex-panel__message-role")?.textContent).toBe("agent"); + expect(element.querySelector(".codex-panel__tool-summary")?.textContent).toBe("spawn child (completed)"); + expect(element.textContent).toContain("child"); + expect(element.textContent).toContain("completed: Done"); + expect([...element.querySelectorAll("details summary")].map((summary) => summary.textContent)).toEqual(["Details", "Prompt"]); + }); + + it("collapses long agent output away from the agent status row", () => { + const longMessage = `Done\n${"a".repeat(180)}`; + const threadId = "019e061e-0046-7653-a362-86de9a47cb5c"; + const onDetailsToggle = vi.fn(); + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "agent-1", + kind: "agent", + role: "tool", + text: "Wait for agent", + turnId: "turn", + tool: "wait", + status: "completed", + senderThreadId: "parent", + receiverThreadIds: [threadId], + prompt: null, + model: null, + reasoningEffort: null, + agents: [{ threadId, status: "completed", message: longMessage }], + }, + ], + openDetails: new Set(), + onDetailsToggle, + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.querySelector(".codex-panel__agent-thread")?.textContent).toBe("019e061e"); + expect(element.querySelector(".codex-panel__agent-status")?.textContent).toBe("completed: Done"); + expect(element.querySelector(".codex-panel__agent-status")?.textContent).not.toContain("a".repeat(180)); + expect([...element.querySelectorAll("details summary")].map((summary) => summary.textContent)).toEqual([ + "Details", + "Agent output 019e061e", + ]); + const details = element.querySelector("details"); + expect(details?.hasAttribute("open")).toBe(false); + details?.dispatchEvent(new Event("toggle")); + expect(onDetailsToggle).toHaveBeenCalled(); + }); + + it("renders a compact live agent summary while subagents are running", () => { + const blocks = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "agent-1", + kind: "agent", + role: "tool", + text: "Wait for agent", + turnId: "turn", + tool: "wait", + status: "running", + senderThreadId: "parent", + receiverThreadIds: ["done", "running"], + prompt: null, + model: null, + reasoningEffort: null, + agents: [ + { threadId: "done", status: "completed", message: null }, + { threadId: "running", status: "running", message: "Inspecting renderer" }, + ], + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + }); + + const summary = blocks.at(-1)?.render(); + + expect(summary?.classList.contains("codex-panel__work-message")).toBe(true); + expect(summary?.classList.contains("codex-panel__agent-summary")).toBe(true); + expect(summary?.textContent).toContain("agents"); + expect(summary?.textContent).toContain("Agents 1 running, 1 done"); + expect(summary?.textContent).toContain("runningrunning: Inspecting renderer"); + expect(summary?.textContent).toContain("donecompleted"); + }); + + it("hides the live agent summary once all subagents are complete", () => { + const blocks = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "agent-1", + kind: "agent", + role: "tool", + text: "Wait for agent", + turnId: "turn", + tool: "wait", + status: "completed", + senderThreadId: "parent", + receiverThreadIds: ["done"], + prompt: null, + model: null, + reasoningEffort: null, + agents: [{ threadId: "done", status: "completed", message: null }], + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + }); + + expect(blocks.some((block) => block.key.startsWith("active-agents:"))).toBe(false); + }); + + it("marks the live agent summary failed when any subagent fails", () => { + const blocks = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: true, + displayItems: [ + { + id: "agent-1", + kind: "agent", + role: "tool", + text: "Wait for agent", + turnId: "turn", + tool: "wait", + status: "completed", + senderThreadId: "parent", + receiverThreadIds: ["failed", "running"], + prompt: null, + model: null, + reasoningEffort: null, + agents: [ + { threadId: "failed", status: "errored", message: "Failed" }, + { threadId: "running", status: "running", message: null }, + ], + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + }); + + const summary = blocks.at(-1)?.render(); + + expect(summary?.classList.contains("codex-panel__execution--failed")).toBe(true); + expect(summary?.textContent).toContain("Agents 1 failed, 1 running"); + expect(summary?.textContent).toContain("failederrored: Failed"); + }); + + it("renders toolbar controls as buttons and signatures include live status/catalog state", () => { + const parent = document.createElement("div"); + const toggleHistory = vi.fn(); + const baseModel = toolbarModel(); + + renderToolbar(parent, baseModel, toolbarActions({ toggleHistory })); + + const statusButton = parent.querySelector(".codex-panel__status-dot"); + expect(statusButton?.tagName).toBe("BUTTON"); + expect(statusButton?.getAttribute("role")).toBeNull(); + parent.querySelector(".codex-panel__history-toggle")?.click(); + expect(toggleHistory).toHaveBeenCalled(); + + expect(toolbarSignature(baseModel)).not.toBe(toolbarSignature({ ...baseModel, status: "Turn running..." })); + expect(toolbarSignature(baseModel)).not.toBe( + toolbarSignature({ ...baseModel, effortChoices: [...baseModel.effortChoices, { label: "xhigh", onClick: vi.fn() }] }), + ); + expect(toolbarSignature(baseModel)).not.toBe( + toolbarSignature({ ...baseModel, diagnostics: [{ label: "compatibility", value: "model/list failed", level: "error" }] }), + ); + expect(toolbarSignature(baseModel)).not.toBe( + toolbarSignature({ + ...baseModel, + rateLimit: { + title: "Codex: 5h 42%", + level: "ok", + rows: [{ label: "5h", value: "42%", resetLabel: "reset in 2h", title: "Codex 5h: 42% used.", percent: 42, level: "ok" }], + }, + }), + ); + }); + + it("renders context as a compact meter and Codex limits only in the status menu", () => { + const parent = document.createElement("div"); + + renderToolbar( + parent, + toolbarModel({ + statusPanelOpen: true, + openPanel: "status", + context: { label: "12%", title: "Context: 12%.", percent: 12, level: "ok" }, + rateLimit: { + title: "Codex: 5h 42%, 1w 21%", + level: "ok", + rows: [ + { + label: "5h", + value: "42%", + resetLabel: "reset in 2h", + title: "Codex 5h: 42% used.", + percent: 42, + level: "ok", + }, + { + label: "1w", + value: "21%", + resetLabel: "reset in 3d 4h", + title: "Codex 1w: 21% used.", + percent: 21, + level: "ok", + }, + ], + }, + }), + toolbarActions(), + ); + + expect(parent.querySelector(".codex-panel__context-compact")?.textContent).toContain("12%"); + expect(parent.querySelector(".codex-panel__limit-compact")).toBeNull(); + expect(parent.querySelector(".codex-panel__limit-panel")?.textContent).toContain("5h"); + expect(parent.querySelector(".codex-panel__limit-panel")?.textContent).toContain("42%"); + expect(parent.querySelector(".codex-panel__limit-panel")?.textContent).toContain("reset in 2h"); + expect(parent.querySelector(".codex-panel__limit-panel")?.textContent).toContain("1w"); + expect(parent.querySelector(".codex-panel__limit-panel")?.textContent).toContain("21%"); + expect(parent.querySelector(".codex-panel__limit-panel")?.textContent).toContain("reset in 3d 4h"); + }); + + it("renders connection diagnostics in the status menu", () => { + const parent = document.createElement("div"); + const openCodexConfigFolder = vi.fn(); + + renderToolbar( + parent, + toolbarModel({ + statusPanelOpen: true, + openPanel: "status", + diagnostics: [ + { label: "running app-server", value: "codex-cli/1.2.3" }, + { label: "compatibility", value: "model/list failed", level: "error" }, + ], + }), + toolbarActions({ openCodexConfigFolder }), + ); + + parent.querySelectorAll(".codex-panel__status-panel-item")[2]?.click(); + expect(openCodexConfigFolder).toHaveBeenCalled(); + expect(parent.textContent).toContain("Open Codex config folder"); + expect(parent.querySelector(".codex-panel__connection-diagnostics-title")?.textContent).toBe("Connection diagnostics"); + expect(parent.textContent).toContain("Effective Codex config"); + expect(parent.textContent).toContain("codex-cli/1.2.3"); + expect(parent.querySelector(".codex-panel__connection-diagnostics-row--error")?.textContent).toContain("model/list failed"); + }); + + it("renders effective config inside the status menu without a separate toggle", () => { + const parent = document.createElement("div"); + + renderToolbar( + parent, + toolbarModel({ + statusPanelOpen: true, + openPanel: "status", + configSections: [{ title: "Runtime", rows: [{ key: "model", value: "gpt-5.5" }] }], + }), + toolbarActions(), + ); + + expect(parent.querySelector(".codex-panel__slot--config")).toBeNull(); + expect(parent.querySelector(".codex-panel__toolbar-panel .codex-panel__config")?.textContent).toContain("Effective Codex config"); + expect(parent.textContent).not.toContain("Show effective config"); + expect(parent.textContent).not.toContain("Hide effective config"); + expect(parent.textContent).toContain("gpt-5.5"); + }); + + it("renders chat history rename actions and an inline rename editor", () => { + const parent = document.createElement("div"); + const startRenameThread = vi.fn(); + const updateRenameDraft = vi.fn(); + const saveRenameThread = vi.fn(); + const cancelRenameThread = vi.fn(); + const autoNameThread = vi.fn(); + + renderToolbar( + parent, + toolbarModel({ + historyOpen: true, + openPanel: "history", + threads: [ + { title: "Thread", threadId: "thread", selected: true, disabled: false, canArchive: true, rename: null }, + { + title: "Editing", + threadId: "editing", + selected: false, + disabled: false, + canArchive: true, + rename: { draft: "Draft title", generating: false }, + }, + ], + }), + toolbarActions({ startRenameThread, updateRenameDraft, saveRenameThread, cancelRenameThread, autoNameThread }), + ); + + parent.querySelector('[aria-label="Rename thread"]')?.click(); + expect(startRenameThread).toHaveBeenCalledWith("thread"); + + const input = parent.querySelector(".codex-panel__thread-rename-input"); + expect(input?.value).toBe("Draft title"); + input!.value = "New title"; + input!.dispatchEvent(new Event("input", { bubbles: true })); + expect(updateRenameDraft).toHaveBeenCalledWith("editing", "New title"); + + parent.querySelector('[aria-label="Save thread name"]')?.click(); + expect(saveRenameThread).toHaveBeenCalledWith("editing", "New title"); + parent.querySelector('[aria-label="Cancel rename"]')?.click(); + expect(cancelRenameThread).toHaveBeenCalledWith("editing"); + parent.querySelector('[aria-label="Auto-name thread"]')?.click(); + expect(autoNameThread).toHaveBeenCalledWith("editing"); + }); + + it("renders auto-name loading without disabling the rename draft field", () => { + const parent = document.createElement("div"); + + renderToolbar( + parent, + toolbarModel({ + historyOpen: true, + openPanel: "history", + threads: [ + { + title: "Editing", + threadId: "editing", + selected: false, + disabled: false, + canArchive: true, + rename: { draft: "Draft title", generating: true }, + }, + ], + }), + toolbarActions(), + ); + + expect(parent.querySelector(".codex-panel__thread-rename-input")?.disabled).toBe(false); + expect(parent.querySelector('[aria-label="Save thread name"]')?.disabled).toBe(true); + expect(parent.querySelector('[aria-label="Auto-name thread"]')?.disabled).toBe(true); + expect(parent.querySelector('[aria-label="Cancel rename"]')?.disabled).toBe(false); + }); + + it("renders pending requests as one message-stream block and keeps user input drafts live", () => { + const parent = document.createElement("div"); + const drafts = new Map(); + const resolveUserInput = vi.fn(); + const input = pendingUserInput(); + + renderPendingRequestMessage( + parent, + [], + [input], + { + values: drafts, + draftKey: (requestId, questionId) => `${String(requestId)}:${questionId}`, + otherDraftKey: (requestId, questionId) => `${String(requestId)}:${questionId}:other`, + }, + new Set(), + { resolveApproval: vi.fn(), resolveUserInput, cancelUserInput: vi.fn() }, + ); + + expect(parent.querySelectorAll(".codex-panel__pending-request-message")).toHaveLength(1); + expect(parent.querySelector(".codex-panel__pending-request-message")?.classList.contains("codex-panel__work-message")).toBe(true); + expect(parent.querySelector(".codex-panel__pending-request-message")?.classList.contains("codex-panel__work-message--warning")).toBe( + true, + ); + expect(parent.querySelector(".codex-panel__pending-request-button.mod-cta")?.textContent).toBe("Submit"); + expect(parent.querySelector(".codex-panel__user-input-answer .codex-panel__user-input-radio")).not.toBeNull(); + expect(parent.querySelector(".codex-panel__user-input-radio")?.checked).toBe(true); + expect(parent.querySelector(".codex-panel__user-input-radio")?.type).toBe("radio"); + expect(parent.querySelector(".codex-panel__user-input-marker")).toBeNull(); + parent.querySelector(".mod-cta")?.click(); + expect(resolveUserInput).toHaveBeenCalledWith(input); + }); + + it("renders submitted user input separately from approvals", () => { + const block = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: "turn", + historyCursor: null, + loadingHistory: false, + busy: false, + displayItems: [ + { + id: "user-input-submitted-1", + kind: "userInputResult", + role: "tool", + text: "Input submitted for 1 question.", + turnId: "turn", + markdown: false, + state: "completed", + details: [{ title: "Scope", rows: [{ key: "answer", value: "Narrow" }] }], + }, + ], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + })[0]; + + const element = block.render(); + + expect(element.querySelector(".codex-panel__message-role")?.textContent).toBe("Input"); + expect(element.textContent).not.toContain("Approval"); + expect(element.querySelector("details summary")?.textContent).toBe("Scope"); + expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain("answerNarrow"); + }); + + it("adds pending requests to the bottom of message render blocks", () => { + const pending = document.createElement("div"); + pending.className = "codex-panel__pending-request-message"; + pending.textContent = "Request"; + + const blocks = messageRenderBlocks({ + activeThreadId: "thread", + activeTurnId: null, + historyCursor: null, + loadingHistory: false, + busy: false, + displayItems: [{ id: "a1", kind: "message", role: "assistant", text: "Done", markdown: true }], + openDetails: new Set(), + loadOlderTurns: vi.fn(), + renderMarkdown: (parent, text) => parent.createDiv({ text }), + renderTextWithWikiLinks: (parent, text) => parent.createDiv({ text }), + pendingRequestsSignature: "request:1", + renderPendingRequests: () => pending, + }); + + expect(blocks.map((block) => block.key)).toEqual(["item:a1", "pending-requests"]); + expect(blocks[1].render()).toBe(pending); + }); + + it("renders composer suggestions outside normal input flow callbacks", () => { + const parent = document.createElement("div"); + const onSuggestionInsert = vi.fn(); + const { composer, suggestions } = renderComposerShell(parent, "view", "", false, { + onInput: vi.fn(), + onUpdateSuggestions: vi.fn(), + onKeydown: vi.fn(), + onNewThread: vi.fn(), + onSendOrInterrupt: vi.fn(), + onSuggestionHover: vi.fn(), + onSuggestionInsert, + }); + + renderComposerSuggestions( + suggestions, + composer, + "view", + [{ display: "/help", detail: "Show help", replacement: "/help", start: 0 }], + 0, + { onSuggestionHover: vi.fn(), onSuggestionInsert }, + ); + + expect(suggestions.getAttribute("role")).toBe("listbox"); + expect(composer.getAttribute("aria-expanded")).toBe("true"); + suggestions + .querySelector(".codex-panel__composer-suggestion") + ?.dispatchEvent(new MouseEvent("mousedown", { bubbles: true })); + expect(onSuggestionInsert).toHaveBeenCalled(); + }); + + it("scrolls the selected composer suggestion fully into view below the viewport", () => { + const { container, option } = composerSuggestionScrollFixture({ + clientHeight: 100, + optionHeight: 32, + optionTop: 92, + scrollTop: 0, + }); + + scrollComposerSuggestionIntoView(container, option); + + expect(container.scrollTop).toBe(24); + }); + + it("scrolls the selected composer suggestion fully into view above the viewport", () => { + const { container, option } = composerSuggestionScrollFixture({ + clientHeight: 100, + optionHeight: 32, + optionTop: 48, + scrollTop: 64, + }); + + scrollComposerSuggestionIntoView(container, option); + + expect(container.scrollTop).toBe(48); + }); + + it("keeps composer suggestion scroll position when the selected item is already visible", () => { + const { container, option } = composerSuggestionScrollFixture({ + clientHeight: 100, + optionHeight: 32, + optionTop: 72, + scrollTop: 48, + }); + + scrollComposerSuggestionIntoView(container, option); + + expect(container.scrollTop).toBe(48); + }); + + it("uses the composer action for interrupt only when a running turn has no steering text", () => { + const parent = document.createElement("div"); + const { composer } = renderComposerShell(parent, "view", "", true, { + onInput: vi.fn(), + onUpdateSuggestions: vi.fn(), + onKeydown: vi.fn(), + onNewThread: vi.fn(), + onSendOrInterrupt: vi.fn(), + onSuggestionHover: vi.fn(), + onSuggestionInsert: vi.fn(), + }); + const sendButton = parent.querySelector(".codex-panel__send"); + + syncComposerControls(parent, composer, true, true); + expect(sendButton?.getAttribute("aria-label")).toBe("Interrupt"); + expect(sendButton?.classList.contains("is-interrupt")).toBe(true); + expect(sendButton?.classList.contains("is-steer")).toBe(false); + expect(sendButton?.dataset.icon).toBe("square"); + + composer.value = "adjust course"; + syncComposerControls(parent, composer, true, true); + expect(sendButton?.getAttribute("aria-label")).toBe("Steer"); + expect(sendButton?.classList.contains("is-interrupt")).toBe(false); + expect(sendButton?.classList.contains("is-steer")).toBe(true); + expect(sendButton?.dataset.icon).toBe("corner-down-right"); + }); + + it("honors the smaller viewport branch of the composer max-height CSS", () => { + const composer = document.createElement("textarea"); + const getComputedStyleMock = vi.spyOn(window, "getComputedStyle").mockReturnValue({ + minHeight: "76px", + maxHeight: "min(208px, 40vh)", + } as CSSStyleDeclaration); + Object.defineProperty(window, "innerHeight", { value: 400, configurable: true }); + Object.defineProperty(composer, "scrollHeight", { value: 280, configurable: true }); + + try { + syncComposerHeight(composer); + } finally { + getComputedStyleMock.mockRestore(); + } + + expect(composer.style.height).toBe("160px"); + expect(composer.style.overflowY).toBe("auto"); + }); +}); + +function toolbarModel(overrides: Partial = {}): ToolbarViewModel { + return { + connected: true, + status: "Connected.", + statusState: "connected", + historyOpen: false, + statusPanelOpen: false, + runtimeOpen: true, + planActive: false, + fastActive: false, + runtimeSummary: "5.5 high", + runtimeTitle: "Model: gpt-5.5; Effort: high", + runtimeAriaLabel: "Runtime: gpt-5.5 high", + runtimeEmphasized: false, + context: null, + rateLimit: null, + configSections: [], + openPanel: "runtime", + threads: [{ title: "Thread", threadId: "thread", selected: true, disabled: false, canArchive: true, rename: null }], + modelChoices: [{ label: "Default", selected: true, onClick: vi.fn() }], + effortChoices: [{ label: "Default", selected: true, onClick: vi.fn() }], + connectLabel: "Reconnect", + diagnostics: [{ label: "running app-server", value: "codex-cli/test" }], + ...overrides, + }; +} + +function toolbarActions(overrides: Partial[2]> = {}): Parameters[2] { + return { + toggleHistory: vi.fn(), + toggleStatusPanel: vi.fn(), + togglePlan: vi.fn(), + toggleFast: vi.fn(), + toggleRuntime: vi.fn(), + connect: vi.fn(), + openCodexConfigFolder: vi.fn(), + refreshThreads: vi.fn(), + resumeThread: vi.fn(), + archiveThread: vi.fn(), + startRenameThread: vi.fn(), + updateRenameDraft: vi.fn(), + saveRenameThread: vi.fn(), + cancelRenameThread: vi.fn(), + autoNameThread: vi.fn(), + ...overrides, + }; +} + +function pendingUserInput(): PendingUserInput { + return { + requestId: 99, + method: "item/tool/requestUserInput", + params: { + threadId: "thread", + turnId: "turn", + itemId: "input", + questions: [ + { + id: "scope", + header: "Scope", + question: "How broad?", + isOther: false, + isSecret: false, + options: [{ label: "Narrow", description: "Small change" }], + }, + ], + }, + }; +} diff --git a/tests/view-scroll.test.ts b/tests/view-scroll.test.ts new file mode 100644 index 00000000..81ded9d2 --- /dev/null +++ b/tests/view-scroll.test.ts @@ -0,0 +1,74 @@ +// @vitest-environment jsdom + +import { describe, expect, it } from "vitest"; + +import { bottomScrollTop, captureScrollAnchor, isNearScrollBottom, restoreScrollAnchor } from "../src/view/scroll"; + +describe("message scroll helpers", () => { + it("detects whether the transcript is pinned near the bottom", () => { + expect(isNearScrollBottom({ scrollHeight: 1000, scrollTop: 620, clientHeight: 320 })).toBe(true); + expect(isNearScrollBottom({ scrollHeight: 1000, scrollTop: 500, clientHeight: 320 })).toBe(false); + }); + + it("uses the full scroll height for bottom pinning", () => { + expect(bottomScrollTop({ scrollHeight: 1200 })).toBe(1200); + }); + + it("captures and restores the first visible message block", () => { + const container = messageContainer({ scrollTop: 150, scrollHeight: 1000, clientHeight: 300 }); + const first = messageBlock("first", 0, 120); + const second = messageBlock("second", 120, 160); + container.append(first, second); + + const anchor = captureScrollAnchor(container); + + expect(anchor).toEqual({ key: "second", offset: -30, fallbackTop: 150 }); + + setLayout(second, 360, 160); + restoreScrollAnchor(container, anchor); + + expect(container.scrollTop).toBe(390); + }); + + it("falls back to the previous scroll top when the anchor block disappears", () => { + const container = messageContainer({ scrollTop: 150, scrollHeight: 1000, clientHeight: 300 }); + container.append(messageBlock("first", 0, 120), messageBlock("second", 120, 160)); + const anchor = captureScrollAnchor(container); + container.replaceChildren(messageBlock("other", 0, 120)); + + restoreScrollAnchor(container, anchor); + + expect(container.scrollTop).toBe(150); + }); +}); + +function messageContainer(metrics: { scrollTop: number; scrollHeight: number; clientHeight: number }): HTMLElement { + const container = document.createElement("div"); + let scrollTop = metrics.scrollTop; + Object.defineProperties(container, { + scrollTop: { + get: () => scrollTop, + set: (value: number) => { + scrollTop = value; + }, + configurable: true, + }, + scrollHeight: { value: metrics.scrollHeight, configurable: true }, + clientHeight: { value: metrics.clientHeight, configurable: true }, + }); + return container; +} + +function messageBlock(key: string, offsetTop: number, offsetHeight: number): HTMLElement { + const element = document.createElement("div"); + element.setAttribute("data-codex-panel-block-key", key); + setLayout(element, offsetTop, offsetHeight); + return element; +} + +function setLayout(element: HTMLElement, offsetTop: number, offsetHeight: number): void { + Object.defineProperties(element, { + offsetTop: { value: offsetTop, configurable: true }, + offsetHeight: { value: offsetHeight, configurable: true }, + }); +} diff --git a/tests/wikilink-context.test.ts b/tests/wikilink-context.test.ts new file mode 100644 index 00000000..b543cb3b --- /dev/null +++ b/tests/wikilink-context.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from "vitest"; + +import { parsedWikiLinks, userInputWithWikiLinkMentions } from "../src/composer/wikilink-context"; + +describe("wikilink context", () => { + it("parses aliases, subpaths, and duplicate links", () => { + expect(parsedWikiLinks("See [[Alpha|label]], [[Beta#Heading]], [[Gamma^block]], and [[Alpha]].")).toEqual([ + { raw: "Alpha|label", target: "Alpha", subpath: "", display: "label" }, + { raw: "Beta#Heading", target: "Beta", subpath: "#Heading", display: "" }, + { raw: "Gamma^block", target: "Gamma", subpath: "^block", display: "" }, + ]); + }); + + it("adds mentions for resolved wikilinks without changing the visible prompt body", () => { + const text = "Please compare [[Alpha|A]] and [[Missing]]."; + const input = userInputWithWikiLinkMentions(text, (target) => + target === "Alpha" ? { name: "Alpha", path: "thoughts/Alpha.md" } : null, + ); + + expect(input).toEqual([ + { type: "text", text, text_elements: [] }, + { type: "mention", name: "Alpha", path: "thoughts/Alpha.md" }, + ]); + }); + + it("deduplicates mentions by resolved path", () => { + const text = "Read [[Alpha]], [[Alpha#Heading]], and [[Alias|A]]."; + const input = userInputWithWikiLinkMentions(text, (target) => + target === "Alpha" || target === "Alias" ? { name: "Alpha", path: "thoughts/Alpha.md" } : null, + ); + + expect(input).toEqual([ + { type: "text", text, text_elements: [] }, + { type: "mention", name: "Alpha", path: "thoughts/Alpha.md" }, + ]); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..b079aab1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "ignoreDeprecations": "6.0", + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "allowSyntheticDefaultImports": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "types": ["node", "vitest"], + "lib": ["ES2022", "DOM"] + }, + "include": ["src/**/*.ts", "tests/**/*.ts", "vitest.config.ts"] +} diff --git a/versions.json b/versions.json new file mode 100644 index 00000000..708016db --- /dev/null +++ b/versions.json @@ -0,0 +1,3 @@ +{ + "0.1.0": "1.5.0" +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..aae38b3d --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vitest/config"; +import { fileURLToPath } from "node:url"; + +export default defineConfig({ + resolve: { + alias: { + obsidian: fileURLToPath(new URL("./tests/mocks/obsidian.ts", import.meta.url)), + }, + }, + test: { + environment: "node", + globals: false, + }, +});