Compare commits

...

10 Commits

Author SHA1 Message Date
Wenxi Onyx
a49df511e2 dummy commit (noop README change) to fix beta tag on docker 2026-01-31 11:09:41 -08:00
Justin Tahara
d5d2a8a1a6 fix(desktop): Remove Global Shortcuts (#7914) 2026-01-30 13:46:26 -08:00
Justin Tahara
b2f46b264c fix(asana): Workspace Team ID mismatch (#7674) 2026-01-30 13:19:07 -08:00
Jamison Lahman
c6ad363fbd chore(mypy): fix mypy cache issues switching between HEAD and release (#7732) 2026-01-27 15:52:53 -08:00
Jamison Lahman
e313119f9a fix(citations): enable citation sidebar w/ web_search-only assistants (#7888) 2026-01-27 14:50:00 -08:00
Wenxi
3a2a542a03 fix: connector details back button should nav back (#7869) 2026-01-27 14:35:15 -08:00
Yuhong Sun
413aeba4a1 fix: Project Creation (#7851) 2026-01-27 14:34:59 -08:00
Wenxi
46028aa2bb fix: user count check (#7811) 2026-01-27 14:34:29 -08:00
Justin Tahara
454943c4a6 fix(llm): Hide private models from Agent Creation (#7873) 2026-01-27 14:33:40 -08:00
Justin Tahara
87946266de fix(redis): Adding more TTLs (#7886) 2026-01-27 14:32:14 -08:00
25 changed files with 165 additions and 343 deletions

View File

@@ -50,8 +50,9 @@ jobs:
uses: runs-on/cache@50350ad4242587b6c8c2baa2e740b1bc11285ff4 # ratchet:runs-on/cache@v4
with:
path: backend/.mypy_cache
key: mypy-${{ runner.os }}-${{ hashFiles('**/*.py', '**/*.pyi', 'backend/pyproject.toml') }}
key: mypy-${{ runner.os }}-${{ github.base_ref || github.event.merge_group.base_ref || 'main' }}-${{ hashFiles('**/*.py', '**/*.pyi', 'backend/pyproject.toml') }}
restore-keys: |
mypy-${{ runner.os }}-${{ github.base_ref || github.event.merge_group.base_ref || 'main' }}-
mypy-${{ runner.os }}-
- name: Run MyPy

View File

@@ -105,4 +105,4 @@ Join our open source community on **[Discord](https://discord.gg/TDJ59cGV2X)**!
## 💡 Contributing
Looking to contribute? Please check out the [Contribution Guide](CONTRIBUTING.md) for more details.
Looking to contribute? Please check out the [Contribution Guide](CONTRIBUTING.md) for more details!

View File

@@ -21,6 +21,8 @@ from onyx.utils.logger import setup_logger
DOCUMENT_SYNC_PREFIX = "documentsync"
DOCUMENT_SYNC_FENCE_KEY = f"{DOCUMENT_SYNC_PREFIX}_fence"
DOCUMENT_SYNC_TASKSET_KEY = f"{DOCUMENT_SYNC_PREFIX}_taskset"
FENCE_TTL = 7 * 24 * 60 * 60 # 7 days - defensive TTL to prevent memory leaks
TASKSET_TTL = FENCE_TTL
logger = setup_logger()
@@ -50,7 +52,7 @@ def set_document_sync_fence(r: Redis, payload: int | None) -> None:
r.delete(DOCUMENT_SYNC_FENCE_KEY)
return
r.set(DOCUMENT_SYNC_FENCE_KEY, payload)
r.set(DOCUMENT_SYNC_FENCE_KEY, payload, ex=FENCE_TTL)
r.sadd(OnyxRedisConstants.ACTIVE_FENCES, DOCUMENT_SYNC_FENCE_KEY)
@@ -110,6 +112,7 @@ def generate_document_sync_tasks(
# Add to the tracking taskset in Redis BEFORE creating the celery task
r.sadd(DOCUMENT_SYNC_TASKSET_KEY, custom_task_id)
r.expire(DOCUMENT_SYNC_TASKSET_KEY, TASKSET_TTL)
# Create the Celery task
celery_app.send_task(

View File

@@ -25,11 +25,17 @@ class AsanaConnector(LoadConnector, PollConnector):
batch_size: int = INDEX_BATCH_SIZE,
continue_on_failure: bool = CONTINUE_ON_CONNECTOR_FAILURE,
) -> None:
self.workspace_id = asana_workspace_id
self.project_ids_to_index: list[str] | None = (
asana_project_ids.split(",") if asana_project_ids is not None else None
)
self.asana_team_id = asana_team_id
self.workspace_id = asana_workspace_id.strip()
if asana_project_ids:
project_ids = [
project_id.strip()
for project_id in asana_project_ids.split(",")
if project_id.strip()
]
self.project_ids_to_index = project_ids or None
else:
self.project_ids_to_index = None
self.asana_team_id = (asana_team_id.strip() or None) if asana_team_id else None
self.batch_size = batch_size
self.continue_on_failure = continue_on_failure
logger.info(

View File

@@ -32,6 +32,7 @@ class RedisConnectorDelete:
FENCE_PREFIX = f"{PREFIX}_fence" # "connectordeletion_fence"
FENCE_TTL = 7 * 24 * 60 * 60 # 7 days - defensive TTL to prevent memory leaks
TASKSET_PREFIX = f"{PREFIX}_taskset" # "connectordeletion_taskset"
TASKSET_TTL = FENCE_TTL
# used to signal the overall workflow is still active
# it's impossible to get the exact state of the system at a single point in time
@@ -136,6 +137,7 @@ class RedisConnectorDelete:
# add to the tracking taskset in redis BEFORE creating the celery task.
# note that for the moment we are using a single taskset key, not differentiated by cc_pair id
self.redis.sadd(self.taskset_key, custom_task_id)
self.redis.expire(self.taskset_key, self.TASKSET_TTL)
# Priority on sync's triggered by new indexing should be medium
celery_app.send_task(

View File

@@ -45,6 +45,7 @@ class RedisConnectorPrune:
) # connectorpruning_generator_complete
TASKSET_PREFIX = f"{PREFIX}_taskset" # connectorpruning_taskset
TASKSET_TTL = FENCE_TTL
SUBTASK_PREFIX = f"{PREFIX}+sub" # connectorpruning+sub
# used to signal the overall workflow is still active
@@ -184,6 +185,7 @@ class RedisConnectorPrune:
# add to the tracking taskset in redis BEFORE creating the celery task.
self.redis.sadd(self.taskset_key, custom_task_id)
self.redis.expire(self.taskset_key, self.TASKSET_TTL)
# Priority on sync's triggered by new indexing should be medium
result = celery_app.send_task(

View File

@@ -23,6 +23,7 @@ class RedisDocumentSet(RedisObjectHelper):
FENCE_PREFIX = PREFIX + "_fence"
FENCE_TTL = 7 * 24 * 60 * 60 # 7 days - defensive TTL to prevent memory leaks
TASKSET_PREFIX = PREFIX + "_taskset"
TASKSET_TTL = FENCE_TTL
def __init__(self, tenant_id: str, id: int) -> None:
super().__init__(tenant_id, str(id))
@@ -83,6 +84,7 @@ class RedisDocumentSet(RedisObjectHelper):
# add to the set BEFORE creating the task.
redis_client.sadd(self.taskset_key, custom_task_id)
redis_client.expire(self.taskset_key, self.TASKSET_TTL)
celery_app.send_task(
OnyxCeleryTask.VESPA_METADATA_SYNC_TASK,

View File

@@ -24,6 +24,7 @@ class RedisUserGroup(RedisObjectHelper):
FENCE_PREFIX = PREFIX + "_fence"
FENCE_TTL = 7 * 24 * 60 * 60 # 7 days - defensive TTL to prevent memory leaks
TASKSET_PREFIX = PREFIX + "_taskset"
TASKSET_TTL = FENCE_TTL
def __init__(self, tenant_id: str, id: int) -> None:
super().__init__(tenant_id, str(id))
@@ -97,6 +98,7 @@ class RedisUserGroup(RedisObjectHelper):
# add to the set BEFORE creating the task.
redis_client.sadd(self.taskset_key, custom_task_id)
redis_client.expire(self.taskset_key, self.TASKSET_TTL)
celery_app.send_task(
OnyxCeleryTask.VESPA_METADATA_SYNC_TASK,

View File

@@ -47,7 +47,7 @@ class UserFileDeleteResult(BaseModel):
assistant_names: list[str] = []
@router.get("/", tags=PUBLIC_API_TAGS)
@router.get("", tags=PUBLIC_API_TAGS)
def get_projects(
user: User | None = Depends(current_user),
db_session: Session = Depends(get_session),

View File

@@ -10,6 +10,7 @@ from onyx.auth.users import anonymous_user_enabled
from onyx.auth.users import user_needs_to_be_verified
from onyx.configs.app_configs import AUTH_TYPE
from onyx.configs.app_configs import PASSWORD_MIN_LENGTH
from onyx.configs.constants import AuthType
from onyx.configs.constants import DEV_VERSION_PATTERN
from onyx.configs.constants import PUBLIC_API_TAGS
from onyx.configs.constants import STABLE_VERSION_PATTERN
@@ -30,13 +31,20 @@ def healthcheck() -> StatusResponse:
@router.get("/auth/type", tags=PUBLIC_API_TAGS)
async def get_auth_type() -> AuthTypeResponse:
user_count = await get_user_count()
# NOTE: This endpoint is critical for the multi-tenant flow and is hit before there is a tenant context
# The reason is this is used during the login flow, but we don't know which tenant the user is supposed to be
# associated with until they auth.
has_users = True
if AUTH_TYPE != AuthType.CLOUD:
user_count = await get_user_count()
has_users = user_count > 0
return AuthTypeResponse(
auth_type=AUTH_TYPE,
requires_verification=user_needs_to_be_verified(),
anonymous_user_enabled=anonymous_user_enabled(),
password_min_length=PASSWORD_MIN_LENGTH,
has_users=user_count > 0,
has_users=has_users,
)

View File

@@ -410,26 +410,20 @@ def list_llm_provider_basics(
all_providers = fetch_existing_llm_providers(db_session)
user_group_ids = fetch_user_group_ids(db_session, user) if user else set()
is_admin = user and user.role == UserRole.ADMIN
is_admin = user is not None and user.role == UserRole.ADMIN
accessible_providers = []
for provider in all_providers:
# Include all public providers
if provider.is_public:
accessible_providers.append(LLMProviderDescriptor.from_model(provider))
continue
# Include restricted providers user has access to via groups
if is_admin:
# Admins see all providers
accessible_providers.append(LLMProviderDescriptor.from_model(provider))
elif provider.groups:
# User must be in at least one of the provider's groups
if user_group_ids.intersection({g.id for g in provider.groups}):
accessible_providers.append(LLMProviderDescriptor.from_model(provider))
elif not provider.personas:
# No restrictions = accessible
# Use centralized access control logic with persona=None since we're
# listing providers without a specific persona context. This correctly:
# - Includes all public providers
# - Includes providers user can access via group membership
# - Excludes persona-only restricted providers (requires specific persona)
# - Excludes non-public providers with no restrictions (admin-only)
if can_user_access_llm_provider(
provider, user_group_ids, persona=None, is_admin=is_admin
):
accessible_providers.append(LLMProviderDescriptor.from_model(provider))
end_time = datetime.now(timezone.utc)

View File

@@ -31,7 +31,7 @@ class ProjectManager:
) -> List[UserProjectSnapshot]:
"""Get all projects for a user via API."""
response = requests.get(
f"{API_SERVER_URL}/user/projects/",
f"{API_SERVER_URL}/user/projects",
headers=user_performing_action.headers or GENERAL_HEADERS,
)
response.raise_for_status()
@@ -56,7 +56,7 @@ class ProjectManager:
) -> bool:
"""Verify that a project has been deleted by ensuring it's not in list."""
response = requests.get(
f"{API_SERVER_URL}/user/projects/",
f"{API_SERVER_URL}/user/projects",
headers=user_performing_action.headers or GENERAL_HEADERS,
)
response.raise_for_status()

View File

@@ -309,6 +309,63 @@ def test_get_llm_for_persona_falls_back_when_access_denied(
assert fallback_llm.config.model_name == default_provider.default_model_name
def test_list_llm_provider_basics_excludes_non_public_unrestricted(
users: tuple[DATestUser, DATestUser],
) -> None:
"""Test that the /llm/provider endpoint correctly excludes non-public providers
with no group/persona restrictions.
This tests the fix for the bug where non-public providers with no restrictions
were incorrectly shown to all users instead of being admin-only.
"""
admin_user, basic_user = users
# Create a public provider (should be visible to all)
public_provider = LLMProviderManager.create(
name="public-provider",
is_public=True,
set_as_default=True,
user_performing_action=admin_user,
)
# Create a non-public provider with no restrictions (should be admin-only)
non_public_provider = LLMProviderManager.create(
name="non-public-unrestricted",
is_public=False,
groups=[],
personas=[],
set_as_default=False,
user_performing_action=admin_user,
)
# Non-admin user calls the /llm/provider endpoint
response = requests.get(
f"{API_SERVER_URL}/llm/provider",
headers=basic_user.headers,
)
assert response.status_code == 200
providers = response.json()
provider_names = [p["name"] for p in providers]
# Public provider should be visible
assert public_provider.name in provider_names
# Non-public provider with no restrictions should NOT be visible to non-admin
assert non_public_provider.name not in provider_names
# Admin user should see both providers
admin_response = requests.get(
f"{API_SERVER_URL}/llm/provider",
headers=admin_user.headers,
)
assert admin_response.status_code == 200
admin_providers = admin_response.json()
admin_provider_names = [p["name"] for p in admin_providers]
assert public_provider.name in admin_provider_names
assert non_public_provider.name in admin_provider_names
def test_provider_delete_clears_persona_references(reset: None) -> None:
"""Test that deleting a provider automatically clears persona references."""
admin_user = UserManager.create(name="admin_user")

View File

@@ -0,0 +1,50 @@
"""Tests for Asana connector configuration parsing."""
import pytest
from onyx.connectors.asana.connector import AsanaConnector
@pytest.mark.parametrize(
"project_ids,expected",
[
(None, None),
("", None),
(" ", None),
(" 123 ", ["123"]),
(" 123 , , 456 , ", ["123", "456"]),
],
)
def test_asana_connector_project_ids_normalization(
project_ids: str | None, expected: list[str] | None
) -> None:
connector = AsanaConnector(
asana_workspace_id=" 1153293530468850 ",
asana_project_ids=project_ids,
asana_team_id=" 1210918501948021 ",
)
assert connector.workspace_id == "1153293530468850"
assert connector.project_ids_to_index == expected
assert connector.asana_team_id == "1210918501948021"
@pytest.mark.parametrize(
"team_id,expected",
[
(None, None),
("", None),
(" ", None),
(" 1210918501948021 ", "1210918501948021"),
],
)
def test_asana_connector_team_id_normalization(
team_id: str | None, expected: str | None
) -> None:
connector = AsanaConnector(
asana_workspace_id="1153293530468850",
asana_project_ids=None,
asana_team_id=team_id,
)
assert connector.asana_team_id == expected

3
desktop/.gitignore vendored
View File

@@ -22,3 +22,6 @@ npm-debug.log*
# Local env files
.env
.env.local
# Generated files
src-tauri/gen/schemas/acl-manifests.json

View File

@@ -706,16 +706,6 @@ dependencies = [
"typeid",
]
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "fdeflate"
version = "0.3.7"
@@ -993,16 +983,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "gethostname"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
dependencies = [
"rustix",
"windows-link 0.2.1",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@@ -1122,24 +1102,6 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "global-hotkey"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7"
dependencies = [
"crossbeam-channel",
"keyboard-types",
"objc2 0.6.3",
"objc2-app-kit 0.3.2",
"once_cell",
"serde",
"thiserror 2.0.17",
"windows-sys 0.59.0",
"x11rb",
"xkeysym",
]
[[package]]
name = "gobject-sys"
version = "0.18.0"
@@ -1713,12 +1675,6 @@ dependencies = [
"libc",
]
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "litemap"
version = "0.8.1"
@@ -2248,7 +2204,6 @@ dependencies = [
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-global-shortcut",
"tauri-plugin-shell",
"tauri-plugin-window-state",
"tokio",
@@ -2878,19 +2833,6 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags 2.10.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
]
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -3605,21 +3547,6 @@ dependencies = [
"walkdir",
]
[[package]]
name = "tauri-plugin-global-shortcut"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "424af23c7e88d05e4a1a6fc2c7be077912f8c76bd7900fd50aa2b7cbf5a2c405"
dependencies = [
"global-hotkey",
"log",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"thiserror 2.0.17",
]
[[package]]
name = "tauri-plugin-shell"
version = "2.3.3"
@@ -5021,29 +4948,6 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "x11rb"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
dependencies = [
"gethostname",
"rustix",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
[[package]]
name = "xkeysym"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "yoke"
version = "0.8.1"

View File

@@ -11,7 +11,6 @@ tauri-build = { version = "2.0", features = [] }
[dependencies]
tauri = { version = "2.0", features = ["macos-private-api", "tray-icon", "image-png"] }
tauri-plugin-shell = "2.0"
tauri-plugin-global-shortcut = "2.0"
tauri-plugin-window-state = "2.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

File diff suppressed because one or more lines are too long

View File

@@ -2354,72 +2354,6 @@
"const": "core:window:deny-unminimize",
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
},
{
"description": "No features are enabled by default, as we believe\nthe shortcuts can be inherently dangerous and it is\napplication specific if specific shortcuts should be\nregistered or unregistered.\n",
"type": "string",
"const": "global-shortcut:default",
"markdownDescription": "No features are enabled by default, as we believe\nthe shortcuts can be inherently dangerous and it is\napplication specific if specific shortcuts should be\nregistered or unregistered.\n"
},
{
"description": "Enables the is_registered command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-is-registered",
"markdownDescription": "Enables the is_registered command without any pre-configured scope."
},
{
"description": "Enables the register command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-register",
"markdownDescription": "Enables the register command without any pre-configured scope."
},
{
"description": "Enables the register_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-register-all",
"markdownDescription": "Enables the register_all command without any pre-configured scope."
},
{
"description": "Enables the unregister command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-unregister",
"markdownDescription": "Enables the unregister command without any pre-configured scope."
},
{
"description": "Enables the unregister_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-unregister-all",
"markdownDescription": "Enables the unregister_all command without any pre-configured scope."
},
{
"description": "Denies the is_registered command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-is-registered",
"markdownDescription": "Denies the is_registered command without any pre-configured scope."
},
{
"description": "Denies the register command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-register",
"markdownDescription": "Denies the register command without any pre-configured scope."
},
{
"description": "Denies the register_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-register-all",
"markdownDescription": "Denies the register_all command without any pre-configured scope."
},
{
"description": "Denies the unregister command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-unregister",
"markdownDescription": "Denies the unregister command without any pre-configured scope."
},
{
"description": "Denies the unregister_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-unregister-all",
"markdownDescription": "Denies the unregister_all command without any pre-configured scope."
},
{
"description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`",
"type": "string",

View File

@@ -2354,72 +2354,6 @@
"const": "core:window:deny-unminimize",
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
},
{
"description": "No features are enabled by default, as we believe\nthe shortcuts can be inherently dangerous and it is\napplication specific if specific shortcuts should be\nregistered or unregistered.\n",
"type": "string",
"const": "global-shortcut:default",
"markdownDescription": "No features are enabled by default, as we believe\nthe shortcuts can be inherently dangerous and it is\napplication specific if specific shortcuts should be\nregistered or unregistered.\n"
},
{
"description": "Enables the is_registered command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-is-registered",
"markdownDescription": "Enables the is_registered command without any pre-configured scope."
},
{
"description": "Enables the register command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-register",
"markdownDescription": "Enables the register command without any pre-configured scope."
},
{
"description": "Enables the register_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-register-all",
"markdownDescription": "Enables the register_all command without any pre-configured scope."
},
{
"description": "Enables the unregister command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-unregister",
"markdownDescription": "Enables the unregister command without any pre-configured scope."
},
{
"description": "Enables the unregister_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-unregister-all",
"markdownDescription": "Enables the unregister_all command without any pre-configured scope."
},
{
"description": "Denies the is_registered command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-is-registered",
"markdownDescription": "Denies the is_registered command without any pre-configured scope."
},
{
"description": "Denies the register command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-register",
"markdownDescription": "Denies the register command without any pre-configured scope."
},
{
"description": "Denies the register_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-register-all",
"markdownDescription": "Denies the register_all command without any pre-configured scope."
},
{
"description": "Denies the unregister command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-unregister",
"markdownDescription": "Denies the unregister command without any pre-configured scope."
},
{
"description": "Denies the unregister_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-unregister-all",
"markdownDescription": "Denies the unregister_all command without any pre-configured scope."
},
{
"description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`",
"type": "string",

View File

@@ -20,7 +20,6 @@ use tauri::Wry;
use tauri::{
webview::PageLoadPayload, AppHandle, Manager, Webview, WebviewUrl, WebviewWindowBuilder,
};
use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, Modifiers, Shortcut};
use url::Url;
#[cfg(target_os = "macos")]
use tokio::time::sleep;
@@ -448,73 +447,6 @@ async fn start_drag_window(window: tauri::Window) -> Result<(), String> {
window.start_dragging().map_err(|e| e.to_string())
}
// ============================================================================
// Shortcuts Setup
// ============================================================================
fn setup_shortcuts(app: &AppHandle) -> Result<(), Box<dyn std::error::Error>> {
let new_chat = Shortcut::new(Some(Modifiers::SUPER), Code::KeyN);
let reload = Shortcut::new(Some(Modifiers::SUPER), Code::KeyR);
let back = Shortcut::new(Some(Modifiers::SUPER), Code::BracketLeft);
let forward = Shortcut::new(Some(Modifiers::SUPER), Code::BracketRight);
let new_window_shortcut = Shortcut::new(Some(Modifiers::SUPER | Modifiers::SHIFT), Code::KeyN);
let show_app = Shortcut::new(Some(Modifiers::SUPER | Modifiers::SHIFT), Code::Space);
let open_settings_shortcut = Shortcut::new(Some(Modifiers::SUPER), Code::Comma);
let app_handle = app.clone();
// Avoid hijacking the system-wide Cmd+R on macOS.
#[cfg(target_os = "macos")]
let shortcuts = [
new_chat,
back,
forward,
new_window_shortcut,
show_app,
open_settings_shortcut,
];
#[cfg(not(target_os = "macos"))]
let shortcuts = [
new_chat,
reload,
back,
forward,
new_window_shortcut,
show_app,
open_settings_shortcut,
];
app.global_shortcut().on_shortcuts(
shortcuts,
move |_app, shortcut, _event| {
if shortcut == &new_chat {
trigger_new_chat(&app_handle);
}
if let Some(window) = app_handle.get_webview_window("main") {
if shortcut == &reload {
let _ = window.eval("window.location.reload()");
} else if shortcut == &back {
let _ = window.eval("window.history.back()");
} else if shortcut == &forward {
let _ = window.eval("window.history.forward()");
} else if shortcut == &open_settings_shortcut {
open_settings(&app_handle);
}
}
if shortcut == &new_window_shortcut {
trigger_new_window(&app_handle);
} else if shortcut == &show_app {
focus_main_window(&app_handle);
}
},
)?;
Ok(())
}
// ============================================================================
// Menu Setup
// ============================================================================
@@ -574,7 +506,7 @@ fn build_tray_menu(app: &AppHandle) -> tauri::Result<Menu<Wry>> {
TRAY_MENU_OPEN_APP_ID,
"Open Onyx",
true,
Some("CmdOrCtrl+Shift+Space"),
None::<&str>,
)?;
let open_chat = MenuItem::with_id(
app,
@@ -666,7 +598,6 @@ fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.plugin(tauri_plugin_window_state::Builder::default().build())
.manage(ConfigState {
config: RwLock::new(config),
@@ -698,11 +629,6 @@ fn main() {
.setup(move |app| {
let app_handle = app.handle();
// Setup global shortcuts
if let Err(e) = setup_shortcuts(&app_handle) {
eprintln!("Failed to setup shortcuts: {}", e);
}
if let Err(e) = setup_app_menu(&app_handle) {
eprintln!("Failed to setup menu: {}", e);
}

View File

@@ -455,9 +455,7 @@ function Main({ ccPairId }: { ccPairId: number }) {
/>
)}
<BackButton
behaviorOverride={() => router.push("/admin/indexing/status")}
/>
<BackButton />
<div
className="flex
items-center

View File

@@ -25,7 +25,6 @@ import { useDocumentSets } from "@/lib/hooks/useDocumentSets";
import { useAgents } from "@/hooks/useAgents";
import { ChatPopup } from "@/app/chat/components/ChatPopup";
import ExceptionTraceModal from "@/components/modals/ExceptionTraceModal";
import { SEARCH_TOOL_ID } from "@/app/chat/components/tools/constants";
import { useUser } from "@/components/user/UserProvider";
import NoAssistantModal from "@/components/modals/NoAssistantModal";
import TextView from "@/components/chat/TextView";
@@ -382,9 +381,7 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
const retrievalEnabled = useMemo(() => {
if (liveAssistant) {
return liveAssistant.tools.some(
(tool) => tool.in_code_tool_id === SEARCH_TOOL_ID
);
return personaIncludesRetrieval(liveAssistant);
}
return false;
}, [liveAssistant]);

View File

@@ -63,7 +63,7 @@ export type ProjectDetails = {
};
export async function fetchProjects(): Promise<Project[]> {
const response = await fetch("/api/user/projects/");
const response = await fetch("/api/user/projects");
if (!response.ok) {
handleRequestError("Fetch projects", response);
}

View File

@@ -4,7 +4,7 @@ import { errorHandlingFetcher } from "@/lib/fetcher";
export function useProjects() {
const { data, error, mutate } = useSWR<Project[]>(
"/api/user/projects/",
"/api/user/projects",
errorHandlingFetcher,
{
revalidateOnFocus: false,