hermes-agent/hermes_cli/web_contracts.py

1061 lines
31 KiB
Python

"""REST payload contracts for the desktop/dashboard API.
These PEP 257-documented TypedDicts are the source of truth for generated
TypeScript gateway contracts. They intentionally describe plain JSON values only.
"""
from __future__ import annotations
from typing import Literal, NotRequired, TypeAlias, TypedDict
from hermes_cli.contract_types import OpaqueValue
from tui_gateway.contracts import GatewaySessionRuntimeInfo
class ConfigFieldSchema(TypedDict):
category: NotRequired[str]
description: NotRequired[str]
options: NotRequired[list[OpaqueValue]]
type: NotRequired[Literal['boolean'] | Literal['list'] | Literal['number'] | Literal['select'] | Literal['string'] | Literal['text']]
class ConfigSchemaResponse(TypedDict):
category_order: NotRequired[list[str]]
fields: dict[str, ConfigFieldSchema]
class AudioTranscriptionResponse(TypedDict):
ok: bool
provider: NotRequired[str]
transcript: str
class AudioSpeakResponse(TypedDict):
ok: bool
data_url: str
mime_type: str
provider: NotRequired[str]
class ElevenLabsVoice(TypedDict):
label: str
name: str
voice_id: str
class ElevenLabsVoicesResponse(TypedDict):
available: bool
voices: list[ElevenLabsVoice]
class OAuthProviderStatus(TypedDict):
error: NotRequired[str]
expires_at: NotRequired[None | str]
has_refresh_token: NotRequired[bool]
last_refresh: NotRequired[None | str]
logged_in: bool
source: NotRequired[None | str]
source_label: NotRequired[None | str]
token_preview: NotRequired[None | str]
class OAuthProvider(TypedDict):
cli_command: str
disconnect_command: NotRequired[None | str]
"""Shell command that clears an external provider's credentials, run in the
embedded terminal. Null when Hermes doesn't know how to remove it."""
disconnect_hint: NotRequired[None | str]
disconnectable: NotRequired[bool]
docs_url: str
flow: Literal['device_code'] | Literal['external'] | Literal['pkce']
id: str
name: str
status: OAuthProviderStatus
class OAuthProvidersResponse(TypedDict):
providers: list[OAuthProvider]
class OAuthSubmitResponse(TypedDict):
message: NotRequired[str]
ok: bool
status: Literal['approved'] | Literal['error']
class OAuthPollResponse(TypedDict):
error_message: NotRequired[None | str]
expires_at: NotRequired[None | float]
session_id: str
status: Literal['approved'] | Literal['denied'] | Literal['error'] | Literal['expired'] | Literal['pending']
class MemoryProviderOAuthStatus(TypedDict):
auth: Literal['apikey'] | Literal['oauth'] | None
connected: bool
detail: str
state: Literal['connected'] | Literal['error'] | Literal['idle'] | Literal['pending']
class EnvVarInfo(TypedDict):
advanced: bool
category: str
channel_managed: NotRequired[bool]
description: str
is_password: bool
is_set: bool
provider: NotRequired[str]
provider_label: NotRequired[str]
redacted_value: None | str
tools: list[str]
url: None | str
MemoryProviderFieldKind: TypeAlias = Literal['bool'] | Literal['json'] | Literal['number'] | Literal['secret'] | Literal['select'] | Literal['text']
class MemoryProviderFieldOption(TypedDict):
description: str
label: str
value: str
class MemoryProviderField(TypedDict):
description: str
group: str
info: NotRequired[str]
inline: bool
is_set: bool
key: str
kind: MemoryProviderFieldKind
label: str
options: list[MemoryProviderFieldOption]
placeholder: str
value: str
class MemoryProviderConfig(TypedDict):
docs_url: str
fields: list[MemoryProviderField]
label: str
name: str
class CustomEndpoint(TypedDict):
api_key_preview: NotRequired[None | str]
base_url: str
context_length: NotRequired[None | float]
discover_models: bool
has_api_key: bool
id: str
is_current: NotRequired[bool]
model: str
models: list[str]
name: str
source: NotRequired[str]
class CustomEndpointUpdate(TypedDict):
api_key: NotRequired[str]
base_url: str
context_length: NotRequired[float]
discover_models: NotRequired[bool]
id: NotRequired[str]
make_default: NotRequired[bool]
model: str
name: str
class CustomEndpointValidationResponse(TypedDict):
message: str
models: list[str]
ok: bool
reachable: bool
class MessagingEnvVarInfo(TypedDict):
advanced: bool
description: str
is_password: bool
is_set: bool
key: str
prompt: str
redacted_value: None | str
required: bool
url: None | str
class MessagingHomeChannel(TypedDict):
chat_id: str
name: str
platform: str
thread_id: NotRequired[str]
class MessagingPlatformInfo(TypedDict):
configured: bool
description: str
docs_url: str
enabled: bool
env_vars: list[MessagingEnvVarInfo]
error_code: NotRequired[None | str]
error_message: NotRequired[None | str]
gateway_running: bool
home_channel: NotRequired[MessagingHomeChannel | None]
id: str
name: str
state: NotRequired[None | str]
updated_at: NotRequired[None | str]
class MessagingPlatformsResponse(TypedDict):
platforms: list[MessagingPlatformInfo]
class MessagingPlatformUpdate(TypedDict):
clear_env: NotRequired[list[str]]
enabled: NotRequired[bool]
env: NotRequired[dict[str, str]]
class MessagingPlatformTestResponse(TypedDict):
message: str
ok: bool
state: NotRequired[None | str]
class GatewayReadyPayload(TypedDict):
skin: NotRequired[OpaqueValue]
class ModelInfoResponse(TypedDict):
auto_context_length: NotRequired[float]
capabilities: NotRequired[dict[str, OpaqueValue]]
config_context_length: NotRequired[float]
effective_context_length: NotRequired[float]
model: str
provider: str
class ModelPricing(TypedDict):
input: str
"""Formatted $/Mtok input price, e.g. "$3.00", or "free", or "" if unknown."""
output: str
"""Formatted $/Mtok output price."""
cache: str | None
"""Formatted $/Mtok cached-input price, or null when the model has none."""
free: bool
"""True when the model costs nothing (free tier eligible)."""
discount_percent: NotRequired[float]
"""Sale: rounded percent off list when gateway sends pricing.original."""
was_input: NotRequired[str]
"""Sale: formatted pre-discount input $/Mtok ("was")."""
was_output: NotRequired[str]
"""Sale: formatted pre-discount output $/Mtok ("was")."""
class ModelOptionProvider(TypedDict):
is_current: NotRequired[bool]
models: NotRequired[list[str]]
name: str
slug: str
total_models: NotRequired[float]
warning: NotRequired[str]
authenticated: NotRequired[bool]
"""True when the provider has usable credentials. False for canonical
providers surfaced by `include_unconfigured` that the user hasn't set up
yet — render these with a setup affordance instead of hiding them."""
auth_type: NotRequired[str]
"""Auth flow for an unconfigured provider: "api_key" can be activated inline
by pasting `key_env`; anything else (oauth_*, external, aws_sdk, …) needs
the `hermes model` CLI / onboarding OAuth flow."""
key_env: NotRequired[str]
"""Env var to paste an API key into, for unconfigured `api_key` providers."""
is_user_defined: NotRequired[bool]
"""True for providers defined via the user's `providers:` config block."""
pricing: NotRequired[dict[str, ModelPricing]]
"""Per-model pricing keyed by model id (present when the picker requested
pricing and the provider supports live pricing)."""
free_tier: NotRequired[bool]
"""Nous only: whether the current account is on the free tier."""
unavailable_models: NotRequired[list[str]]
"""Nous only: paid models a free-tier user cannot select (shown disabled)."""
capabilities: NotRequired[dict[str, ModelCapabilities]]
"""Per-model option support, keyed by model id (present when the picker
requested capabilities). Lets the UI gate fast/reasoning controls."""
class ModelCapabilities(TypedDict):
fast: bool
reasoning: bool
class ModelOptionsResponse(TypedDict):
model: NotRequired[str]
provider: NotRequired[str]
providers: NotRequired[list[ModelOptionProvider]]
class SessionCreateResponse(TypedDict):
info: NotRequired[GatewaySessionRuntimeInfo]
message_count: NotRequired[float]
messages: NotRequired[list[SessionMessage]]
session_id: str
stored_session_id: NotRequired[str]
class SessionInfo(TypedDict):
archived: NotRequired[bool]
cwd: NotRequired[None | str]
git_branch: NotRequired[None | str]
"""Git branch checked out in {@link cwd} when the session started/resumed.
The sidebar groups main-checkout sessions by this so feature-branch work
doesn't collapse under a single directory-named "main" row. Null for
non-git workspaces and sessions created before branch capture landed."""
git_repo_root: NotRequired[None | str]
"""Git repo root that owns {@link cwd} — the authoritative project key,
resolved server-side at cwd-set (and backfilled for history). The sidebar
groups by this instead of probing git in the GUI. Null for non-git
workspaces and not-yet-backfilled rows."""
ended_at: None | float
id: str
_lineage_root_id: NotRequired[None | str]
"""Original root id of a compression chain, when this entry is a projected
continuation tip. Stable across compressions — used as the durable id for
pins so a pinned conversation survives auto-compression."""
input_tokens: float
is_active: bool
last_active: float
message_count: float
model: None | str
output_tokens: float
parent_session_id: NotRequired[None | str]
"""Parent conversation when this row is a /branch fork."""
preview: None | str
source: None | str
started_at: float
title: None | str
tool_call_count: float
handoff_platform: NotRequired[None | str]
"""Origin platform when this session was handed off from a messaging
platform (e.g. a Telegram thread continued in the desktop app). The live
{@link source} becomes local (tui/desktop) after a handoff, so the origin
is preserved here to surface the platform badge on the row."""
handoff_state: NotRequired[None | str]
"""Handoff lifecycle: 'pending' | 'in_progress' | 'completed' | 'failed'."""
handoff_error: NotRequired[None | str]
profile: NotRequired[str]
"""Owning profile name, set by the cross-profile aggregator
(`/api/profiles/sessions`). Absent on legacy single-profile responses,
which the UI treats as the default profile."""
is_default_profile: NotRequired[bool]
"""True when {@link profile} is the default profile."""
class TimelineModelDisplayMetadata(TypedDict):
"""Model-switch metadata attached to a timeline display event."""
model: str
provider: NotRequired[str]
class TimelineDelegationDisplayMetadata(TypedDict):
"""Delegated-task completion metadata attached to a timeline event."""
delegation_id: str
task_count: float
completed_count: NotRequired[float]
failed_count: NotRequired[float]
duration_seconds: NotRequired[float]
TimelineDisplayMetadata: TypeAlias = (
TimelineModelDisplayMetadata | TimelineDelegationDisplayMetadata
)
class SessionMessage(TypedDict):
codex_reasoning_items: NotRequired[OpaqueValue]
content: OpaqueValue
context: NotRequired[OpaqueValue]
name: NotRequired[str]
reasoning: NotRequired[None | str]
reasoning_content: NotRequired[None | str]
reasoning_details: NotRequired[OpaqueValue]
display_kind: NotRequired[Literal['async_delegation_complete'] | Literal['hidden'] | Literal['model_switch'] | str]
display_metadata: NotRequired[TimelineDisplayMetadata]
role: Literal['assistant'] | Literal['system'] | Literal['tool'] | Literal['user']
text: NotRequired[OpaqueValue]
timestamp: NotRequired[float]
tool_call_id: NotRequired[None | str]
tool_calls: NotRequired[OpaqueValue]
tool_name: NotRequired[str]
class SessionMessagesResponse(TypedDict):
messages: list[SessionMessage]
session_id: str
class UsageStats(TypedDict):
calls: float
context_max: NotRequired[float]
context_percent: NotRequired[float]
context_used: NotRequired[float]
cost_usd: NotRequired[float]
input: float
output: float
total: float
class StarmapNode(TypedDict):
"""One graph node in the star map (learned skill or memory chunk)."""
id: str
label: str
kind: Literal['memory'] | Literal['skill']
memorySource: NotRequired[Literal['memory'] | Literal['profile']]
timestamp: NotRequired[None | float]
category: str
useCount: float
state: str
createdBy: None | str
pinned: bool
class StarmapEdge(TypedDict):
"""A declared `related_skills` link; both endpoints are guaranteed to be nodes."""
source: str
target: str
class StarmapCluster(TypedDict):
category: str
count: float
class StarmapMemoryCard(TypedDict):
"""Freeform memory rendered as a card — never a graph node."""
source: Literal['memory'] | Literal['profile']
timestamp: NotRequired[None | float]
title: str
body: str
class StarmapGraph(TypedDict):
nodes: list[StarmapNode]
edges: list[StarmapEdge]
clusters: list[StarmapCluster]
memory: list[StarmapMemoryCard]
stats: dict[str, OpaqueValue]
class ContextUsageCategory(TypedDict):
color: str
id: str
label: str
tokens: float
class ContextBreakdown(TypedDict):
categories: list[ContextUsageCategory]
context_max: float
context_percent: float
context_used: float
estimated_total: float
model: NotRequired[str]
class AnalyticsDailyEntry(TypedDict):
actual_cost: float
api_calls: float
cache_read_tokens: float
day: str
estimated_cost: float
input_tokens: float
output_tokens: float
reasoning_tokens: float
sessions: float
class AnalyticsModelEntry(TypedDict):
api_calls: float
estimated_cost: float
input_tokens: float
model: str
output_tokens: float
sessions: float
class AnalyticsToolEntry(TypedDict):
count: float
percentage: float
tool: str
class AnalyticsSkillEntry(TypedDict):
last_used_at: None | float
manage_count: float
percentage: float
skill: str
total_count: float
view_count: float
class AnalyticsSkillsSummary(TypedDict):
distinct_skills_used: float
total_skill_actions: float
total_skill_edits: float
total_skill_loads: float
class AnalyticsTotals(TypedDict):
total_actual_cost: float
total_api_calls: None | float
total_cache_read: None | float
total_estimated_cost: float
total_input: None | float
total_output: None | float
total_reasoning: None | float
total_sessions: float
class CronJob(TypedDict):
deliver: NotRequired[None | str]
enabled: bool
id: str
last_error: NotRequired[None | str]
last_run_at: NotRequired[None | str]
model: NotRequired[None | str]
name: NotRequired[None | str]
next_run_at: NotRequired[None | str]
no_agent: NotRequired[bool]
prompt: NotRequired[None | str]
provider: NotRequired[None | str]
schedule: NotRequired[CronJobSchedule]
schedule_display: NotRequired[None | str]
script: NotRequired[None | str]
state: NotRequired[None | str]
class CronJobCreatePayload(TypedDict):
deliver: NotRequired[str]
model: NotRequired[str]
name: NotRequired[str]
prompt: str
provider: NotRequired[str]
schedule: str
class CronJobSchedule(TypedDict):
display: NotRequired[str]
expr: NotRequired[str]
kind: NotRequired[str]
class CronJobUpdates(TypedDict):
deliver: NotRequired[str]
enabled: NotRequired[bool]
model: NotRequired[None | str]
name: NotRequired[str]
prompt: NotRequired[str]
provider: NotRequired[None | str]
schedule: NotRequired[str]
class ProfileCreatePayload(TypedDict):
clone_all: NotRequired[bool]
clone_from: NotRequired[None | str]
clone_from_default: NotRequired[bool]
name: str
no_skills: NotRequired[bool]
class ProfileInfo(TypedDict):
has_env: bool
is_default: bool
model: None | str
name: str
path: str
provider: None | str
skill_count: float
class ProfileSetupCommand(TypedDict):
command: str
class ProjectFolder(TypedDict):
path: str
label: None | str
is_primary: bool
added_at: float
class ProjectInfo(TypedDict):
id: str
slug: str
name: str
description: None | str
icon: None | str
color: None | str
board_slug: None | str
primary_path: None | str
archived: bool
created_at: float
folders: list[ProjectFolder]
class ProjectsPayload(TypedDict):
projects: list[ProjectInfo]
active_id: None | str
class ProfileSoul(TypedDict):
content: str
exists: bool
class ProfilesResponse(TypedDict):
profiles: list[ProfileInfo]
class SkillInfo(TypedDict):
category: str
description: str
enabled: bool
name: str
usage: NotRequired[float]
"""Total observed activity (use + view + patch). Absent on older backends."""
provenance: NotRequired[Literal['agent'] | Literal['bundled'] | Literal['hub']]
"""'agent' = learned/local (editable), 'bundled' = ships with Hermes, 'hub' = installed."""
class ToolsetInfo(TypedDict):
configured: bool
description: str
enabled: bool
label: str
name: str
tools: list[str]
class ToolEnvVar(TypedDict):
key: str
prompt: str
url: str | None
default: str | None
is_set: bool
# Server-computed readiness for a provider picker row. Absent on older
# backends that predate the truthful-readiness endpoint.
ToolProviderStatus: TypeAlias = Literal['ready'] | Literal['needs_setup'] | Literal['needs_auth'] | Literal['needs_keys']
class ToolProvider(TypedDict):
name: str
badge: str
tag: str
env_vars: list[ToolEnvVar]
post_setup: str | None
requires_nous_auth: bool
is_active: bool
"""True when this is the provider currently written to config (mirrors the
CLI `hermes tools` active-provider detection)."""
status: NotRequired[ToolProviderStatus]
"""Honest readiness computed server-side (keys ∧ Nous entitlement ∧
post-setup install state). Optional for older backends."""
web_backend: NotRequired[str]
"""Web toolset only: the backend key written to web.*backend config
(e.g. 'searxng'). Absent on other toolsets and older backends."""
tts_provider: NotRequired[str]
"""TTS toolset only: the provider key written to tts.provider when this row
is selected (e.g. 'openai'). Doubles as the config section that holds the
provider's voice/model settings (tts.<key>.*). Absent on other toolsets
and older backends."""
capabilities: NotRequired[list[WebCapability]]
"""Web toolset only: capabilities this backend can serve. Search-only
providers (ddgs, brave-free) report ['search']."""
# A web toolset capability — the runtime dispatches web_search and
# web_extract to independently configurable backends.
WebCapability: TypeAlias = Literal['search'] | Literal['extract']
class ToolsetConfig(TypedDict):
name: str
has_category: bool
providers: list[ToolProvider]
active_provider: str | None
"""Name of the currently active provider, or null if none is configured."""
active_search_backend: NotRequired[str | None]
"""Web toolset only: backend the web_search tool resolves to right now
(web.search_backend → web.backend → credential auto-detect)."""
active_extract_backend: NotRequired[str | None]
"""Web toolset only: backend the web_extract tool resolves to right now."""
# Health status of a terminal execution backend row.
#
# `ready` — usable now; `needs_setup` — selectable but missing a dependency
# or credential (detail says which); `unavailable` — the probe itself failed.
TerminalBackendStatus: TypeAlias = Literal['ready'] | Literal['needs_setup'] | Literal['unavailable']
class TerminalBackendInfo(TypedDict):
"""One row from `GET /api/tools/terminal/backends`."""
name: str
label: str
description: str
active: bool
"""True when this backend is the current `terminal.backend` config value."""
status: TerminalBackendStatus
detail: str
"""Setup guidance / probe detail for non-ready rows (empty when ready)."""
class TerminalBackendsResponse(TypedDict):
"""Shape of `GET /api/tools/terminal/backends`."""
active: str
backends: list[TerminalBackendInfo]
class ToolsetModel(TypedDict):
"""One model row from a toolset backend's catalog (image/video gen)."""
id: str
display: str
speed: str
strengths: str
price: str
class ToolsetModelsResponse(TypedDict):
"""Shape of `GET /api/tools/toolsets/{name}/models`."""
name: str
has_models: bool
provider: NotRequired[str | None]
plugin: NotRequired[str | None]
models: list[ToolsetModel]
current: str | None
default: str | None
class ComputerUsePermissionSource(TypedDict):
"""Shape of `GET /api/tools/computer-use/status`.
cua-driver runs on macOS, Windows, and Linux. `ready` is the single OS-aware
readiness signal: on macOS both TCC grants (Accessibility + Screen
Recording, which attach to cua-driver's own `com.trycua.driver` identity,
not Hermes); elsewhere, driver health from `cua-driver doctor`. `null`
means unknown (binary missing / probe failed)."""
attribution: NotRequired[str]
executable: NotRequired[str]
note: NotRequired[str]
pid: NotRequired[float]
responsible_ppid: NotRequired[float]
class ComputerUseCheck(TypedDict):
label: str
status: str
message: str
class ComputerUseStatus(TypedDict):
platform: str
"""`sys.platform`: "darwin" | "win32" | "linux" | ..."""
platform_supported: bool
"""cua-driver has a runtime backend for this platform."""
installed: bool
"""cua-driver binary resolved on PATH."""
version: str | None
"""e.g. "cua-driver 0.5.1", or null when unknown."""
ready: bool | None
"""Unified readiness — both TCC grants (macOS) or driver health (else)."""
can_grant: bool
"""Whether a permission grant flow exists (macOS-only TCC)."""
checks: list[ComputerUseCheck]
"""Cross-platform `cua-driver doctor` probes."""
accessibility: bool | None
"""macOS TCC detail — `null` off macOS or when unknown."""
screen_recording: bool | None
screen_recording_capturable: bool | None
source: ComputerUsePermissionSource | None
error: str | None
"""Populated when the status probe itself failed."""
class SessionSearchResult(TypedDict):
lineage_root: NotRequired[str | None]
"""Lineage root of the matched conversation. Stable across compression and
used as the durable pin id; falls back to session_id when absent."""
model: str | None
role: str | None
session_id: str
"""Live compression tip of the matched conversation — resume by this id."""
session_started: float | None
snippet: str
source: str | None
class SessionSearchResponse(TypedDict):
results: list[SessionSearchResult]
class LogsResponse(TypedDict):
file: str
lines: list[str]
class PlatformStatus(TypedDict):
error_code: NotRequired[str]
error_message: NotRequired[str]
state: str
updated_at: str
class StatusResponse(TypedDict):
active_sessions: float
config_path: str
config_version: float
env_path: str
gateway_exit_reason: str | None
gateway_health_url: str | None
gateway_pid: float | None
gateway_platforms: dict[str, PlatformStatus]
gateway_running: bool
gateway_state: str | None
gateway_updated_at: str | None
hermes_home: str
latest_config_version: float
release_date: str
version: str
class ActionResponse(TypedDict):
name: str
ok: bool
pid: float
class ActionStatusResponse(TypedDict):
exit_code: float | None
lines: list[str]
name: str
pid: float | None
running: bool
class BackendUpdateCommit(TypedDict):
sha: str
summary: str
author: str
at: float
class BackendUpdateCheckResponse(TypedDict):
"""Shape of `GET /api/hermes/update/check` — the backend's own update state.
Used by the desktop's remote update overlay so the backend version (not the
Electron client clone) drives "what's changed + Install" in remote mode."""
install_method: str
current_version: str
behind: float | None
update_available: bool
can_apply: bool
update_command: str | None
message: str | None
commits: NotRequired[list[BackendUpdateCommit]]
class AuxiliaryTaskAssignment(TypedDict):
base_url: str
model: str
provider: str
task: str
class MoaModelSlot(TypedDict):
provider: str
model: str
reasoning_effort: NotRequired[str]
"""Optional per-slot reasoning effort — round-tripped, not edited here."""
class ModelAssignmentRequest(TypedDict):
api_key: NotRequired[str]
"""Optional API key for a custom/local endpoint. Persisted to model.api_key
(where the runtime reads it) for self-hosted endpoints that require auth.
Only honored for custom/local providers on the main slot."""
base_url: NotRequired[str]
"""OpenAI-compatible endpoint URL. Only honored for custom/local providers
on the main slot — wires a self-hosted endpoint into runtime resolution."""
model: str
provider: str
scope: Literal['main'] | Literal['auxiliary']
task: NotRequired[str]
class StaleAuxAssignment(TypedDict):
"""An auxiliary task still pinned to a provider that differs from the
newly-selected main provider after a main-model switch."""
task: str
provider: str
model: str
class SkillHubSource(TypedDict):
"""One skill-hub source (official index, GitHub, skills.sh, …) as reported by
`GET /api/skills/hub/sources`."""
id: str
label: str
available: NotRequired[bool]
rate_limited: NotRequired[bool]
searchable: NotRequired[bool]
class SkillHubResult(TypedDict):
"""A searchable/installable hub skill from `GET /api/skills/hub/search`."""
name: str
description: str
source: str
identifier: str
trust_level: str
repo: str | None
tags: list[str]
class SkillHubInstalledEntry(TypedDict):
name: str | None
trust_level: str | None
scan_verdict: str | None
class SkillHubSourcesResponse(TypedDict):
sources: list[SkillHubSource]
index_available: bool
featured: list[SkillHubResult]
installed: dict[str, SkillHubInstalledEntry]
class SkillHubSearchResponse(TypedDict):
results: list[SkillHubResult]
source_counts: dict[str, float]
timed_out: list[str]
installed: dict[str, SkillHubInstalledEntry]
class SkillHubPreview(TypedDict):
"""`GET /api/skills/hub/preview` — SKILL.md + manifest without installing."""
name: str
description: str
source: str
identifier: str
trust_level: str
repo: str | None
tags: list[str]
skill_md: str
files: list[str]
class SkillHubScanFinding(TypedDict):
severity: str
category: str
file: str
line: float | None
description: str
class SkillHubScanResult(TypedDict):
"""`GET /api/skills/hub/scan` — install-time security scan verdict."""
name: str
identifier: str
source: str
trust_level: str
verdict: str
summary: str
policy: Literal['allow'] | Literal['ask'] | Literal['block']
policy_reason: str | None
findings: list[SkillHubScanFinding]
severity_counts: dict[str, float]
class McpServerSummary(TypedDict):
"""One configured MCP server row from `GET /api/mcp/servers`."""
name: str
transport: str
command: str | None
args: list[str]
url: str | None
enabled: bool
tools: list[str] | None
class CuratorStatusResponse(TypedDict):
"""`GET /api/curator` — background skill-curator status."""
enabled: bool
paused: bool
interval_hours: float | None
last_run_at: str | None
min_idle_hours: float | None
stale_after_days: float | None
archive_after_days: float | None
class DebugShareResponse(TypedDict):
"""`POST /api/ops/debug-share` — shareable diagnostics upload result."""
ok: bool
urls: dict[str, str]
failures: dict[str, str]
redacted: bool
auto_delete_seconds: float | None
class ModelAssignmentResponse(TypedDict):
base_url: NotRequired[str]
"""Persisted endpoint URL for custom/local providers (echoed back)."""
gateway_tools: NotRequired[list[str]]
"""Toolset keys auto-routed through the Nous Tool Gateway as a result of
switching the main provider to Nous. Empty unless provider === 'nous'
and the user is a paid subscriber with unconfigured tools."""
model: NotRequired[str]
ok: bool
provider: NotRequired[str]
reset: NotRequired[bool]
scope: NotRequired[str]
stale_aux: NotRequired[list[StaleAuxAssignment]]
"""Auxiliary slots still pinned to a different provider than the new main.
Switching main never clears aux pins; this lets the UI warn the user
their helper tasks aren't following the switch. Only set on scope:'main'."""
tasks: NotRequired[list[str]]
EXPORTED_CONTRACT_NAMES = (
ConfigFieldSchema,
ConfigSchemaResponse,
AudioTranscriptionResponse,
AudioSpeakResponse,
ElevenLabsVoice,
ElevenLabsVoicesResponse,
OAuthProviderStatus,
OAuthProvider,
OAuthProvidersResponse,
OAuthSubmitResponse,
OAuthPollResponse,
MemoryProviderOAuthStatus,
EnvVarInfo,
MemoryProviderFieldKind,
MemoryProviderFieldOption,
MemoryProviderField,
MemoryProviderConfig,
CustomEndpoint,
CustomEndpointUpdate,
CustomEndpointValidationResponse,
MessagingEnvVarInfo,
MessagingHomeChannel,
MessagingPlatformInfo,
MessagingPlatformsResponse,
MessagingPlatformUpdate,
MessagingPlatformTestResponse,
GatewayReadyPayload,
ModelInfoResponse,
ModelPricing,
ModelOptionProvider,
ModelCapabilities,
ModelOptionsResponse,
SessionCreateResponse,
SessionInfo,
SessionMessage,
SessionMessagesResponse,
UsageStats,
StarmapNode,
StarmapEdge,
StarmapCluster,
StarmapMemoryCard,
StarmapGraph,
ContextUsageCategory,
ContextBreakdown,
AnalyticsDailyEntry,
AnalyticsModelEntry,
AnalyticsToolEntry,
AnalyticsSkillEntry,
AnalyticsSkillsSummary,
AnalyticsTotals,
CronJob,
CronJobCreatePayload,
CronJobSchedule,
CronJobUpdates,
ProfileCreatePayload,
ProfileInfo,
ProfileSetupCommand,
ProjectFolder,
ProjectInfo,
ProjectsPayload,
ProfileSoul,
ProfilesResponse,
SkillInfo,
ToolsetInfo,
ToolEnvVar,
ToolProviderStatus,
ToolProvider,
WebCapability,
ToolsetConfig,
TerminalBackendStatus,
TerminalBackendInfo,
TerminalBackendsResponse,
ToolsetModel,
ToolsetModelsResponse,
ComputerUsePermissionSource,
ComputerUseCheck,
ComputerUseStatus,
SessionSearchResult,
SessionSearchResponse,
LogsResponse,
PlatformStatus,
StatusResponse,
ActionResponse,
ActionStatusResponse,
BackendUpdateCommit,
BackendUpdateCheckResponse,
AuxiliaryTaskAssignment,
MoaModelSlot,
ModelAssignmentRequest,
StaleAuxAssignment,
SkillHubSource,
SkillHubResult,
SkillHubInstalledEntry,
SkillHubSourcesResponse,
SkillHubSearchResponse,
SkillHubPreview,
SkillHubScanFinding,
SkillHubScanResult,
McpServerSummary,
CuratorStatusResponse,
DebugShareResponse,
ModelAssignmentResponse,
)
EXPORTED_CONTRACTS = {
contract.__name__: contract
for contract in EXPORTED_CONTRACT_NAMES
if isinstance(contract, type)
} | {
"MemoryProviderFieldKind": MemoryProviderFieldKind,
"ToolProviderStatus": ToolProviderStatus,
"WebCapability": WebCapability,
"TerminalBackendStatus": TerminalBackendStatus,
}